From 84c9fdbecf0b44a0897badcc12e5a9b333da4ec0 Mon Sep 17 00:00:00 2001 From: Cat McGee Date: Wed, 24 Jan 2024 14:58:35 +0600 Subject: [PATCH 1/7] chore(docs): Fix token bridge tutorial (#3935) Had a few missing links, wrong filepaths, and missing snippets --- .../token_portal/depositing_to_aztec.md | 15 +++++++++- .../dev_docs/tutorials/token_portal/setup.md | 30 +++++++------------ .../token_portal/typescript_glue_code.md | 17 ++++------- .../token_portal/withdrawing_to_l1.md | 20 ++++++++----- l1-contracts/test/portals/TokenPortal.sol | 2 +- 5 files changed, 43 insertions(+), 41 deletions(-) diff --git a/docs/docs/dev_docs/tutorials/token_portal/depositing_to_aztec.md b/docs/docs/dev_docs/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/dev_docs/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/setup.md b/docs/docs/dev_docs/tutorials/token_portal/setup.md index 2c1e4472f48..3f738e02fc0 100644 --- a/docs/docs/dev_docs/tutorials/token_portal/setup.md +++ b/docs/docs/dev_docs/tutorials/token_portal/setup.md @@ -62,7 +62,8 @@ 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" } +protocol_types = { git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="yarn-project/noir-protocol-circuits/src/crates/types"} ``` 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 +75,7 @@ aztec-contracts ├── src ├── main.nr ├── token_interface.nr + ├── util.nr ``` # Create a JS hardhat project @@ -99,7 +101,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 +131,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 +146,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 { @@ -154,18 +156,11 @@ Your `package.json` should look something like this: "license": "MIT", "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" + "dependencies": { + "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 +222,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 +238,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/dev_docs/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/dev_docs/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/dev_docs/tutorials/token_portal/withdrawing_to_l1.md index fd2a2ea1721..8716421c10b 100644 --- a/docs/docs/dev_docs/tutorials/token_portal/withdrawing_to_l1.md +++ b/docs/docs/dev_docs/tutorials/token_portal/withdrawing_to_l1.md @@ -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/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; From 8c3efba92f74905709760f3d8838df50076aaa92 Mon Sep 17 00:00:00 2001 From: Cat McGee Date: Wed, 24 Jan 2024 15:46:10 +0600 Subject: [PATCH 2/7] feat(docs): Historical trees docs (#3895) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Please provide a paragraph or two giving a summary of the change, including relevant motivation and context. # Checklist: Remove the checklist to signal you've completed it. Enable auto-merge if the PR is ready to merge. - [x] If the pull request requires a cryptography review (e.g. cryptographic algorithm implementations) I have added the 'crypto' tag. - [x] I have reviewed my diff in github, line by line and removed unexpected formatting changes, testing logs, or commented-out code. - [x] Every change is related to the PR description. - [x] I have [linked](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue) this pull request to relevant issues (if any exist). --------- Co-authored-by: josh crites Co-authored-by: Jan Beneš Co-authored-by: Rahul Kothari --- .../advanced/data_structures/trees.md | 34 ++++- .../history_lib_reference.md | 119 ++++++++++++++++++ .../historical_access/how_to_prove_history.md | 102 +++++++++++++++ docs/sidebars.js | 9 ++ .../inclusion_proofs_contract/src/main.nr | 24 +++- 5 files changed, 282 insertions(+), 6 deletions(-) create mode 100644 docs/docs/dev_docs/contracts/syntax/historical_access/history_lib_reference.md create mode 100644 docs/docs/dev_docs/contracts/syntax/historical_access/how_to_prove_history.md diff --git a/docs/docs/concepts/advanced/data_structures/trees.md b/docs/docs/concepts/advanced/data_structures/trees.md index 658782ad010..d29f9c04669 100644 --- a/docs/docs/concepts/advanced/data_structures/trees.md +++ b/docs/docs/concepts/advanced/data_structures/trees.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 @@ -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](../../../dev_docs/contracts/syntax/historical_access/how_to_prove_history.md). ## Trees of valid Kernel/Rollup circuit Verification Keys diff --git a/docs/docs/dev_docs/contracts/syntax/historical_access/history_lib_reference.md b/docs/docs/dev_docs/contracts/syntax/historical_access/history_lib_reference.md new file mode 100644 index 00000000000..b798a129243 --- /dev/null +++ b/docs/docs/dev_docs/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](../../../../concepts/advanced/data_structures/trees.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/dev_docs/contracts/syntax/historical_access/how_to_prove_history.md b/docs/docs/dev_docs/contracts/syntax/historical_access/how_to_prove_history.md new file mode 100644 index 00000000000..28e3ea04ccf --- /dev/null +++ b/docs/docs/dev_docs/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](../../../../concepts/advanced/data_structures/trees.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/sidebars.js b/docs/sidebars.js index ae468539d33..30cd202bb3c 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -326,7 +326,16 @@ const sidebars = { }, "dev_docs/contracts/syntax/events", "dev_docs/contracts/syntax/functions", + { + label: "Proving Historical Blockchain Data", + type: "category", + items: [ + "dev_docs/contracts/syntax/historical_access/how_to_prove_history", + "dev_docs/contracts/syntax/historical_access/history_lib_reference", + ], + }, "dev_docs/contracts/syntax/slow_updates_tree", + "dev_docs/contracts/syntax/context", "dev_docs/contracts/syntax/globals", ], 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..5b99fdd60bb 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 @@ -23,11 +23,11 @@ contract InclusionProofs { 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,9 +46,11 @@ contract InclusionProofs { prove_public_value_inclusion, }, }, + // docs:end:imports }; + // docs:start:value_note_imports use dep::value_note::value_note::{ValueNote, ValueNoteMethods, VALUE_NOTE_LEN}; - + // docs:end:value_note_imports struct Storage { private_values: Map>, public_value: PublicState, @@ -84,6 +86,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,6 +94,7 @@ 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)] @@ -102,20 +106,24 @@ contract InclusionProofs { // 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 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() { + // docs:start:prove_note_inclusion prove_note_inclusion( ValueNoteMethods, 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); @@ -140,15 +148,19 @@ contract InclusionProofs { // 3) Compute the nullifier from the note if maybe_note.is_some() { + // docs:start:prove_note_not_nullified prove_note_not_nullified( ValueNoteMethods, 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 }; } @@ -164,9 +176,12 @@ contract InclusionProofs { let note = notes[0].unwrap(); // 2) Prove the note validity + // docs:start:prove_note_validity prove_note_validity(ValueNoteMethods, 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); @@ -176,6 +191,7 @@ contract InclusionProofs { 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 +201,9 @@ 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)] From 93420480603b2dfa126e5bddb08cd768b7093352 Mon Sep 17 00:00:00 2001 From: maramihali Date: Wed, 24 Jan 2024 10:02:04 +0000 Subject: [PATCH 3/7] feat: recursive folding and decider verifier for Protogalaxy (#4156) Implements the recursive folding verifier and recursive decider verifier resembling the native ones as closely as possible. Tests follow the example of the other recursive honk verifiers and native protogalaxy tests. Additionally, I introduced a function that sequentially computes polynomials in coefficient form in the `ProtoGalaxyRecursiveVerifier` to avoid having to instantiate the entire `Polynomial` class on `stdlib::bn254`. Closes https://github.com/AztecProtocol/barretenberg/issues/789. Closes https://github.com/AztecProtocol/barretenberg/issues/834. --- .../protogalaxy_bench/protogalaxy.bench.cpp | 2 +- .../ecc/fields/field_declarations.hpp | 7 + .../src/barretenberg/flavor/goblin_ultra.hpp | 67 +--- .../flavor/goblin_ultra_recursive.hpp | 20 +- .../cpp/src/barretenberg/flavor/ultra.hpp | 48 +-- .../barretenberg/flavor/ultra_recursive.hpp | 96 +++-- .../protogalaxy/protogalaxy_prover.cpp | 4 - .../protogalaxy/protogalaxy_verifier.cpp | 15 + .../stdlib/primitives/curves/bn254.hpp | 2 + .../verifier/decider_recursive_verifier.cpp | 96 +++++ .../verifier/decider_recursive_verifier.hpp | 30 ++ .../protogalaxy_recursive_verifier.cpp | 320 ++++++++++++++++ .../protogalaxy_recursive_verifier.hpp | 118 ++++++ .../protogalaxy_recursive_verifier.test.cpp | 350 ++++++++++++++++++ .../verifier/ultra_recursive_verifier.hpp | 5 - .../barretenberg/stdlib/utility/utility.hpp | 4 + .../ultra_honk/protogalaxy.test.cpp | 4 +- .../ultra_honk/ultra_composer.hpp | 3 +- 18 files changed, 1060 insertions(+), 131 deletions(-) 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 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..b0a4f05afbf 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(); 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/flavor/goblin_ultra.hpp b/barretenberg/cpp/src/barretenberg/flavor/goblin_ultra.hpp index 8629356b726..5f13c7ac66b 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/goblin_ultra.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/goblin_ultra.hpp @@ -155,7 +155,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 +418,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 +452,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). 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..2fd21fab351 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; + } } }; 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/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..bf7d4c50117 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(); 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/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..6578f48cc99 --- /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::plonk::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.proof_data); + 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..18e6b75fa0b --- /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/plonk/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::plonk::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/protogalaxy_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/protogalaxy_recursive_verifier.cpp new file mode 100644 index 00000000000..68c366de5b9 --- /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(std::vector 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..64b75719542 --- /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/plonk/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(std::vector 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.hpp b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.hpp index f26b44d1116..5712966f001 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 @@ -20,11 +20,6 @@ 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. 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/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/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); From bef65c38c58427325b4481ab794f0fb4f12196b0 Mon Sep 17 00:00:00 2001 From: Lasse Herskind <16536249+LHerskind@users.noreply.github.com> Date: Wed, 24 Jan 2024 10:39:06 +0000 Subject: [PATCH 4/7] feat: add inclusion check l1->l2 (#4141) Fixes https://github.com/AztecProtocol/aztec-packages/issues/1383 - Adding more tests - Adding assertion strings - Remove `root` from oracle response as it is not needed --- .../src/core/libraries/ConstantsGen.sol | 2 +- .../acir-simulator/src/acvm/oracle/oracle.ts | 4 +- .../src/acvm/oracle/typed_oracle.ts | 14 +- .../acir-simulator/src/acvm/serialize.ts | 7 +- .../src/client/private_execution.test.ts | 274 ++++++++++++++--- .../src/client/view_data_oracle.ts | 3 +- .../acir-simulator/src/public/index.test.ts | 287 ++++++++++++++---- .../src/public/public_execution_context.ts | 4 +- 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 +- .../circuit-types/src/l1_to_l2_message.ts | 5 + yarn-project/circuits.js/src/constants.gen.ts | 2 +- .../src/e2e_cross_chain_messaging.test.ts | 6 +- .../e2e_public_cross_chain_messaging.test.ts | 4 +- .../src/crates/types/src/constants.nr | 2 +- 16 files changed, 502 insertions(+), 151 deletions(-) diff --git a/l1-contracts/src/core/libraries/ConstantsGen.sol b/l1-contracts/src/core/libraries/ConstantsGen.sol index 4b0aecbc3ce..e8eb5f3c5c0 100644 --- a/l1-contracts/src/core/libraries/ConstantsGen.sol +++ b/l1-contracts/src/core/libraries/ConstantsGen.sol @@ -62,7 +62,7 @@ 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; diff --git a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts index 10a78d70875..020e1738776 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts @@ -232,8 +232,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..27d9f8d5f30 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts @@ -48,7 +48,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 +66,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) @@ -167,7 +157,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..5e844ff8a7c 100644 --- a/yarn-project/acir-simulator/src/acvm/serialize.ts +++ b/yarn-project/acir-simulator/src/acvm/serialize.ts @@ -199,18 +199,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/client/private_execution.test.ts b/yarn-project/acir-simulator/src/client/private_execution.test.ts index 01b8c34ac75..775433dbeb8 100644 --- a/yarn-project/acir-simulator/src/client/private_execution.test.ts +++ b/yarn-project/acir-simulator/src/client/private_execution.test.ts @@ -1,4 +1,4 @@ -import { Note, PackedArguments, TxExecutionRequest } from '@aztec/circuit-types'; +import { L1ToL2Message, Note, PackedArguments, TxExecutionRequest } from '@aztec/circuit-types'; import { BlockHeader, CallContext, @@ -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, @@ -143,7 +143,7 @@ describe('Private Execution test suite', () => { await trees[name].appendLeaves(leaves.map(l => l.toBuffer())); // Update root. - const newRoot = trees[name].getRoot(false); + const newRoot = trees[name].getRoot(true); const prevRoots = blockHeader.toBuffer(); const rootIndex = name === 'noteHash' ? 0 : 32 * 3; const newRoots = Buffer.concat([prevRoots.subarray(0, rootIndex), newRoot, prevRoots.subarray(rootIndex + 32)]); @@ -177,6 +177,7 @@ describe('Private Execution test suite', () => { }); beforeEach(() => { + trees = {}; oracle = mock(); oracle.getNullifierKeyPair.mockImplementation((accountAddress: AztecAddress, contractAddress: AztecAddress) => { if (accountAddress.equals(ownerCompleteAddress.address)) { @@ -476,53 +477,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.getBlockHeader.mockResolvedValue(blockHeader); + + 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.getBlockHeader.mockResolvedValue(blockHeader); + + 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.getBlockHeader.mockResolvedValue(blockHeader); + + 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.getBlockHeader.mockResolvedValue(blockHeader); + + 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.getBlockHeader.mockResolvedValue(blockHeader); + + 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.getBlockHeader.mockResolvedValue(blockHeader); + + 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.getBlockHeader.mockResolvedValue(blockHeader); + + 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.getBlockHeader.mockResolvedValue(blockHeader); + + 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/view_data_oracle.ts b/yarn-project/acir-simulator/src/client/view_data_oracle.ts index 3b6128e3221..3fb79b16b6f 100644 --- a/yarn-project/acir-simulator/src/client/view_data_oracle.ts +++ b/yarn-project/acir-simulator/src/client/view_data_oracle.ts @@ -240,8 +240,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/index.test.ts b/yarn-project/acir-simulator/src/public/index.test.ts index 05d9654ae47..739a4bfcd3e 100644 --- a/yarn-project/acir-simulator/src/public/index.test.ts +++ b/yarn-project/acir-simulator/src/public/index.test.ts @@ -1,3 +1,4 @@ +import { L1ToL2Message } from '@aztec/circuit-types'; import { BlockHeader, CallContext, FunctionData, GlobalVariables, 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'; @@ -353,69 +354,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 +383,228 @@ 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 root; + }; + + 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 + blockHeader.l1ToL2MessageTreeRoot = mockOracles(); + globalVariables = computeGlobalVariables(); + + const execution: PublicExecution = { contractAddress, functionData, args, callContext }; + executor = new PublicExecutor(publicState, publicContracts, commitmentsDb, blockHeader); + 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 + blockHeader.l1ToL2MessageTreeRoot = mockOracles(); + globalVariables = computeGlobalVariables(); + + const execution: PublicExecution = { contractAddress, functionData, args, callContext }; + executor = new PublicExecutor(publicState, publicContracts, commitmentsDb, blockHeader); + 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, blockHeader); + 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 + blockHeader.l1ToL2MessageTreeRoot = mockOracles(); + globalVariables = computeGlobalVariables(); + + const execution: PublicExecution = { contractAddress, functionData, args, callContext }; + executor = new PublicExecutor(publicState, publicContracts, commitmentsDb, blockHeader); + 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 + blockHeader.l1ToL2MessageTreeRoot = mockOracles(); + globalVariables = computeGlobalVariables(); + + const execution: PublicExecution = { contractAddress, functionData, args, callContext }; + executor = new PublicExecutor(publicState, publicContracts, commitmentsDb, blockHeader); + await expect(executor.simulate(execution, globalVariables)).rejects.toThrowError('Invalid sender'); + }); + + it('Invalid chainid', async () => { + preimage = computePreImage(); + args = computeArgs(); + callContext = computeCallContext(); + + // Prepare the state + blockHeader.l1ToL2MessageTreeRoot = mockOracles(); + globalVariables = computeGlobalVariables(); + globalVariables.chainId = Fr.random(); + + const execution: PublicExecution = { contractAddress, functionData, args, callContext }; + executor = new PublicExecutor(publicState, publicContracts, commitmentsDb, blockHeader); + await expect(executor.simulate(execution, globalVariables)).rejects.toThrowError('Invalid Chainid'); + }); + + it('Invalid version', async () => { + preimage = computePreImage(); + args = computeArgs(); + callContext = computeCallContext(); + + // Prepare the state + blockHeader.l1ToL2MessageTreeRoot = mockOracles(); + globalVariables = computeGlobalVariables(); + globalVariables.version = Fr.random(); + + const execution: PublicExecution = { contractAddress, functionData, args, callContext }; + executor = new PublicExecutor(publicState, publicContracts, commitmentsDb, blockHeader); + 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 + blockHeader.l1ToL2MessageTreeRoot = mockOracles(); + globalVariables = computeGlobalVariables(); + + const execution: PublicExecution = { contractAddress, functionData, args, callContext }; + executor = new PublicExecutor(publicState, publicContracts, commitmentsDb, blockHeader); + 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 + blockHeader.l1ToL2MessageTreeRoot = mockOracles(); + globalVariables = computeGlobalVariables(); + + const execution: PublicExecution = { contractAddress, functionData, args, callContext }; + executor = new PublicExecutor(publicState, publicContracts, commitmentsDb, blockHeader); + 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..6b2674886c8 100644 --- a/yarn-project/acir-simulator/src/public/public_execution_context.ts +++ b/yarn-project/acir-simulator/src/public/public_execution_context.ts @@ -102,9 +102,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); } /** 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/circuit-types/src/l1_to_l2_message.ts b/yarn-project/circuit-types/src/l1_to_l2_message.ts index a072e6c270b..2165d4bd49e 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); diff --git a/yarn-project/circuits.js/src/constants.gen.ts b/yarn-project/circuits.js/src/constants.gen.ts index 79cd3856feb..39a05fee9ca 100644 --- a/yarn-project/circuits.js/src/constants.gen.ts +++ b/yarn-project/circuits.js/src/constants.gen.ts @@ -48,7 +48,7 @@ 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; 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_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/noir-protocol-circuits/src/crates/types/src/constants.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/constants.nr index c0b05053066..ae5fa0d5e19 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 @@ -86,7 +86,7 @@ global ARGS_HASH_CHUNK_COUNT: u32 = 16; // Move these constants to a noir file once the issue bellow 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. From bcab9ceab62bede3bc1c105b3e639e7c64e3217a Mon Sep 17 00:00:00 2001 From: guipublic <47281315+guipublic@users.noreply.github.com> Date: Wed, 24 Jan 2024 13:11:21 +0100 Subject: [PATCH 5/7] feat!: Add big int opcodes (without implementation) (#4050) Adds the biginteger opcode skeleton https://github.com/noir-lang/noir/issues/4040 The PR adds ACIR opcodes for bigint operations. It does not provide any implantation of the opcodes, neither in BB, in the solver or in Brillig. --------- Co-authored-by: kevaundray --- .../dsl/acir_format/acir_format.cpp | 8 + .../dsl/acir_format/acir_format.hpp | 7 +- .../dsl/acir_format/acir_format.test.cpp | 12 + .../acir_format/acir_to_constraint_buf.hpp | 35 + .../dsl/acir_format/bigint_constraint.cpp | 33 + .../dsl/acir_format/bigint_constraint.hpp | 35 + .../acir_format/bigint_constraint.test.cpp | 88 ++ .../dsl/acir_format/block_constraint.test.cpp | 2 + .../dsl/acir_format/ecdsa_secp256k1.test.cpp | 6 + .../dsl/acir_format/ecdsa_secp256r1.test.cpp | 8 + .../acir_format/recursion_constraint.test.cpp | 4 + .../dsl/acir_format/serde/acir.hpp | 1318 +++++++++++++---- noir/acvm-repo/acir/codegen/acir.cpp | 644 +++++++- .../acir/src/circuit/black_box_functions.rs | 25 + .../opcodes/black_box_function_call.rs | 56 +- .../acvm/src/compiler/transformers/mod.rs | 12 +- noir/acvm-repo/acvm/src/pwg/blackbox/mod.rs | 6 + noir/acvm-repo/brillig/src/black_box.rs | 79 +- noir/acvm-repo/brillig_vm/src/black_box.rs | 12 + .../brillig/brillig_gen/brillig_black_box.rs | 101 ++ .../src/brillig/brillig_ir/debug_show.rs | 53 + .../ssa/acir_gen/acir_ir/generated_acir.rs | 62 + .../src/ssa/ir/instruction/call.rs | 9 +- 23 files changed, 2338 insertions(+), 277 deletions(-) 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 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..0fb5cebbe55 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp @@ -94,6 +94,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..82cf7eccad3 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" @@ -41,6 +42,8 @@ struct AcirFormat { 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 @@ -69,7 +72,9 @@ struct AcirFormat { fixed_base_scalar_mul_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..8804cd0c573 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 @@ -47,6 +47,8 @@ TEST_F(AcirFormatTests, TestASingleConstraintNoPubInputs) .ec_add_constraints = {}, .ec_double_constraints = {}, .recursion_constraints = {}, + .bigint_from_le_bytes_constraints = {}, + .bigint_operations = {}, .constraints = { constraint }, .block_constraints = {}, }; @@ -158,6 +160,8 @@ TEST_F(AcirFormatTests, TestLogicGateFromNoirCircuit) .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 = {} }; @@ -221,6 +225,8 @@ TEST_F(AcirFormatTests, TestSchnorrVerifyPass) .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, @@ -312,6 +318,8 @@ TEST_F(AcirFormatTests, TestSchnorrVerifySmallRange) .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, @@ -422,6 +430,8 @@ TEST_F(AcirFormatTests, TestVarKeccak) .ec_add_constraints = {}, .ec_double_constraints = {}, .recursion_constraints = {}, + .bigint_from_le_bytes_constraints = {}, + .bigint_operations = {}, .constraints = { dummy }, .block_constraints = {}, }; @@ -464,6 +474,8 @@ TEST_F(AcirFormatTests, TestKeccakPermutation) .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..5d6cf0063b8 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" @@ -240,6 +241,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..0762bfd783a --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/bigint_constraint.test.cpp @@ -0,0 +1,88 @@ +#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 = {}, + .ec_double_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..7c0444f6191 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 @@ -128,6 +128,8 @@ TEST_F(UltraPlonkRAM, TestBlockConstraint) .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/ecdsa_secp256k1.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp index 5554fdd8d6d..2e17b97008d 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 @@ -107,6 +107,8 @@ TEST_F(ECDSASecp256k1, TestECDSAConstraintSucceed) .ec_add_constraints = {}, .ec_double_constraints = {}, .recursion_constraints = {}, + .bigint_from_le_bytes_constraints = {}, + .bigint_operations = {}, .constraints = {}, .block_constraints = {}, }; @@ -151,6 +153,8 @@ TEST_F(ECDSASecp256k1, TestECDSACompilesForVerifier) .ec_add_constraints = {}, .ec_double_constraints = {}, .recursion_constraints = {}, + .bigint_from_le_bytes_constraints = {}, + .bigint_operations = {}, .constraints = {}, .block_constraints = {}, }; @@ -190,6 +194,8 @@ TEST_F(ECDSASecp256k1, TestECDSAConstraintFail) .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..c3487e35da0 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 @@ -142,6 +142,8 @@ TEST(ECDSASecp256r1, test_hardcoded) .ec_add_constraints = {}, .ec_double_constraints = {}, .recursion_constraints = {}, + .bigint_from_le_bytes_constraints = {}, + .bigint_operations = {}, .constraints = {}, .block_constraints = {}, }; @@ -187,6 +189,8 @@ TEST(ECDSASecp256r1, TestECDSAConstraintSucceed) .ec_add_constraints = {}, .ec_double_constraints = {}, .recursion_constraints = {}, + .bigint_from_le_bytes_constraints = {}, + .bigint_operations = {}, .constraints = {}, .block_constraints = {}, }; @@ -230,6 +234,8 @@ TEST(ECDSASecp256r1, TestECDSACompilesForVerifier) .ec_add_constraints = {}, .ec_double_constraints = {}, .recursion_constraints = {}, + .bigint_from_le_bytes_constraints = {}, + .bigint_operations = {}, .constraints = {}, .block_constraints = {}, }; @@ -268,6 +274,8 @@ TEST(ECDSASecp256r1, TestECDSAConstraintFail) .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..20650bf589b 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 @@ -100,6 +100,8 @@ Builder create_inner_circuit() .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 = {} }; @@ -252,6 +254,8 @@ Builder create_outer_circuit(std::vector& inner_circuits) .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..8e18e5bc043 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp @@ -206,6 +206,65 @@ 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); + }; + std::variant + RecursiveAggregation, + BigIntAdd, + BigIntNeg, + BigIntMul, + BigIntDiv, + BigIntFromLeBytes, + BigIntToLeBytes> value; friend bool operator==(const BlackBoxFuncCall&, const BlackBoxFuncCall&); @@ -239,8 +304,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; @@ -563,6 +626,65 @@ struct BlackBoxOp { static EmbeddedCurveDouble bincodeDeserialize(std::vector); }; + struct BigIntAdd { + Circuit::RegisterIndex lhs; + Circuit::RegisterIndex rhs; + Circuit::RegisterIndex output; + + friend bool operator==(const BigIntAdd&, const BigIntAdd&); + std::vector bincodeSerialize() const; + static BigIntAdd bincodeDeserialize(std::vector); + }; + + struct BigIntNeg { + Circuit::RegisterIndex lhs; + Circuit::RegisterIndex rhs; + Circuit::RegisterIndex output; + + friend bool operator==(const BigIntNeg&, const BigIntNeg&); + std::vector bincodeSerialize() const; + static BigIntNeg bincodeDeserialize(std::vector); + }; + + struct BigIntMul { + Circuit::RegisterIndex lhs; + Circuit::RegisterIndex rhs; + Circuit::RegisterIndex output; + + friend bool operator==(const BigIntMul&, const BigIntMul&); + std::vector bincodeSerialize() const; + static BigIntMul bincodeDeserialize(std::vector); + }; + + struct BigIntDiv { + Circuit::RegisterIndex lhs; + Circuit::RegisterIndex rhs; + Circuit::RegisterIndex 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::RegisterIndex output; + + friend bool operator==(const BigIntFromLeBytes&, const BigIntFromLeBytes&); + std::vector bincodeSerialize() const; + static BigIntFromLeBytes bincodeDeserialize(std::vector); + }; + + struct BigIntToLeBytes { + Circuit::RegisterIndex input; + Circuit::HeapVector output; + + friend bool operator==(const BigIntToLeBytes&, const BigIntToLeBytes&); + std::vector bincodeSerialize() const; + static BigIntToLeBytes bincodeDeserialize(std::vector); + }; + std::variant + EmbeddedCurveDouble, + BigIntAdd, + BigIntNeg, + BigIntMul, + BigIntDiv, + BigIntFromLeBytes, + BigIntToLeBytes> value; friend bool operator==(const BlackBoxOp&, const BlackBoxOp&); @@ -2883,25 +3011,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 +3046,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 +3082,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 +3103,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 +3139,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 +3160,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 +3196,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 +3217,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 +3253,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 +3275,51 @@ 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 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_or_abort("Some input bytes were not read"); } @@ -3170,57 +3330,45 @@ 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::BlackBoxFuncCall::BigIntToLeBytes& obj, Serializer& serializer) { - serde::Serializable::serialize(obj.message, serializer); - serde::Serializable::serialize(obj.output, serializer); + serde::Serializable::serialize(obj.input, serializer); + serde::Serializable::serialize(obj.outputs, serializer); } template <> template -Circuit::BlackBoxOp::Keccakf1600 serde::Deserializable::deserialize( - Deserializer& deserializer) +Circuit::BlackBoxFuncCall::BigIntToLeBytes serde::Deserializable< + Circuit::BlackBoxFuncCall::BigIntToLeBytes>::deserialize(Deserializer& deserializer) { - Circuit::BlackBoxOp::Keccakf1600 obj; - obj.message = serde::Deserializable::deserialize(deserializer); - obj.output = serde::Deserializable::deserialize(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) +inline bool operator==(const BlackBoxOp& lhs, const BlackBoxOp& 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)) { + if (!(lhs.value == rhs.value)) { return false; } return true; } -inline std::vector BlackBoxOp::EcdsaSecp256k1::bincodeSerialize() const +inline std::vector BlackBoxOp::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 BlackBoxOp::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 +3379,668 @@ 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& 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); + serializer.increase_container_depth(); + serde::Serializable::serialize(obj.value, serializer); + serializer.decrease_container_depth(); } template <> template -Circuit::BlackBoxOp::EcdsaSecp256k1 serde::Deserializable::deserialize( - Deserializer& deserializer) +Circuit::BlackBoxOp 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); + 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::EcdsaSecp256r1& lhs, const BlackBoxOp::EcdsaSecp256r1& rhs) +inline bool operator==(const BlackBoxOp::Sha256& lhs, const BlackBoxOp::Sha256& rhs) { - if (!(lhs.hashed_msg == rhs.hashed_msg)) { - return false; - } - if (!(lhs.public_key_x == rhs.public_key_x)) { + if (!(lhs.message == rhs.message)) { return false; } - if (!(lhs.public_key_y == rhs.public_key_y)) { + if (!(lhs.output == rhs.output)) { return false; } - if (!(lhs.signature == rhs.signature)) { + 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); + 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"); + } + 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::FixedBaseScalarMul::bincodeSerialize() const +{ + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); +} + +inline BlackBoxOp::FixedBaseScalarMul BlackBoxOp::FixedBaseScalarMul::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::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::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)) { @@ -3277,17 +4049,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,44 +4070,38 @@ 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::EmbeddedCurveDouble& lhs, const BlackBoxOp::EmbeddedCurveDouble& 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)) { + if (!(lhs.input1_x == rhs.input1_x)) { return false; } - if (!(lhs.signature == rhs.signature)) { + if (!(lhs.input1_y == rhs.input1_y)) { return false; } if (!(lhs.result == rhs.result)) { @@ -3344,17 +4110,17 @@ inline bool operator==(const BlackBoxOp::SchnorrVerify& lhs, const BlackBoxOp::S return true; } -inline std::vector BlackBoxOp::SchnorrVerify::bincodeSerialize() const +inline std::vector BlackBoxOp::EmbeddedCurveDouble::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::EmbeddedCurveDouble BlackBoxOp::EmbeddedCurveDouble::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 +4131,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::EmbeddedCurveDouble& 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.input1_x, serializer); + serde::Serializable::serialize(obj.input1_y, serializer); serde::Serializable::serialize(obj.result, serializer); } template <> template -Circuit::BlackBoxOp::SchnorrVerify serde::Deserializable::deserialize( +Circuit::BlackBoxOp::EmbeddedCurveDouble 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); + Circuit::BlackBoxOp::EmbeddedCurveDouble obj; + obj.input1_x = serde::Deserializable::deserialize(deserializer); + obj.input1_y = 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) +inline bool operator==(const BlackBoxOp::BigIntAdd& lhs, const BlackBoxOp::BigIntAdd& 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 +4167,17 @@ inline bool operator==(const BlackBoxOp::PedersenCommitment& lhs, const BlackBox return true; } -inline std::vector BlackBoxOp::PedersenCommitment::bincodeSerialize() const +inline std::vector BlackBoxOp::BigIntAdd::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::BigIntAdd BlackBoxOp::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"); } @@ -3426,34 +4188,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::BigIntAdd& 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::BigIntAdd serde::Deserializable::deserialize( Deserializer& deserializer) { - Circuit::BlackBoxOp::PedersenCommitment obj; - obj.inputs = serde::Deserializable::deserialize(deserializer); - obj.domain_separator = serde::Deserializable::deserialize(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::PedersenHash& lhs, const BlackBoxOp::PedersenHash& rhs) +inline bool operator==(const BlackBoxOp::BigIntNeg& lhs, const BlackBoxOp::BigIntNeg& 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)) { @@ -3462,17 +4224,17 @@ inline bool operator==(const BlackBoxOp::PedersenHash& lhs, const BlackBoxOp::Pe return true; } -inline std::vector BlackBoxOp::PedersenHash::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::PedersenHash BlackBoxOp::PedersenHash::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"); } @@ -3483,53 +4245,53 @@ 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::BigIntNeg& 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::PedersenHash serde::Deserializable::deserialize( +Circuit::BlackBoxOp::BigIntNeg serde::Deserializable::deserialize( Deserializer& deserializer) { - Circuit::BlackBoxOp::PedersenHash obj; - obj.inputs = serde::Deserializable::deserialize(deserializer); - obj.domain_separator = 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::FixedBaseScalarMul& lhs, const BlackBoxOp::FixedBaseScalarMul& rhs) +inline bool operator==(const BlackBoxOp::BigIntMul& lhs, const BlackBoxOp::BigIntMul& rhs) { - if (!(lhs.low == rhs.low)) { + if (!(lhs.lhs == rhs.lhs)) { return false; } - if (!(lhs.high == rhs.high)) { + if (!(lhs.rhs == rhs.rhs)) { 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::BigIntMul::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::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"); } @@ -3540,59 +4302,110 @@ 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::BigIntMul& 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.lhs, serializer); + serde::Serializable::serialize(obj.rhs, serializer); + serde::Serializable::serialize(obj.output, serializer); } template <> template -Circuit::BlackBoxOp::FixedBaseScalarMul serde::Deserializable::deserialize( +Circuit::BlackBoxOp::BigIntMul 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::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::EmbeddedCurveAdd& lhs, const BlackBoxOp::EmbeddedCurveAdd& rhs) +inline bool operator==(const BlackBoxOp::BigIntDiv& lhs, const BlackBoxOp::BigIntDiv& rhs) { - if (!(lhs.input1_x == rhs.input1_x)) { + if (!(lhs.lhs == rhs.lhs)) { return false; } - if (!(lhs.input1_y == rhs.input1_y)) { + if (!(lhs.rhs == rhs.rhs)) { return false; } - if (!(lhs.input2_x == rhs.input2_x)) { + if (!(lhs.output == rhs.output)) { return false; } - if (!(lhs.input2_y == rhs.input2_y)) { + 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"); + } + return value; +} + +} // end of namespace Circuit + +template <> +template +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::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::BigIntFromLeBytes& lhs, const BlackBoxOp::BigIntFromLeBytes& rhs) +{ + if (!(lhs.inputs == rhs.inputs)) { return false; } - if (!(lhs.result == rhs.result)) { + if (!(lhs.modulus == rhs.modulus)) { + return false; + } + if (!(lhs.output == rhs.output)) { return false; } return true; } -inline std::vector BlackBoxOp::EmbeddedCurveAdd::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::EmbeddedCurveAdd BlackBoxOp::EmbeddedCurveAdd::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"); } @@ -3603,57 +4416,50 @@ 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::BigIntFromLeBytes& 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.inputs, serializer); + serde::Serializable::serialize(obj.modulus, serializer); + serde::Serializable::serialize(obj.output, serializer); } template <> template -Circuit::BlackBoxOp::EmbeddedCurveAdd serde::Deserializable::deserialize( +Circuit::BlackBoxOp::BigIntFromLeBytes 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::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::EmbeddedCurveDouble& lhs, const BlackBoxOp::EmbeddedCurveDouble& rhs) +inline bool operator==(const BlackBoxOp::BigIntToLeBytes& lhs, const BlackBoxOp::BigIntToLeBytes& rhs) { - if (!(lhs.input1_x == rhs.input1_x)) { - return false; - } - if (!(lhs.input1_y == rhs.input1_y)) { + 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::EmbeddedCurveDouble::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::EmbeddedCurveDouble BlackBoxOp::EmbeddedCurveDouble::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"); } @@ -3664,23 +4470,21 @@ 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::BigIntToLeBytes& 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.output, serializer); } template <> template -Circuit::BlackBoxOp::EmbeddedCurveDouble serde::Deserializable::deserialize( +Circuit::BlackBoxOp::BigIntToLeBytes 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::BigIntToLeBytes obj; + obj.input = serde::Deserializable::deserialize(deserializer); + obj.output = serde::Deserializable::deserialize(deserializer); return obj; } diff --git a/noir/acvm-repo/acir/codegen/acir.cpp b/noir/acvm-repo/acir/codegen/acir.cpp index 9b74a8ea631..07bcd5f9f97 100644 --- a/noir/acvm-repo/acir/codegen/acir.cpp +++ b/noir/acvm-repo/acir/codegen/acir.cpp @@ -206,7 +206,66 @@ 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); + }; + + std::variant value; friend bool operator==(const BlackBoxFuncCall&, const BlackBoxFuncCall&); std::vector bincodeSerialize() const; @@ -543,7 +602,66 @@ namespace Circuit { static EmbeddedCurveDouble bincodeDeserialize(std::vector); }; - std::variant value; + struct BigIntAdd { + Circuit::RegisterIndex lhs; + Circuit::RegisterIndex rhs; + Circuit::RegisterIndex output; + + friend bool operator==(const BigIntAdd&, const BigIntAdd&); + std::vector bincodeSerialize() const; + static BigIntAdd bincodeDeserialize(std::vector); + }; + + struct BigIntNeg { + Circuit::RegisterIndex lhs; + Circuit::RegisterIndex rhs; + Circuit::RegisterIndex output; + + friend bool operator==(const BigIntNeg&, const BigIntNeg&); + std::vector bincodeSerialize() const; + static BigIntNeg bincodeDeserialize(std::vector); + }; + + struct BigIntMul { + Circuit::RegisterIndex lhs; + Circuit::RegisterIndex rhs; + Circuit::RegisterIndex output; + + friend bool operator==(const BigIntMul&, const BigIntMul&); + std::vector bincodeSerialize() const; + static BigIntMul bincodeDeserialize(std::vector); + }; + + struct BigIntDiv { + Circuit::RegisterIndex lhs; + Circuit::RegisterIndex rhs; + Circuit::RegisterIndex 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::RegisterIndex output; + + friend bool operator==(const BigIntFromLeBytes&, const BigIntFromLeBytes&); + std::vector bincodeSerialize() const; + static BigIntFromLeBytes bincodeDeserialize(std::vector); + }; + + struct BigIntToLeBytes { + Circuit::RegisterIndex input; + Circuit::HeapVector output; + + friend bool operator==(const BigIntToLeBytes&, const BigIntToLeBytes&); + std::vector bincodeSerialize() const; + static BigIntToLeBytes bincodeDeserialize(std::vector); + }; + + std::variant value; friend bool operator==(const BlackBoxOp&, const BlackBoxOp&); std::vector bincodeSerialize() const; @@ -2469,6 +2587,267 @@ Circuit::BlackBoxFuncCall::RecursiveAggregation serde::Deserializable BlackBoxFuncCall::BigIntAdd::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline BlackBoxFuncCall::BigIntAdd BlackBoxFuncCall::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::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::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 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 BlackBoxFuncCall::BigIntNeg::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline BlackBoxFuncCall::BigIntNeg BlackBoxFuncCall::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::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::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 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 BlackBoxFuncCall::BigIntMul::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline BlackBoxFuncCall::BigIntMul BlackBoxFuncCall::BigIntMul::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::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::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 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 BlackBoxFuncCall::BigIntDiv::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline BlackBoxFuncCall::BigIntDiv BlackBoxFuncCall::BigIntDiv::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::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::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 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 BlackBoxFuncCall::BigIntFromLeBytes::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline BlackBoxFuncCall::BigIntFromLeBytes BlackBoxFuncCall::BigIntFromLeBytes::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::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::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 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 BlackBoxFuncCall::BigIntToLeBytes::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + 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 serde::deserialization_error("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::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 &lhs, const BlackBoxOp &rhs) { @@ -3092,6 +3471,267 @@ Circuit::BlackBoxOp::EmbeddedCurveDouble serde::Deserializable 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::BigIntMul::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline BlackBoxOp::BigIntMul BlackBoxOp::BigIntMul::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::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 serde::deserialization_error("Some input bytes were not read"); + } + return value; + } + +} // end of namespace Circuit + +template <> +template +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::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::BigIntFromLeBytes &lhs, const BlackBoxOp::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::BigIntFromLeBytes::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline BlackBoxOp::BigIntFromLeBytes BlackBoxOp::BigIntFromLeBytes::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::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::BigIntFromLeBytes serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BlackBoxOp::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::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::BigIntToLeBytes::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline BlackBoxOp::BigIntToLeBytes BlackBoxOp::BigIntToLeBytes::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::BigIntToLeBytes &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.input, serializer); + serde::Serializable::serialize(obj.output, serializer); +} + +template <> +template +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 BlockId &lhs, const BlockId &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..41b8923a8c9 100644 --- a/noir/acvm-repo/acir/src/circuit/black_box_functions.rs +++ b/noir/acvm-repo/acir/src/circuit/black_box_functions.rs @@ -49,6 +49,18 @@ pub enum BlackBoxFunc { 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, } impl std::fmt::Display for BlackBoxFunc { @@ -77,8 +89,15 @@ 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", } } + pub fn lookup(op_name: &str) -> Option { match op_name { "sha256" => Some(BlackBoxFunc::SHA256), @@ -98,6 +117,12 @@ impl BlackBoxFunc { "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), _ => None, } } 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..1fdc0265377 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 @@ -120,6 +120,35 @@ 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, + }, } impl BlackBoxFuncCall { @@ -143,6 +172,12 @@ impl BlackBoxFuncCall { 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, } } @@ -158,10 +193,16 @@ impl BlackBoxFuncCall { | BlackBoxFuncCall::Keccak256 { inputs, .. } | BlackBoxFuncCall::Keccakf1600 { inputs, .. } | BlackBoxFuncCall::PedersenCommitment { inputs, .. } - | BlackBoxFuncCall::PedersenHash { inputs, .. } => inputs.to_vec(), + | BlackBoxFuncCall::PedersenHash { inputs, .. } + | BlackBoxFuncCall::BigIntFromLeBytes { 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, .. @@ -249,7 +290,8 @@ impl BlackBoxFuncCall { | BlackBoxFuncCall::Blake2s { outputs, .. } | BlackBoxFuncCall::Blake3 { outputs, .. } | BlackBoxFuncCall::Keccak256 { outputs, .. } - | BlackBoxFuncCall::Keccakf1600 { outputs, .. } => outputs.to_vec(), + | BlackBoxFuncCall::Keccakf1600 { outputs, .. } + | BlackBoxFuncCall::Keccak256VariableLength { outputs, .. } => outputs.to_vec(), BlackBoxFuncCall::AND { output, .. } | BlackBoxFuncCall::XOR { output, .. } | BlackBoxFuncCall::SchnorrVerify { output, .. } @@ -260,10 +302,16 @@ impl BlackBoxFuncCall { | BlackBoxFuncCall::PedersenCommitment { outputs, .. } | BlackBoxFuncCall::EmbeddedCurveAdd { outputs, .. } | BlackBoxFuncCall::EmbeddedCurveDouble { outputs, .. } => vec![outputs.0, outputs.1], - BlackBoxFuncCall::RANGE { .. } | BlackBoxFuncCall::RecursiveAggregation { .. } => { + 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/acvm/src/compiler/transformers/mod.rs b/noir/acvm-repo/acvm/src/compiler/transformers/mod.rs index 306ea1b7c12..effe1128d36 100644 --- a/noir/acvm-repo/acvm/src/compiler/transformers/mod.rs +++ b/noir/acvm-repo/acvm/src/compiler/transformers/mod.rs @@ -105,7 +105,12 @@ pub(super) fn transform_internal( transformer.mark_solvable(*output); } acir::circuit::opcodes::BlackBoxFuncCall::RANGE { .. } - | acir::circuit::opcodes::BlackBoxFuncCall::RecursiveAggregation { .. } => (), + | acir::circuit::opcodes::BlackBoxFuncCall::RecursiveAggregation { .. } + | acir::circuit::opcodes::BlackBoxFuncCall::BigIntFromLeBytes { .. } + | acir::circuit::opcodes::BlackBoxFuncCall::BigIntAdd { .. } + | acir::circuit::opcodes::BlackBoxFuncCall::BigIntNeg { .. } + | acir::circuit::opcodes::BlackBoxFuncCall::BigIntMul { .. } + | acir::circuit::opcodes::BlackBoxFuncCall::BigIntDiv { .. } => (), acir::circuit::opcodes::BlackBoxFuncCall::SHA256 { outputs, .. } | acir::circuit::opcodes::BlackBoxFuncCall::Keccak256 { outputs, .. } | acir::circuit::opcodes::BlackBoxFuncCall::Keccak256VariableLength { @@ -114,7 +119,10 @@ pub(super) fn transform_internal( } | acir::circuit::opcodes::BlackBoxFuncCall::Keccakf1600 { outputs, .. } | acir::circuit::opcodes::BlackBoxFuncCall::Blake2s { outputs, .. } - | acir::circuit::opcodes::BlackBoxFuncCall::Blake3 { outputs, .. } => { + | acir::circuit::opcodes::BlackBoxFuncCall::Blake3 { outputs, .. } + | acir::circuit::opcodes::BlackBoxFuncCall::BigIntToLeBytes { + outputs, .. + } => { for witness in outputs { transformer.mark_solvable(*witness); } diff --git a/noir/acvm-repo/acvm/src/pwg/blackbox/mod.rs b/noir/acvm-repo/acvm/src/pwg/blackbox/mod.rs index 5eea234885c..a56b24b86f3 100644 --- a/noir/acvm-repo/acvm/src/pwg/blackbox/mod.rs +++ b/noir/acvm-repo/acvm/src/pwg/blackbox/mod.rs @@ -185,5 +185,11 @@ pub(crate) fn solve( } // 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!(), } } diff --git a/noir/acvm-repo/brillig/src/black_box.rs b/noir/acvm-repo/brillig/src/black_box.rs index c007e78b785..5893758ce9e 100644 --- a/noir/acvm-repo/brillig/src/black_box.rs +++ b/noir/acvm-repo/brillig/src/black_box.rs @@ -6,15 +6,30 @@ 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, @@ -40,11 +55,23 @@ pub enum BlackBoxOp { result: RegisterIndex, }, /// Calculates a Pedersen commitment to the inputs. - PedersenCommitment { inputs: HeapVector, domain_separator: RegisterIndex, output: HeapArray }, + PedersenCommitment { + inputs: HeapVector, + domain_separator: RegisterIndex, + output: HeapArray, + }, /// Calculates a Pedersen hash to the inputs. - PedersenHash { inputs: HeapVector, domain_separator: RegisterIndex, output: RegisterIndex }, + PedersenHash { + inputs: HeapVector, + domain_separator: RegisterIndex, + output: RegisterIndex, + }, /// Performs scalar multiplication over the embedded curve. - FixedBaseScalarMul { low: RegisterIndex, high: RegisterIndex, result: HeapArray }, + FixedBaseScalarMul { + low: RegisterIndex, + high: RegisterIndex, + result: HeapArray, + }, /// Performs addition over the embedded curve. EmbeddedCurveAdd { input1_x: RegisterIndex, @@ -54,5 +81,39 @@ pub enum BlackBoxOp { result: HeapArray, }, /// Performs point doubling over the embedded curve. - EmbeddedCurveDouble { input1_x: RegisterIndex, input1_y: RegisterIndex, result: HeapArray }, + EmbeddedCurveDouble { + input1_x: RegisterIndex, + input1_y: RegisterIndex, + result: HeapArray, + }, + + BigIntAdd { + lhs: RegisterIndex, + rhs: RegisterIndex, + output: RegisterIndex, + }, + BigIntNeg { + lhs: RegisterIndex, + rhs: RegisterIndex, + output: RegisterIndex, + }, + BigIntMul { + lhs: RegisterIndex, + rhs: RegisterIndex, + output: RegisterIndex, + }, + BigIntDiv { + lhs: RegisterIndex, + rhs: RegisterIndex, + output: RegisterIndex, + }, + BigIntFromLeBytes { + inputs: HeapVector, + modulus: HeapVector, + output: RegisterIndex, + }, + BigIntToLeBytes { + input: RegisterIndex, + output: HeapVector, + }, } diff --git a/noir/acvm-repo/brillig_vm/src/black_box.rs b/noir/acvm-repo/brillig_vm/src/black_box.rs index 463038509e1..edbebb61ece 100644 --- a/noir/acvm-repo/brillig_vm/src/black_box.rs +++ b/noir/acvm-repo/brillig_vm/src/black_box.rs @@ -200,6 +200,12 @@ pub(crate) fn evaluate_black_box( registers.set(*output, hash.into()); Ok(()) } + BlackBoxOp::BigIntAdd { .. } => todo!(), + BlackBoxOp::BigIntNeg { .. } => todo!(), + BlackBoxOp::BigIntMul { .. } => todo!(), + BlackBoxOp::BigIntDiv { .. } => todo!(), + BlackBoxOp::BigIntFromLeBytes { .. } => todo!(), + BlackBoxOp::BigIntToLeBytes { .. } => todo!(), } } @@ -218,6 +224,12 @@ fn black_box_function_from_op(op: &BlackBoxOp) -> BlackBoxFunc { 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, } } 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..c7b6f39279b 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 @@ -244,6 +244,107 @@ 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" + ) + } + } } } 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..5709b0a1aa2 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 @@ -457,6 +457,59 @@ 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 + ); + } } } 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..4572cf1dd08 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 @@ -251,6 +251,34 @@ impl GeneratedAcir { public_inputs: inputs[2].clone(), key_hash: inputs[3][0], }, + BlackBoxFunc::BigIntAdd => BlackBoxFuncCall::BigIntAdd { + lhs: constants[0].to_u128() as u32, + rhs: constants[1].to_u128() as u32, + output: constants[2].to_u128() as u32, + }, + BlackBoxFunc::BigIntNeg => BlackBoxFuncCall::BigIntNeg { + lhs: constants[0].to_u128() as u32, + rhs: constants[1].to_u128() as u32, + output: constants[2].to_u128() as u32, + }, + BlackBoxFunc::BigIntMul => BlackBoxFuncCall::BigIntMul { + lhs: constants[0].to_u128() as u32, + rhs: constants[1].to_u128() as u32, + output: constants[2].to_u128() as u32, + }, + BlackBoxFunc::BigIntDiv => BlackBoxFuncCall::BigIntDiv { + lhs: constants[0].to_u128() as u32, + rhs: constants[1].to_u128() as u32, + output: constants[2].to_u128() as u32, + }, + BlackBoxFunc::BigIntFromLeBytes => BlackBoxFuncCall::BigIntFromLeBytes { + inputs: inputs[0].clone(), + modulus: vecmap(constants, |c| c.to_u128() as u8), + output: todo!(), + }, + BlackBoxFunc::BigIntToLeBytes => { + BlackBoxFuncCall::BigIntToLeBytes { input: constants[0].to_u128() as u32, outputs } + } }; self.push_opcode(AcirOpcode::BlackBoxFuncCall(black_box_func_call)); @@ -573,6 +601,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 @@ -593,15 +622,30 @@ 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 2 inputs + BlackBoxFunc::BigIntAdd + | BlackBoxFunc::BigIntNeg + | BlackBoxFunc::BigIntMul + | BlackBoxFunc::BigIntDiv => Some(2), + + // FromLeBytes takes a variable array of bytes as input + BlackBoxFunc::BigIntFromLeBytes => None, + // ToLeBytes takes a single big integer as input + BlackBoxFunc::BigIntToLeBytes => Some(1), } } @@ -612,28 +656,46 @@ 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), + // 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), + + // Big integer operations return a big integer + BlackBoxFunc::BigIntAdd + | BlackBoxFunc::BigIntNeg + | BlackBoxFunc::BigIntMul + | BlackBoxFunc::BigIntDiv + | BlackBoxFunc::BigIntFromLeBytes => Some(1), + + // ToLeBytes returns a variable array of bytes + BlackBoxFunc::BigIntToLeBytes => None, + // Recursive aggregation has a variable number of outputs BlackBoxFunc::RecursiveAggregation => None, } 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..ff9dc4f345e 100644 --- a/noir/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/noir/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -434,8 +434,13 @@ fn simplify_black_box_func( // 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`") From ddac77ba23bb46f4df6cd1676a528aa6d407ea39 Mon Sep 17 00:00:00 2001 From: AztecBot Date: Wed, 24 Jan 2024 12:11:58 +0000 Subject: [PATCH 6/7] git subrepo push --branch=aztec-packages noir subrepo: subdir: "noir" merged: "7dba0a18a" upstream: origin: "https://github.com/noir-lang/noir" branch: "aztec-packages" commit: "7dba0a18a" git-subrepo: version: "0.4.6" origin: "???" commit: "???" [skip ci] --- noir/.gitrepo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/noir/.gitrepo b/noir/.gitrepo index 8fb835cdb68..9fa4e9ae521 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 = 7dba0a18ad83e74f8114c6ca594ad10e5e334704 + parent = bcab9ceab62bede3bc1c105b3e639e7c64e3217a method = merge cmdver = 0.4.6 From 301f0e6d0832a999a31d0e9a5b4e8267474de6ab Mon Sep 17 00:00:00 2001 From: Lasse Herskind <16536249+LHerskind@users.noreply.github.com> Date: Wed, 24 Jan 2024 12:16:25 +0000 Subject: [PATCH 7/7] feat!: Updates singleton usage (#4186) Fixes #4174 by making the singletons leaky. - Updates the docs - Adds more tests to the singleton e2e. --- .../dev_docs/contracts/syntax/storage/main.md | 12 +- .../src/state_vars/immutable_singleton.nr | 28 ++-- .../aztec/src/state_vars/singleton.nr | 45 +++--- .../end-to-end/src/e2e_singleton.test.ts | 133 ++++++++++++++++-- .../docs_example_contract/src/main.nr | 47 ++++++- .../ecdsa_account_contract/src/main.nr | 2 +- .../schnorr_account_contract/src/main.nr | 2 +- .../contracts/test_contract/src/main.nr | 2 +- .../token_blacklist_contract/src/main.nr | 2 +- 9 files changed, 212 insertions(+), 61 deletions(-) diff --git a/docs/docs/dev_docs/contracts/syntax/storage/main.md b/docs/docs/dev_docs/contracts/syntax/storage/main.md index b774ee0c682..263363832f1 100644 --- a/docs/docs/dev_docs/contracts/syntax/storage/main.md +++ b/docs/docs/dev_docs/contracts/syntax/storage/main.md @@ -288,7 +288,11 @@ As part of the initialization of the `Storage` struct, the `Singleton` is create 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. +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 is "leaky". This means that if the storage slot depends on the an address then it is possible to link the nullifier to the address. For example, 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. @@ -338,7 +342,11 @@ As part of the initialization of the `Storage` struct, the `Singleton` is create ### `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. +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 is "leaky". This means that if the storage slot depends on the an address then it is possible to link the nullifier to the address. For example, 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: 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..5bdc4d072d3 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,10 @@ use dep::std::option::Option; use dep::protocol_types::{ address::AztecAddress, + constants::{ + GENERATOR_INDEX__INITIALIZATION_NULLIFIER, + }, + hash::pedersen_hash, }; use crate::context::{PrivateContext, Context}; @@ -11,14 +15,12 @@ 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 { context: Option<&mut PrivateContext>, storage_slot: Field, note_interface: NoteInterface, - compute_initialization_nullifier: fn (Field, Option, Option<&mut PrivateContext>) -> Field, } // docs:end:struct @@ -30,19 +32,27 @@ impl ImmutableSingleton { 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 @@ -51,14 +61,12 @@ impl ImmutableSingleton { pub fn initialize( self, note: &mut Note, - owner: Option, broadcast: bool, ) { 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( 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..b5a2d3fe280 100644 --- a/yarn-project/aztec-nr/aztec/src/state_vars/singleton.nr +++ b/yarn-project/aztec-nr/aztec/src/state_vars/singleton.nr @@ -20,32 +20,11 @@ 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 { context: Option<&mut PrivateContext>, storage_slot: Field, note_interface: NoteInterface, - compute_initialization_nullifier: fn (Field, Option, Option<&mut PrivateContext>) -> Field, } // docs:end:struct @@ -57,19 +36,29 @@ impl Singleton { 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 @@ -78,14 +67,12 @@ impl Singleton { pub fn initialize( self, note: &mut Note, - owner: Option, broadcast: bool, ) { 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); diff --git a/yarn-project/end-to-end/src/e2e_singleton.test.ts b/yarn-project/end-to-end/src/e2e_singleton.test.ts index 251b4e7a44f..6c1407ef053 100644 --- a/yarn-project/end-to-end/src/e2e_singleton.test.ts +++ b/yarn-project/end-to-end/src/e2e_singleton.test.ts @@ -1,4 +1,4 @@ -import { Fr, Wallet } from '@aztec/aztec.js'; +import { TxStatus, Wallet } from '@aztec/aztec.js'; import { DocsExampleContract } from '@aztec/noir-contracts'; import { setup } from './fixtures/utils.js'; @@ -9,23 +9,134 @@ describe('e2e_singleton', () => { 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(); - // 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', - ); + 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); + }); - const newPoints = 3n; - await contract.methods.update_legendary_card(Fr.random(), newPoints).send().wait(); - expect((await contract.methods.get_leader().view()).points).toEqual(newPoints); + 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/noir-contracts/contracts/docs_example_contract/src/main.nr b/yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr index 6662f6de2f0..e7c9befa319 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 @@ -23,7 +23,7 @@ contract DocsExample { 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}, }; // how to import methods from other files/folders within your workspace use crate::options::create_account_card_getter_options; @@ -42,6 +42,7 @@ contract DocsExample { // docs:start:storage-map-singleton-declaration profiles: Map>, // docs:end:storage-map-singleton-declaration + imm_singleton: ImmutableSingleton, } impl Storage { @@ -65,6 +66,7 @@ contract DocsExample { }, ), // docs:end:state_vars-MapSingleton + imm_singleton: ImmutableSingleton::new(context, 4, CardNoteMethods), } } } @@ -72,25 +74,44 @@ contract DocsExample { #[aztec(private)] fn constructor() {} + #[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 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 +133,22 @@ contract DocsExample { storage.leader.read() } + unconstrained fn get_legendary_card() -> pub CardNote { + storage.legendary_card.view_note() + } + + unconstrained fn is_legendary_initialized() -> pub bool { + storage.legendary_card.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, 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..da730b23c32 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 @@ -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 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..001437e9c9d 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 @@ -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 } 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..6402ec444e8 100644 --- a/yarn-project/noir-contracts/contracts/test_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/test_contract/src/main.nr @@ -193,7 +193,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 { 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..46ff827b00c 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 @@ -110,7 +110,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(