diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..a799b96 --- /dev/null +++ b/.env.example @@ -0,0 +1,4 @@ +CONTRACT_ADDRESS= +DEPLOYER_ADDRESS= +DEPLOYMENT_SALT= +AZTEC_NODE_URL=http://localhost:8080 \ No newline at end of file diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 293b294..c1ea76e 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -9,38 +9,38 @@ jobs: timeout-minutes: 120 runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: - node-version: lts/* + - uses: actions/setup-node@v4 + with: + node-version: lts/* - - name: Set up Docker - uses: docker/setup-buildx-action@v2 + - name: Set up Docker + uses: docker/setup-buildx-action@v2 - - name: Install Aztec CLI - run: | - curl -s https://install.aztec.network > tmp.sh - bash tmp.sh <<< yes "yes" + - name: Install Aztec CLI + run: | + curl -s https://install.aztec.network > tmp.sh + bash tmp.sh <<< yes "yes" - - name: Update path - run: echo "/home/runner/.aztec/bin" >> $GITHUB_PATH + - name: Update path + run: echo "/home/runner/.aztec/bin" >> $GITHUB_PATH - - name: Set Aztec version and start sandbox - run: | - aztec-up 0.87.4 - aztec start --sandbox & + - name: Set Aztec version and start sandbox + run: | + aztec-up 2.0.2 + aztec start --sandbox & - - name: Install dependencies - working-directory: ./app - run: npm install -g yarn && yarn + - name: Install dependencies + working-directory: ./app + run: npm install -g yarn && yarn - - name: Install Playwright Browsers - working-directory: ./app - run: yarn playwright install --with-deps + - name: Install Playwright Browsers + working-directory: ./app + run: yarn playwright install --with-deps - - name: Build - run: yarn build + - name: Build + run: yarn build - - name: Run tests - run: yarn test + - name: Run tests + run: yarn test diff --git a/.gitignore b/.gitignore index 234f4f6..334d583 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,4 @@ blob-report/ playwright/.cache/ .env +app/artifacts/ \ No newline at end of file diff --git a/README.md b/README.md index e371e07..1014781 100644 --- a/README.md +++ b/README.md @@ -10,11 +10,10 @@ This is an example web app that demonstrates how to interact with an Aztec contr 1. Install the Aztec tools from the first few steps in [Quick Start Guide](https://docs.aztec.network/developers/getting_started). -Please note that this project uses `0.87.4` version of Aztec SDK. If you wish to use a different version, please update the dependencies in the `app/package.json` and in `contracts/Nargo.toml` file to match your version. +Please note that this project uses `2.0.2` version of Aztec SDK. If you wish to use a different version, please update the dependencies in the `app/package.json` and in `contracts/Nargo.toml` file to match your version. You can install a specific version of Aztec tools by running `aztec-up 0.X.X` - 2. Compile smart contracts in `/contracts`: ```sh diff --git a/app/artifacts/EasyPrivateVoting.ts b/app/artifacts/EasyPrivateVoting.ts deleted file mode 100644 index ee66b3a..0000000 --- a/app/artifacts/EasyPrivateVoting.ts +++ /dev/null @@ -1,168 +0,0 @@ - -/* Autogenerated file, do not edit! */ - -/* eslint-disable */ -import { - type AbiType, - AztecAddress, - type AztecAddressLike, - CompleteAddress, - Contract, - type ContractArtifact, - ContractBase, - ContractFunctionInteraction, - type ContractInstanceWithAddress, - type ContractMethod, - type ContractStorageLayout, - type ContractNotes, - decodeFromAbi, - DeployMethod, - EthAddress, - type EthAddressLike, - EventSelector, - type FieldLike, - Fr, - type FunctionSelectorLike, - loadContractArtifact, - loadContractArtifactForPublic, - type NoirCompiledContract, - NoteSelector, - Point, - type PublicKey, - PublicKeys, - type Wallet, - type U128Like, - type WrappedFieldLike, -} from '@aztec/aztec.js'; -import EasyPrivateVotingContractArtifactJson from './private_voting-EasyPrivateVoting.json' with { type: 'json' }; -export const EasyPrivateVotingContractArtifact = loadContractArtifact(EasyPrivateVotingContractArtifactJson as NoirCompiledContract); - - - -/** - * Type-safe interface for contract EasyPrivateVoting; - */ -export class EasyPrivateVotingContract extends ContractBase { - - private constructor( - instance: ContractInstanceWithAddress, - wallet: Wallet, - ) { - super(instance, EasyPrivateVotingContractArtifact, wallet); - } - - - - /** - * Creates a contract instance. - * @param address - The deployed contract's address. - * @param wallet - The wallet to use when interacting with the contract. - * @returns A promise that resolves to a new Contract instance. - */ - public static async at( - address: AztecAddress, - wallet: Wallet, - ) { - return Contract.at(address, EasyPrivateVotingContract.artifact, wallet) as Promise; - } - - - /** - * Creates a tx to deploy a new instance of this contract. - */ - public static deploy(wallet: Wallet, admin: AztecAddressLike) { - return new DeployMethod(PublicKeys.default(), wallet, EasyPrivateVotingContractArtifact, EasyPrivateVotingContract.at, Array.from(arguments).slice(1)); - } - - /** - * Creates a tx to deploy a new instance of this contract using the specified public keys hash to derive the address. - */ - public static deployWithPublicKeys(publicKeys: PublicKeys, wallet: Wallet, admin: AztecAddressLike) { - return new DeployMethod(publicKeys, wallet, EasyPrivateVotingContractArtifact, EasyPrivateVotingContract.at, Array.from(arguments).slice(2)); - } - - /** - * Creates a tx to deploy a new instance of this contract using the specified constructor method. - */ - public static deployWithOpts( - opts: { publicKeys?: PublicKeys; method?: M; wallet: Wallet }, - ...args: Parameters - ) { - return new DeployMethod( - opts.publicKeys ?? PublicKeys.default(), - opts.wallet, - EasyPrivateVotingContractArtifact, - EasyPrivateVotingContract.at, - Array.from(arguments).slice(1), - opts.method ?? 'constructor', - ); - } - - - - /** - * Returns this contract's artifact. - */ - public static get artifact(): ContractArtifact { - return EasyPrivateVotingContractArtifact; - } - - /** - * Returns this contract's artifact with public bytecode. - */ - public static get artifactForPublic(): ContractArtifact { - return loadContractArtifactForPublic(EasyPrivateVotingContractArtifactJson as NoirCompiledContract); - } - - - public static get storage(): ContractStorageLayout<'admin' | 'tally' | 'vote_ended' | 'active_at_block'> { - return { - admin: { - slot: new Fr(1n), - }, -tally: { - slot: new Fr(2n), - }, -vote_ended: { - slot: new Fr(3n), - }, -active_at_block: { - slot: new Fr(4n), - } - } as ContractStorageLayout<'admin' | 'tally' | 'vote_ended' | 'active_at_block'>; - } - - - public static get notes(): ContractNotes<'ValueNote'> { - return { - ValueNote: { - id: new NoteSelector(0), - } - } as ContractNotes<'ValueNote'>; - } - - - /** Type-safe wrappers for the public methods exposed by the contract. */ - public declare methods: { - - /** cast_vote(candidate: field) */ - cast_vote: ((candidate: FieldLike) => ContractFunctionInteraction) & Pick; - - /** constructor(admin: struct) */ - constructor: ((admin: AztecAddressLike) => ContractFunctionInteraction) & Pick; - - /** end_vote() */ - end_vote: (() => ContractFunctionInteraction) & Pick; - - /** get_vote(candidate: field) */ - get_vote: ((candidate: FieldLike) => ContractFunctionInteraction) & Pick; - - /** public_dispatch(selector: field) */ - public_dispatch: ((selector: FieldLike) => ContractFunctionInteraction) & Pick; - - /** sync_private_state() */ - sync_private_state: (() => ContractFunctionInteraction) & Pick; - }; - - -} diff --git a/app/artifacts/private_voting-EasyPrivateVoting.json b/app/artifacts/private_voting-EasyPrivateVoting.json deleted file mode 100644 index c09592f..0000000 --- a/app/artifacts/private_voting-EasyPrivateVoting.json +++ /dev/null @@ -1,2983 +0,0 @@ -{ - "transpiled": true, - "noir_version": "1.0.0-beta.5+0000000000000000000000000000000000000000", - "name": "EasyPrivateVoting", - "functions": [ - { - "name": "add_to_tally_public", - "is_unconstrained": true, - "custom_attributes": [ - "public", - "internal" - ], - "abi": { - "parameters": [ - { - "name": "candidate", - "type": { - "kind": "field" - }, - "visibility": "private" - } - ], - "return_type": null, - "error_types": { - "206160798890201757": { - "error_kind": "string", - "string": "Storage slot 0 not allowed. Storage slots must start from 1." - }, - "2024020833944022298": { - "error_kind": "string", - "string": "Function add_to_tally_public can only be called internally" - }, - "3557153117338734214": { - "error_kind": "string", - "string": "Vote has ended" - }, - "5019202896831570965": { - "error_kind": "string", - "string": "attempt to add with overflow" - }, - "13699457482007836410": { - "error_kind": "string", - "string": "Not initialized" - }, - "14225679739041873922": { - "error_kind": "string", - "string": "Index out of bounds" - }, - "17843811134343075018": { - "error_kind": "string", - "string": "Stack too deep" - } - } - }, - "bytecode": "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", - "debug_symbols": "tZnbbuM4DIbfJde5kChSh77KYFCkbToIEKRFpl1gUfTdl5RIOilgo+Ps3NSfaemveNDBzsfmaf/w/uv+cHp++b25+/GxeTgfjsfDr/vjy+Pu7fByYuvHJsifGMvmLm43EaJe87gm0Kveoz5HbU96T/o8632mzR3xtbB8lSvfR3lQEwMwtGggXbktBLFUAbE0hhgNmgLg5g5QoCkkVobMgMHALCSWIlAVclYorAzyv0pWqGapZNAUWjIoA1KIBmSgjVNEA7NAMrDuybon6564cUoMMvjEfiUZ/ACxcHxSDgZoUBUKGGSFao2rNa7WWNwZoBYMYKDdMQYDNODGGLYbAh4PZoGmkNDALGgWNAslA7NkayM1MsCUJRfIacoysAFZIdqjaBYwC5glBQOzoLWRuu1Apiy10SEnA2nDLmcpkgFZQaI6AA2agkR1gHYvffAdqkI0S7Q2UhtYBaqCzLgBWUEKewAaWGOyxlQUslmyde9edCCDplDtv1fr3qxx08Y1BAO3cHfiSqiRuxNP6iopGNAUpCQG8FCJp3nFZFAUZO0YQAZNIVvjbN2LWYp1lyKhJNAUJAXE9dNkeg5oChLwDhJeKgIcH2J3mkS1gwRzQFGQ1WyAjJDXn1a5V2bfWwsGaFAHxCCJHxSDkz+VlOfYqRnJYAZJpSoVIxmYkttkaBk6NaOCRhWcilHzHpJbJesbpUZzEpKsKhUjSbASOTWj5D2Sq6Db0FXIVchVJOUZhSTGg2Q1UypGUqpK1Uh2jk4QLM4Q0MkiDtFtMpUy9f1NxpI7kVMz6pEcVEe9RJARKBWjBk5mSwGc3BbdFt0GycltsjEokVMzQu+B3qPXBnWSaHRbH/2gqn6kHr9BbpPRK0lf2adRpo6S23oVt07k1IzAlBEsftjruRNGp2JE4OQ2zwd6PtDzgcX/R6/s/Pm53dgp5v7tvN/LIebiWMOHndfdeX9629yd3o/H7eaf3fG9N/r9ujv169vuzE+5KvanJ76y4PPhuBf63E69w3zXUipo78qzwQUafVdBcmsDkEzSnAYsaASwUfCScTEKuFJI8wrYQlEFbDG7Qr6OBM4rVJR9YUQCM84pLHrRmnnB83rOi3yzF+XvekHVc1Fnc9EWFKiYFzGnNDeGuFBUjeTU2CV4Iys3uhFh1o2lwgZeZkwC+OVgrrDjQl1SqDY5GOtsLJYKk+TFYqSUUpiTWPYkw+RJnZ3m8fbajLcX5/dzUnHNglWilycfS12BvtTWkkRE8uIqk0Rkp6404sLKW5NVRuEUz5b4Qo3zOdkWHD4pzxYXpEVPWnJXCGbzuuwKTK7keQlamCfFCpTqtHKmlFdl9SKc9P3amhLChZVvVcgwW1nt9spK4ebKSvHmykrwP1TWtwNaV6Wk+HpRG96o0OJsYaWFpVMOpCaBUOaTurR2wnRQS2F27ZTVcVYiVN9I+FvYrMRiefLXDE9qxrQmqSmjRyNOnvD71PVZLS4dMbJKQJjmWYr1WmFp7SzoFV7aFAyoX06MC4tnIosFXmyI/B53rbCwt/M3QasMgNDmNZaWTp+ql6eDP1JotnTmSOv84O+M5ke6yMgfaZToSS1LnrS/qxFz9kUnl7pOoxRf+0qDdRr84ccXHpjXWJpq6GmJdLGlfZ1qtJCWXHzxKpCnCd+u5xrR0m7iChfx/Lr6hW++H2ZaFYo4bSUclnUS0yk61nWjSD7VGNeNgvywxTkt6yTyVBa1rXNkKs4E6xxJiNN3g1WOFLSEFMxrBJqfthqtcmJ6KWqwbgReU63QjS58FfjJt7vHw/nqt7JPkTofdg/Hvd4+v58eL56+/ftqT+y3ttfzy+P+6f28F6XpBzf+84N/RdomgJ/yIZZvK25rk5soz/jNIwH9/JSh/Ac=", - "brillig_names": [ - "add_to_tally_public" - ] - }, - { - "name": "cast_vote", - "is_unconstrained": false, - "custom_attributes": [ - "private" - ], - "abi": { - "parameters": [ - { - "name": "inputs", - "type": { - "kind": "struct", - "path": "aztec::context::inputs::private_context_inputs::PrivateContextInputs", - "fields": [ - { - "name": "call_context", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::call_context::CallContext", - "fields": [ - { - "name": "msg_sender", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::address::aztec_address::AztecAddress", - "fields": [ - { - "name": "inner", - "type": { - "kind": "field" - } - } - ] - } - }, - { - "name": "contract_address", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::address::aztec_address::AztecAddress", - "fields": [ - { - "name": "inner", - "type": { - "kind": "field" - } - } - ] - } - }, - { - "name": "function_selector", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::function_selector::FunctionSelector", - "fields": [ - { - "name": "inner", - "type": { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - } - ] - } - }, - { - "name": "is_static_call", - "type": { - "kind": "boolean" - } - } - ] - } - }, - { - "name": "historical_header", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::block_header::BlockHeader", - "fields": [ - { - "name": "last_archive", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::append_only_tree_snapshot::AppendOnlyTreeSnapshot", - "fields": [ - { - "name": "root", - "type": { - "kind": "field" - } - }, - { - "name": "next_available_leaf_index", - "type": { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - } - ] - } - }, - { - "name": "content_commitment", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::content_commitment::ContentCommitment", - "fields": [ - { - "name": "num_txs", - "type": { - "kind": "field" - } - }, - { - "name": "blobs_hash", - "type": { - "kind": "field" - } - }, - { - "name": "in_hash", - "type": { - "kind": "field" - } - }, - { - "name": "out_hash", - "type": { - "kind": "field" - } - } - ] - } - }, - { - "name": "state", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::state_reference::StateReference", - "fields": [ - { - "name": "l1_to_l2_message_tree", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::append_only_tree_snapshot::AppendOnlyTreeSnapshot", - "fields": [ - { - "name": "root", - "type": { - "kind": "field" - } - }, - { - "name": "next_available_leaf_index", - "type": { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - } - ] - } - }, - { - "name": "partial", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::partial_state_reference::PartialStateReference", - "fields": [ - { - "name": "note_hash_tree", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::append_only_tree_snapshot::AppendOnlyTreeSnapshot", - "fields": [ - { - "name": "root", - "type": { - "kind": "field" - } - }, - { - "name": "next_available_leaf_index", - "type": { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - } - ] - } - }, - { - "name": "nullifier_tree", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::append_only_tree_snapshot::AppendOnlyTreeSnapshot", - "fields": [ - { - "name": "root", - "type": { - "kind": "field" - } - }, - { - "name": "next_available_leaf_index", - "type": { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - } - ] - } - }, - { - "name": "public_data_tree", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::append_only_tree_snapshot::AppendOnlyTreeSnapshot", - "fields": [ - { - "name": "root", - "type": { - "kind": "field" - } - }, - { - "name": "next_available_leaf_index", - "type": { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - } - ] - } - } - ] - } - } - ] - } - }, - { - "name": "global_variables", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::global_variables::GlobalVariables", - "fields": [ - { - "name": "chain_id", - "type": { - "kind": "field" - } - }, - { - "name": "version", - "type": { - "kind": "field" - } - }, - { - "name": "block_number", - "type": { - "kind": "field" - } - }, - { - "name": "slot_number", - "type": { - "kind": "field" - } - }, - { - "name": "timestamp", - "type": { - "kind": "integer", - "sign": "unsigned", - "width": 64 - } - }, - { - "name": "coinbase", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::address::eth_address::EthAddress", - "fields": [ - { - "name": "inner", - "type": { - "kind": "field" - } - } - ] - } - }, - { - "name": "fee_recipient", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::address::aztec_address::AztecAddress", - "fields": [ - { - "name": "inner", - "type": { - "kind": "field" - } - } - ] - } - }, - { - "name": "gas_fees", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::gas_fees::GasFees", - "fields": [ - { - "name": "fee_per_da_gas", - "type": { - "kind": "field" - } - }, - { - "name": "fee_per_l2_gas", - "type": { - "kind": "field" - } - } - ] - } - } - ] - } - }, - { - "name": "total_fees", - "type": { - "kind": "field" - } - }, - { - "name": "total_mana_used", - "type": { - "kind": "field" - } - } - ] - } - }, - { - "name": "tx_context", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::transaction::tx_context::TxContext", - "fields": [ - { - "name": "chain_id", - "type": { - "kind": "field" - } - }, - { - "name": "version", - "type": { - "kind": "field" - } - }, - { - "name": "gas_settings", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::gas_settings::GasSettings", - "fields": [ - { - "name": "gas_limits", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::gas::Gas", - "fields": [ - { - "name": "da_gas", - "type": { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - }, - { - "name": "l2_gas", - "type": { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - } - ] - } - }, - { - "name": "teardown_gas_limits", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::gas::Gas", - "fields": [ - { - "name": "da_gas", - "type": { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - }, - { - "name": "l2_gas", - "type": { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - } - ] - } - }, - { - "name": "max_fees_per_gas", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::gas_fees::GasFees", - "fields": [ - { - "name": "fee_per_da_gas", - "type": { - "kind": "field" - } - }, - { - "name": "fee_per_l2_gas", - "type": { - "kind": "field" - } - } - ] - } - }, - { - "name": "max_priority_fees_per_gas", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::gas_fees::GasFees", - "fields": [ - { - "name": "fee_per_da_gas", - "type": { - "kind": "field" - } - }, - { - "name": "fee_per_l2_gas", - "type": { - "kind": "field" - } - } - ] - } - } - ] - } - } - ] - } - }, - { - "name": "start_side_effect_counter", - "type": { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - } - ] - }, - "visibility": "private" - }, - { - "name": "candidate", - "type": { - "kind": "field" - }, - "visibility": "private" - } - ], - "return_type": { - "abi_type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::private_circuit_public_inputs::PrivateCircuitPublicInputs", - "fields": [ - { - "name": "call_context", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::call_context::CallContext", - "fields": [ - { - "name": "msg_sender", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::address::aztec_address::AztecAddress", - "fields": [ - { - "name": "inner", - "type": { - "kind": "field" - } - } - ] - } - }, - { - "name": "contract_address", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::address::aztec_address::AztecAddress", - "fields": [ - { - "name": "inner", - "type": { - "kind": "field" - } - } - ] - } - }, - { - "name": "function_selector", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::function_selector::FunctionSelector", - "fields": [ - { - "name": "inner", - "type": { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - } - ] - } - }, - { - "name": "is_static_call", - "type": { - "kind": "boolean" - } - } - ] - } - }, - { - "name": "args_hash", - "type": { - "kind": "field" - } - }, - { - "name": "returns_hash", - "type": { - "kind": "field" - } - }, - { - "name": "min_revertible_side_effect_counter", - "type": { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - }, - { - "name": "is_fee_payer", - "type": { - "kind": "boolean" - } - }, - { - "name": "max_block_number", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::max_block_number::MaxBlockNumber", - "fields": [ - { - "name": "_opt", - "type": { - "kind": "struct", - "path": "std::option::Option", - "fields": [ - { - "name": "_is_some", - "type": { - "kind": "boolean" - } - }, - { - "name": "_value", - "type": { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - } - ] - } - } - ] - } - }, - { - "name": "note_hash_read_requests", - "type": { - "kind": "array", - "length": 16, - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::read_request::ReadRequest", - "fields": [ - { - "name": "value", - "type": { - "kind": "field" - } - }, - { - "name": "counter", - "type": { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - } - ] - } - } - }, - { - "name": "nullifier_read_requests", - "type": { - "kind": "array", - "length": 16, - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::read_request::ReadRequest", - "fields": [ - { - "name": "value", - "type": { - "kind": "field" - } - }, - { - "name": "counter", - "type": { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - } - ] - } - } - }, - { - "name": "key_validation_requests_and_generators", - "type": { - "kind": "array", - "length": 16, - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::validation_requests::key_validation_request_and_generator::KeyValidationRequestAndGenerator", - "fields": [ - { - "name": "request", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::validation_requests::key_validation_request::KeyValidationRequest", - "fields": [ - { - "name": "pk_m", - "type": { - "kind": "struct", - "path": "std::embedded_curve_ops::EmbeddedCurvePoint", - "fields": [ - { - "name": "x", - "type": { - "kind": "field" - } - }, - { - "name": "y", - "type": { - "kind": "field" - } - }, - { - "name": "is_infinite", - "type": { - "kind": "boolean" - } - } - ] - } - }, - { - "name": "sk_app", - "type": { - "kind": "field" - } - } - ] - } - }, - { - "name": "sk_app_generator", - "type": { - "kind": "field" - } - } - ] - } - } - }, - { - "name": "note_hashes", - "type": { - "kind": "array", - "length": 16, - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::note_hash::NoteHash", - "fields": [ - { - "name": "value", - "type": { - "kind": "field" - } - }, - { - "name": "counter", - "type": { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - } - ] - } - } - }, - { - "name": "nullifiers", - "type": { - "kind": "array", - "length": 16, - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::nullifier::Nullifier", - "fields": [ - { - "name": "value", - "type": { - "kind": "field" - } - }, - { - "name": "counter", - "type": { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - }, - { - "name": "note_hash", - "type": { - "kind": "field" - } - } - ] - } - } - }, - { - "name": "private_call_requests", - "type": { - "kind": "array", - "length": 5, - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::private_call_request::PrivateCallRequest", - "fields": [ - { - "name": "call_context", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::call_context::CallContext", - "fields": [ - { - "name": "msg_sender", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::address::aztec_address::AztecAddress", - "fields": [ - { - "name": "inner", - "type": { - "kind": "field" - } - } - ] - } - }, - { - "name": "contract_address", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::address::aztec_address::AztecAddress", - "fields": [ - { - "name": "inner", - "type": { - "kind": "field" - } - } - ] - } - }, - { - "name": "function_selector", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::function_selector::FunctionSelector", - "fields": [ - { - "name": "inner", - "type": { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - } - ] - } - }, - { - "name": "is_static_call", - "type": { - "kind": "boolean" - } - } - ] - } - }, - { - "name": "args_hash", - "type": { - "kind": "field" - } - }, - { - "name": "returns_hash", - "type": { - "kind": "field" - } - }, - { - "name": "start_side_effect_counter", - "type": { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - }, - { - "name": "end_side_effect_counter", - "type": { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - } - ] - } - } - }, - { - "name": "public_call_requests", - "type": { - "kind": "array", - "length": 16, - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::side_effect::counted::Counted", - "fields": [ - { - "name": "inner", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::public_call_request::PublicCallRequest", - "fields": [ - { - "name": "msg_sender", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::address::aztec_address::AztecAddress", - "fields": [ - { - "name": "inner", - "type": { - "kind": "field" - } - } - ] - } - }, - { - "name": "contract_address", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::address::aztec_address::AztecAddress", - "fields": [ - { - "name": "inner", - "type": { - "kind": "field" - } - } - ] - } - }, - { - "name": "is_static_call", - "type": { - "kind": "boolean" - } - }, - { - "name": "calldata_hash", - "type": { - "kind": "field" - } - } - ] - } - }, - { - "name": "counter", - "type": { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - } - ] - } - } - }, - { - "name": "public_teardown_call_request", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::public_call_request::PublicCallRequest", - "fields": [ - { - "name": "msg_sender", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::address::aztec_address::AztecAddress", - "fields": [ - { - "name": "inner", - "type": { - "kind": "field" - } - } - ] - } - }, - { - "name": "contract_address", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::address::aztec_address::AztecAddress", - "fields": [ - { - "name": "inner", - "type": { - "kind": "field" - } - } - ] - } - }, - { - "name": "is_static_call", - "type": { - "kind": "boolean" - } - }, - { - "name": "calldata_hash", - "type": { - "kind": "field" - } - } - ] - } - }, - { - "name": "l2_to_l1_msgs", - "type": { - "kind": "array", - "length": 2, - "type": { - "kind": "struct", - "path": "aztec::protocol_types::messaging::l2_to_l1_message::L2ToL1Message", - "fields": [ - { - "name": "recipient", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::address::eth_address::EthAddress", - "fields": [ - { - "name": "inner", - "type": { - "kind": "field" - } - } - ] - } - }, - { - "name": "content", - "type": { - "kind": "field" - } - }, - { - "name": "counter", - "type": { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - } - ] - } - } - }, - { - "name": "private_logs", - "type": { - "kind": "array", - "length": 16, - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::private_log::PrivateLogData", - "fields": [ - { - "name": "log", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::log::Log", - "fields": [ - { - "name": "fields", - "type": { - "kind": "array", - "length": 18, - "type": { - "kind": "field" - } - } - }, - { - "name": "length", - "type": { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - } - ] - } - }, - { - "name": "note_hash_counter", - "type": { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - }, - { - "name": "counter", - "type": { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - } - ] - } - } - }, - { - "name": "contract_class_logs_hashes", - "type": { - "kind": "array", - "length": 1, - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::side_effect::counted::Counted", - "fields": [ - { - "name": "inner", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::log_hash::LogHash", - "fields": [ - { - "name": "value", - "type": { - "kind": "field" - } - }, - { - "name": "length", - "type": { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - } - ] - } - }, - { - "name": "counter", - "type": { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - } - ] - } - } - }, - { - "name": "start_side_effect_counter", - "type": { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - }, - { - "name": "end_side_effect_counter", - "type": { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - }, - { - "name": "historical_header", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::block_header::BlockHeader", - "fields": [ - { - "name": "last_archive", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::append_only_tree_snapshot::AppendOnlyTreeSnapshot", - "fields": [ - { - "name": "root", - "type": { - "kind": "field" - } - }, - { - "name": "next_available_leaf_index", - "type": { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - } - ] - } - }, - { - "name": "content_commitment", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::content_commitment::ContentCommitment", - "fields": [ - { - "name": "num_txs", - "type": { - "kind": "field" - } - }, - { - "name": "blobs_hash", - "type": { - "kind": "field" - } - }, - { - "name": "in_hash", - "type": { - "kind": "field" - } - }, - { - "name": "out_hash", - "type": { - "kind": "field" - } - } - ] - } - }, - { - "name": "state", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::state_reference::StateReference", - "fields": [ - { - "name": "l1_to_l2_message_tree", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::append_only_tree_snapshot::AppendOnlyTreeSnapshot", - "fields": [ - { - "name": "root", - "type": { - "kind": "field" - } - }, - { - "name": "next_available_leaf_index", - "type": { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - } - ] - } - }, - { - "name": "partial", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::partial_state_reference::PartialStateReference", - "fields": [ - { - "name": "note_hash_tree", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::append_only_tree_snapshot::AppendOnlyTreeSnapshot", - "fields": [ - { - "name": "root", - "type": { - "kind": "field" - } - }, - { - "name": "next_available_leaf_index", - "type": { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - } - ] - } - }, - { - "name": "nullifier_tree", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::append_only_tree_snapshot::AppendOnlyTreeSnapshot", - "fields": [ - { - "name": "root", - "type": { - "kind": "field" - } - }, - { - "name": "next_available_leaf_index", - "type": { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - } - ] - } - }, - { - "name": "public_data_tree", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::append_only_tree_snapshot::AppendOnlyTreeSnapshot", - "fields": [ - { - "name": "root", - "type": { - "kind": "field" - } - }, - { - "name": "next_available_leaf_index", - "type": { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - } - ] - } - } - ] - } - } - ] - } - }, - { - "name": "global_variables", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::global_variables::GlobalVariables", - "fields": [ - { - "name": "chain_id", - "type": { - "kind": "field" - } - }, - { - "name": "version", - "type": { - "kind": "field" - } - }, - { - "name": "block_number", - "type": { - "kind": "field" - } - }, - { - "name": "slot_number", - "type": { - "kind": "field" - } - }, - { - "name": "timestamp", - "type": { - "kind": "integer", - "sign": "unsigned", - "width": 64 - } - }, - { - "name": "coinbase", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::address::eth_address::EthAddress", - "fields": [ - { - "name": "inner", - "type": { - "kind": "field" - } - } - ] - } - }, - { - "name": "fee_recipient", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::address::aztec_address::AztecAddress", - "fields": [ - { - "name": "inner", - "type": { - "kind": "field" - } - } - ] - } - }, - { - "name": "gas_fees", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::gas_fees::GasFees", - "fields": [ - { - "name": "fee_per_da_gas", - "type": { - "kind": "field" - } - }, - { - "name": "fee_per_l2_gas", - "type": { - "kind": "field" - } - } - ] - } - } - ] - } - }, - { - "name": "total_fees", - "type": { - "kind": "field" - } - }, - { - "name": "total_mana_used", - "type": { - "kind": "field" - } - } - ] - } - }, - { - "name": "tx_context", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::transaction::tx_context::TxContext", - "fields": [ - { - "name": "chain_id", - "type": { - "kind": "field" - } - }, - { - "name": "version", - "type": { - "kind": "field" - } - }, - { - "name": "gas_settings", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::gas_settings::GasSettings", - "fields": [ - { - "name": "gas_limits", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::gas::Gas", - "fields": [ - { - "name": "da_gas", - "type": { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - }, - { - "name": "l2_gas", - "type": { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - } - ] - } - }, - { - "name": "teardown_gas_limits", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::gas::Gas", - "fields": [ - { - "name": "da_gas", - "type": { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - }, - { - "name": "l2_gas", - "type": { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - } - ] - } - }, - { - "name": "max_fees_per_gas", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::gas_fees::GasFees", - "fields": [ - { - "name": "fee_per_da_gas", - "type": { - "kind": "field" - } - }, - { - "name": "fee_per_l2_gas", - "type": { - "kind": "field" - } - } - ] - } - }, - { - "name": "max_priority_fees_per_gas", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::abis::gas_fees::GasFees", - "fields": [ - { - "name": "fee_per_da_gas", - "type": { - "kind": "field" - } - }, - { - "name": "fee_per_l2_gas", - "type": { - "kind": "field" - } - } - ] - } - } - ] - } - } - ] - } - } - ] - }, - "visibility": "databus" - }, - "error_types": { - "576755928210959028": { - "error_kind": "string", - "string": "0 has a square root; you cannot claim it is not square" - }, - "855401245733623969": { - "error_kind": "string", - "string": "Obtained invalid key validation request" - }, - "2709101749560550278": { - "error_kind": "string", - "string": "Cannot serialize point at infinity as bytes." - }, - "2896122431943215824": { - "error_kind": "fmtstring", - "length": 144, - "item_types": [ - { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - ] - }, - "2920182694213909827": { - "error_kind": "string", - "string": "attempt to subtract with overflow" - }, - "3305101268118424981": { - "error_kind": "string", - "string": "Attempted to delete past the length of a CapsuleArray" - }, - "3367683922240523006": { - "error_kind": "fmtstring", - "length": 58, - "item_types": [ - { - "kind": "field" - } - ] - }, - "5019202896831570965": { - "error_kind": "string", - "string": "attempt to add with overflow" - }, - "5727012404371710682": { - "error_kind": "string", - "string": "push out of bounds" - }, - "5870202753060865374": { - "error_kind": "fmtstring", - "length": 61, - "item_types": [ - { - "kind": "field" - }, - { - "kind": "field" - } - ] - }, - "6336853191198150230": { - "error_kind": "fmtstring", - "length": 77, - "item_types": [ - { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - ] - }, - "6485997221020871071": { - "error_kind": "string", - "string": "call to assert_max_bit_size" - }, - "6753155520859132764": { - "error_kind": "string", - "string": "Failed to deliver note" - }, - "7233212735005103307": { - "error_kind": "string", - "string": "attempt to multiply with overflow" - }, - "8270195893599566439": { - "error_kind": "string", - "string": "Invalid public keys hint for address" - }, - "8830323656616886390": { - "error_kind": "string", - "string": "Got a public log emitted by a different contract" - }, - "12822839658937144934": { - "error_kind": "fmtstring", - "length": 75, - "item_types": [] - }, - "13649294680379557736": { - "error_kind": "string", - "string": "extend_from_bounded_vec out of bounds" - }, - "14225679739041873922": { - "error_kind": "string", - "string": "Index out of bounds" - }, - "14514982005979867414": { - "error_kind": "string", - "string": "attempt to bit-shift with overflow" - }, - "14657895983200220173": { - "error_kind": "string", - "string": "Attempted to read past the length of a CapsuleArray" - }, - "15366650908120444287": { - "error_kind": "fmtstring", - "length": 48, - "item_types": [ - { - "kind": "field" - }, - { - "kind": "field" - } - ] - }, - "16218014537381711836": { - "error_kind": "string", - "string": "Value does not fit in field" - }, - "16446004518090376065": { - "error_kind": "string", - "string": "Input length must be a multiple of 32" - }, - "16954218183513903507": { - "error_kind": "string", - "string": "Attempted to read past end of BoundedVec" - }, - "17843811134343075018": { - "error_kind": "string", - "string": "Stack too deep" - }, - "17879506016437779469": { - "error_kind": "fmtstring", - "length": 128, - "item_types": [ - { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - ] - }, - "18194595712952743247": { - "error_kind": "fmtstring", - "length": 98, - "item_types": [ - { - "kind": "integer", - "sign": "unsigned", - "width": 32 - }, - { - "kind": "integer", - "sign": "unsigned", - "width": 32 - }, - { - "kind": "field" - } - ] - } - } - }, - "bytecode": "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", - "debug_symbols": "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WHpYelh6WHh4UdFJklSSalJL6kmaNJJmUnpIekh6SHpIekh6SHpIekh6SHpIepT0KOlR0qOkR0mPkh4lPUp6lPQo6VHTo6ZHTY+aHjU9anrU9KjpUdOjpkdLj5YeLT1aerT0aOnR0qOlR0uPlh49PXp69PTo6dHTo6dHT4+eHj09enpoemh6aHpoemh6aHpoemh6eJxXJwvyOD9IkkpSTWpJPUmTRlJ6jPSY6THTY6bHTI+ZHjM9ZnrM9JjpMdPD0sPSw9LD0sPSw9LD0sPSw9LDwsMLl06SpJJUk1pST9KkkTST0kPSQ9JD0kPSQ9JD0kPSQ9JD0kPSo6RHSY+SHiU9SnqU9CjpUdKjpEdJj5oeNT1qetT0qOlR06OmR02Pmh41PVp6tPRo6eFxPpxaUk96esyH00iaSRa04vwkSSpJNakl9aT06OnR06Onh6aHpoemh6aHpoemh6aHpoemh6bHSI+RHiM9RnqM9BjpMdJjpMdIj5EeMz1mesz0mOkx02Omx0yPmR4zPWZ6WHpYelh6WHpYelh6WHpYelh6WHh4cdRJklSSalJL6kmaNJJmUnpIekh6SHpIekh6SHpIekh6SHpIepT0KOlR0qOkR0mPkh4lPUp6lPQo6VHTo6ZHTY+aHjU9anrU9KjpUdOjpkdLj5YeLT1aerT0aOmRcd4zznvGec847xnnPeO8Z5z3jPOecd4zznvGec847xnnPeO8Z5z3jPOecd4zznvGec847xnnPeO8Z5z3jPOecd4zznvGec847xnnPeO8Z5z3jPOecd4zznvGec847xnnPeO8Z5z3jPOecd4zznvGec847xnnPeO8Z5z3jPOecd4zznvGec847xnnPeNcM84141wzzjXjXDPONeNcM84141wzzjXjXDPONeNcM84141wzzjXjXDPONeNcM84141wzzjXjXDPONeNcM84141wzzjXjXDPONeNcM84141wzzjXjXDPONeNcM84141wzzjXjXDPONeNcM84141wzzjXjXDPONeNcM84141wzzjXjXDPONeNcM84141wzzjXjXDPONeNcM84141wzzjXjXDPONeNcM84141wzzjXjXDPONeNcM84141wzzjXjXDPONeNcM84141wzzjXjXDPONeNcM84141wzzjXjXDPONeNcM84141wzzjXjXDPONeNcM84141wzzjXj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", - "brillig_names": [ - "discover_new_messages", - "get_public_keys_and_partial_address", - "decompose_hint", - "lte_hint", - "get_key_validation_request", - "notify_created_nullifier_oracle_wrapper", - "store_in_execution_cache_oracle_wrapper", - "notify_enqueued_public_function_call_wrapper", - "directive_invert" - ], - "verification_key": "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\nPa5YgJ+q7saoanj0s7xh8Z1ucGk1m79H5/kH" - }, - { - "name": "constructor", - "is_unconstrained": true, - "custom_attributes": [ - "public", - "initializer" - ], - "abi": { - "parameters": [ - { - "name": "admin", - "type": { - "kind": "struct", - "path": "aztec::protocol_types::address::aztec_address::AztecAddress", - "fields": [ - { - "name": "inner", - "type": { - "kind": "field" - } - } - ] - }, - "visibility": "private" - } - ], - "return_type": null, - "error_types": { - "2233873454491509486": { - "error_kind": "string", - "string": "Initializer address is not the contract deployer" - }, - "2236649814169388962": { - "error_kind": "string", - "string": "PublicImmutable already initialized" - }, - "5019202896831570965": { - "error_kind": "string", - "string": "attempt to add with overflow" - }, - "14225679739041873922": { - "error_kind": "string", - "string": "Index out of bounds" - }, - "17618083556256589634": { - "error_kind": "string", - "string": "Initialization hash does not match" - }, - "17843811134343075018": { - "error_kind": "string", - "string": "Stack too deep" - } - } - }, - "bytecode": "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", - "debug_symbols": "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", - "brillig_names": [ - "constructor" - ] - }, - { - "name": "end_vote", - "is_unconstrained": true, - "custom_attributes": [ - "public" - ], - "abi": { - "parameters": [], - "return_type": null, - "error_types": { - "8095637994846897154": { - "error_kind": "string", - "string": "Only admin can end votes" - }, - "13699457482007836410": { - "error_kind": "string", - "string": "Not initialized" - }, - "17843811134343075018": { - "error_kind": "string", - "string": "Stack too deep" - } - } - }, - "bytecode": "JwACBAEoAAABBIBEJwAABAMnAgEEACcCAgQAHwoAAQACgEQlAAAAPyUAAABAKAIAAQSARCcCAgQAOw4AAgABJiUAAACXHgIAAQAeAgACADMqAAEAAgADJwIBAQEkAgADAAAAaSUAAADAJwIBAAEvCgABAAIeAgADAQoqAgMEJAIABAAAAIslAAAA0icCAgADMAoAAQACJigAgAQEeAANAAAAgASAAyQAgAMAAAC/KgEAAQX3ofOvpa3UyjwEAgEmKgEAAQW+Hj//PqT2+jwEAgEmKgEAAQVwWXvdIQFEAjwEAgEm", - "debug_symbols": "tZXdbrMwDIbvJcccxM6vuZWpqmhLJyREKwaf9Kni3udsJIVKycGmnTQONE/92m+dh7i0p/n92A3X24eo3x7iNHZ9370f+9u5mbrbwE8fQoYPS6LWlXBa1I4X3oGshHe8QiUIv1eQMgaGA8sByBhYDogDBFGjDIFdA6VjwFw0HOjwxCxLJWIqx2ls25DJJjfO+N6M7TCJepj7vhL/mn7++tLHvRm+1qkZ+S2z2+HCKwOvXd+GaKmep2X+KChF8Tgow0lFBJkdAwoMiRgRUuOTgDsC5gmapFsJmsAmgt3rUHmC19qvBK+tzhGKKoiiCgDKqTC/VmH/VoXxqRc+2wtfIBgXVYBVKpcD5QlklF4JxLBfqgDMqij5mnyyNZFPBPOSQ8GUANqkJNzzvwXcmB2jYEvnVUzDeQPZUugCwmL0pbPWZxGmqIRUkmIwa81CQVHK2FSUm5a8FtTlEUamnnCYV1IwJ9cw9sQbJX+gY2MM1mFyOoqVoJgDbgfFCwFL/tYSYj80uqy1sGBPjz5OXTZZdlZgaWhKn6oJKusK1CVrWZmGP6e/nxcH3jXnbtzduEugjV1z6tt1e52H8+bt9P8e38Qb+z7ezu1lHttAel7b4fZ9Q18pdQhXMG+UqxQdlvDTnw==", - "brillig_names": [ - "end_vote" - ] - }, - { - "name": "get_vote", - "is_unconstrained": true, - "custom_attributes": [ - "utility" - ], - "abi": { - "parameters": [ - { - "name": "candidate", - "type": { - "kind": "field" - }, - "visibility": "private" - } - ], - "return_type": { - "abi_type": { - "kind": "field" - }, - "visibility": "public" - }, - "error_types": { - "206160798890201757": { - "error_kind": "string", - "string": "Storage slot 0 not allowed. Storage slots must start from 1." - }, - "576755928210959028": { - "error_kind": "string", - "string": "0 has a square root; you cannot claim it is not square" - }, - "2709101749560550278": { - "error_kind": "string", - "string": "Cannot serialize point at infinity as bytes." - }, - "2896122431943215824": { - "error_kind": "fmtstring", - "length": 144, - "item_types": [ - { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - ] - }, - "2920182694213909827": { - "error_kind": "string", - "string": "attempt to subtract with overflow" - }, - "3305101268118424981": { - "error_kind": "string", - "string": "Attempted to delete past the length of a CapsuleArray" - }, - "3367683922240523006": { - "error_kind": "fmtstring", - "length": 58, - "item_types": [ - { - "kind": "field" - } - ] - }, - "5019202896831570965": { - "error_kind": "string", - "string": "attempt to add with overflow" - }, - "5727012404371710682": { - "error_kind": "string", - "string": "push out of bounds" - }, - "5870202753060865374": { - "error_kind": "fmtstring", - "length": 61, - "item_types": [ - { - "kind": "field" - }, - { - "kind": "field" - } - ] - }, - "6336853191198150230": { - "error_kind": "fmtstring", - "length": 77, - "item_types": [ - { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - ] - }, - "6485997221020871071": { - "error_kind": "string", - "string": "call to assert_max_bit_size" - }, - "6753155520859132764": { - "error_kind": "string", - "string": "Failed to deliver note" - }, - "7233212735005103307": { - "error_kind": "string", - "string": "attempt to multiply with overflow" - }, - "8270195893599566439": { - "error_kind": "string", - "string": "Invalid public keys hint for address" - }, - "8830323656616886390": { - "error_kind": "string", - "string": "Got a public log emitted by a different contract" - }, - "12822839658937144934": { - "error_kind": "fmtstring", - "length": 75, - "item_types": [] - }, - "13649294680379557736": { - "error_kind": "string", - "string": "extend_from_bounded_vec out of bounds" - }, - "14225679739041873922": { - "error_kind": "string", - "string": "Index out of bounds" - }, - "14514982005979867414": { - "error_kind": "string", - "string": "attempt to bit-shift with overflow" - }, - "14657895983200220173": { - "error_kind": "string", - "string": "Attempted to read past the length of a CapsuleArray" - }, - "15366650908120444287": { - "error_kind": "fmtstring", - "length": 48, - "item_types": [ - { - "kind": "field" - }, - { - "kind": "field" - } - ] - }, - "16218014537381711836": { - "error_kind": "string", - "string": "Value does not fit in field" - }, - "16446004518090376065": { - "error_kind": "string", - "string": "Input length must be a multiple of 32" - }, - "16954218183513903507": { - "error_kind": "string", - "string": "Attempted to read past end of BoundedVec" - }, - "17843811134343075018": { - "error_kind": "string", - "string": "Stack too deep" - }, - "17879506016437779469": { - "error_kind": "fmtstring", - "length": 128, - "item_types": [ - { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - ] - }, - "18194595712952743247": { - "error_kind": "fmtstring", - "length": 98, - "item_types": [ - { - "kind": "integer", - "sign": "unsigned", - "width": 32 - }, - { - "kind": "integer", - "sign": "unsigned", - "width": 32 - }, - { - "kind": "field" - } - ] - } - } - }, - "bytecode": "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", - "debug_symbols": "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fLYgKWpFWtSLrMiLRtEsWkmrHKscqxyrHKscqxyrHKscqxwrHTGp6CIpakVa1IusyItG0Swqh5RDyiHlkHJIOaQcUg4ph5RDytHK0crRytHK0crRytHK0crRytHKoeXQcmg5tBxaDi2HlkPLoeXQcvRy9HL0cvRy9HL0cvRy9HL0cvRyWDmsHFYOK4eVw8ph5bByWDmsHF4OL4eXw8vh5fByeDm8HF4OL8coR+S5BrUiLepFVuRFo2gWraTI85PKMcsxyzHLMcsxyzHLMcsxy7HKscqxyrHKscqxyrHKscqxyrHSEROXLpKiVqRFvciKvGgUzaJySDmkHFIOKYeUQ8oh5ZBySDmkHK0crRytHK0crRytHK0crRytHK0cWg4th5ZDy6Hl0HJoObQcWg4tRy9HL0cvRy9HL0cvR+T5CBpFs+juWMdvVEx1ukiKWpEW9SIr8qJRNIvK4eXwcng5vBxeDi+Hl8PL4eXwcoxyjHKMcoxyjHKMcoxyjHKMcoxyzHLMcsxyzHLMcsxyzHLMcsxyzHKscqxyrHKscqxyrHKscqxyrHKsdMTkqIukqBVpUS+yIi8aRbOoHFIOKYeUQ8oh5ZBySDmkHFIOKUcrRytHK0crRytHK0crRytHK0crh5ZDy6Hl0HJoObQcWg4th5ZDy9HL0cvRy9HL0cvRy9HL0cvRy1F5bpXnVnluledWeW6V51Z5bpXnVnluledWeW6V51Z5bpXnVnluledWeW6V51Z5bpXnVnluledWeW6V51Z5bpXnVnluledWeW6V51Z5bpXnVnluledWeW6V51Z5bpXnVnluledWeW6V51Z5bpXnVnluledWeW6V51Z5bpXnVnnuledeee6V51557pXnXnnuledeee6V51557pXnXnnuledeee6V51557pXnXnnuledeee6V51557pXnXnnuledeee6V51557pXnXnnuledeee6V51557pXnXnnuledeee6V51557pXnXnnuledeee6V51557pXnXnnuledeee6V51557pXnXnnuledeee6V51557pXnXnnuledeee6V51557pXnXnnuledeee6V51557pXnXnnuledeee6V51557pXnXnnuledeee6V51557pXnXnnuledeee6V51557pXnXnnuledeee6V51557pXnXnnuledeee6V5155PirPR+X5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", - "brillig_names": [ - "get_vote" - ] - }, - { - "name": "public_dispatch", - "is_unconstrained": true, - "custom_attributes": [ - "public" - ], - "abi": { - "parameters": [ - { - "name": "selector", - "type": { - "kind": "field" - }, - "visibility": "private" - } - ], - "return_type": null, - "error_types": { - "206160798890201757": { - "error_kind": "string", - "string": "Storage slot 0 not allowed. Storage slots must start from 1." - }, - "2024020833944022298": { - "error_kind": "string", - "string": "Function add_to_tally_public can only be called internally" - }, - "2233873454491509486": { - "error_kind": "string", - "string": "Initializer address is not the contract deployer" - }, - "2236649814169388962": { - "error_kind": "string", - "string": "PublicImmutable already initialized" - }, - "2830029349304997821": { - "error_kind": "fmtstring", - "length": 27, - "item_types": [ - { - "kind": "field" - } - ] - }, - "3557153117338734214": { - "error_kind": "string", - "string": "Vote has ended" - }, - "5019202896831570965": { - "error_kind": "string", - "string": "attempt to add with overflow" - }, - "8095637994846897154": { - "error_kind": "string", - "string": "Only admin can end votes" - }, - "13699457482007836410": { - "error_kind": "string", - "string": "Not initialized" - }, - "14225679739041873922": { - "error_kind": "string", - "string": "Index out of bounds" - }, - "17618083556256589634": { - "error_kind": "string", - "string": "Initialization hash does not match" - }, - "17843811134343075018": { - "error_kind": "string", - "string": "Stack too deep" - } - } - }, - "bytecode": "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", - "debug_symbols": "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", - "brillig_names": [ - "public_dispatch" - ] - }, - { - "name": "sync_private_state", - "is_unconstrained": true, - "custom_attributes": [ - "utility" - ], - "abi": { - "parameters": [], - "return_type": null, - "error_types": { - "576755928210959028": { - "error_kind": "string", - "string": "0 has a square root; you cannot claim it is not square" - }, - "2709101749560550278": { - "error_kind": "string", - "string": "Cannot serialize point at infinity as bytes." - }, - "2896122431943215824": { - "error_kind": "fmtstring", - "length": 144, - "item_types": [ - { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - ] - }, - "2920182694213909827": { - "error_kind": "string", - "string": "attempt to subtract with overflow" - }, - "3305101268118424981": { - "error_kind": "string", - "string": "Attempted to delete past the length of a CapsuleArray" - }, - "3367683922240523006": { - "error_kind": "fmtstring", - "length": 58, - "item_types": [ - { - "kind": "field" - } - ] - }, - "5019202896831570965": { - "error_kind": "string", - "string": "attempt to add with overflow" - }, - "5727012404371710682": { - "error_kind": "string", - "string": "push out of bounds" - }, - "5870202753060865374": { - "error_kind": "fmtstring", - "length": 61, - "item_types": [ - { - "kind": "field" - }, - { - "kind": "field" - } - ] - }, - "6336853191198150230": { - "error_kind": "fmtstring", - "length": 77, - "item_types": [ - { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - ] - }, - "6485997221020871071": { - "error_kind": "string", - "string": "call to assert_max_bit_size" - }, - "6753155520859132764": { - "error_kind": "string", - "string": "Failed to deliver note" - }, - "7233212735005103307": { - "error_kind": "string", - "string": "attempt to multiply with overflow" - }, - "8270195893599566439": { - "error_kind": "string", - "string": "Invalid public keys hint for address" - }, - "8830323656616886390": { - "error_kind": "string", - "string": "Got a public log emitted by a different contract" - }, - "12822839658937144934": { - "error_kind": "fmtstring", - "length": 75, - "item_types": [] - }, - "13649294680379557736": { - "error_kind": "string", - "string": "extend_from_bounded_vec out of bounds" - }, - "14225679739041873922": { - "error_kind": "string", - "string": "Index out of bounds" - }, - "14514982005979867414": { - "error_kind": "string", - "string": "attempt to bit-shift with overflow" - }, - "14657895983200220173": { - "error_kind": "string", - "string": "Attempted to read past the length of a CapsuleArray" - }, - "15366650908120444287": { - "error_kind": "fmtstring", - "length": 48, - "item_types": [ - { - "kind": "field" - }, - { - "kind": "field" - } - ] - }, - "16218014537381711836": { - "error_kind": "string", - "string": "Value does not fit in field" - }, - "16446004518090376065": { - "error_kind": "string", - "string": "Input length must be a multiple of 32" - }, - "16954218183513903507": { - "error_kind": "string", - "string": "Attempted to read past end of BoundedVec" - }, - "17843811134343075018": { - "error_kind": "string", - "string": "Stack too deep" - }, - "17879506016437779469": { - "error_kind": "fmtstring", - "length": 128, - "item_types": [ - { - "kind": "integer", - "sign": "unsigned", - "width": 32 - } - ] - }, - "18194595712952743247": { - "error_kind": "fmtstring", - "length": 98, - "item_types": [ - { - "kind": "integer", - "sign": "unsigned", - "width": 32 - }, - { - "kind": "integer", - "sign": "unsigned", - "width": 32 - }, - { - "kind": "field" - } - ] - } - } - }, - "bytecode": "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", - "debug_symbols": "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RjmOPJ8taBatpCPPL5KiVqRFvciKvKgcsxyzHKscqxyrHKscqxyrHKscqxyrHCsdManoIilqRVrUi6zIi0bRLCqHlEPKIeWQckg5pBxSDimHlEPK0crRytHK0crRytHK0crRytHK0cqh5dByaDm0HFoOLYeWQ8uh5dBy9HL0cvRy9HL0cvRy9HL0cvRy9HJYOawcVg4rh5XDymHlsHJYOawcXg4vh5fDy+Hl8HJ4ObwckecatJIiz0+SolakRb3IirxoFJVjlGOWY5ZjlmOWY5ZjlmOWY5ZjlmOWY5VjlWOVY5VjlWOVY5VjlWOVY6UjJi5dJEWtSIt6kRV50SiaReWQckg5pBxSDimHlEPKIeWQckg5WjlaOVo5WjlaOVo5WjlaOVo5Wjm0HFoOLYeWQ8uh5dByaDm0HFqOXo5ejl6OyPMR1Ius6O5Yt6BRNItW0pHnF0lRK9KiXmRF5bByWDmsHF4OL4eXw8vh5fByeDm8HF4OL8coxyjHKMcoxyjHKMcoxyjHKMcoxyzHLMcsxyzHLMcsxyzHLMcsxyzHKscqxyrHKscqxyrHKscqxyrHSkdMjrpIilqRFvUiK/KiUTSLyiHlkHJIOaQcUg4ph5RDyiHlkHK0crRytHK0crRytHK0crRytHK0cmg5tBxaDi2HlkPLoeXQcmg5tBy9HL0cvRy9HL0cvRyV51Z5bpXnVnluledWeW6V51Z5bpXnVnluledWeW6V51Z5bpXnVnluledWeW6V51Z5bpXnVnluledWeW6V51Z5bpXnVnluledWeW6V51Z5bpXnVnluledWeW6V51Z5bpXnVnluledWeW6V51Z5bpXnVnluledWeW6V51Z5bpXnVnluledWee6V51557pXnXnnuledeee6V51557pXnXnnuledeee6V51557pXnXnnuledeee6V51557pXnXnnuledeee6V51557pXnXnnuledeee6V51557pXnXnnuledeee6V51557pXnXnnuledeee6V51557pXnXnnuledeee6V51557pXnXnnuledeee6V51557pXnXnnuledeee6V51557pXnXnnuledeee6V51557pXnXnnuledeee6V51557pXnXnnuledeee6V51557pXnXnnuledeee6V51557pXnXnnuledeee6V51557pXnXnnuledeee6V51557pXnXnk+Ks9H5fmoPB+V56PyfFSej8rzUXk+Ks9H5fmoPB+V56PyfFSej8rzUXk+Ks9H5fmoPB+V56PyfFSej8rzUXk+Ks9H5fmoPB+V56PyfFSej8rzUXk+Ks9H5fmoPB+V56PyfFSej8rzmAu2jifzmAx2kRS1Ii3qRVbkRaNoFpXDymHlsHJEnvegXmRFXjSKZtFKijw/SYpaUTm8HF4OL4eXw8vh5RjlGOUY5RjlGOUY5RjlGOUY5RjlmOWY5ZjlmOWY5ZjlmOWY5ZjlmOVY5VjlWOVY5VjlWOVY5VjlWOVY6YiJZBdJUSvSol5kRV40imZROaQcUg4ph5Qj8jw+LI48P8mLRtEsWkmR5ydJUSvSonK0crRytHK0crRyaDm0HFoOLYeWQ8uh5dByaDm0HL0cvRy9HL0cvRy9HL0cvRy9HL0cVg4rh5XDymHlsHJYOawcVg4rh5fDy+Hl8HJ4OTwdMYfn+mb8sHnQEXkeFEd/khS1Ii3qRVbkRaNoFpXDy+Hl8HJ4ObwcXg4vh5fDy+HlGOUY5RjlGOUY5RjlGOUY5RjlGOWY5ZjlmOWY5ZjlmOWY5ZjlmOWY5VjlWOVY5VjlWOVY5VjlWOVY5ViXo8Usn4uk6HCsIC3qRVbkRaNoFq2ko0rJ7RYoB2pgAxXsoIEODnCCqzAW6rgQW8PWsDVssWTHrQc6OMAJrsJYvOPCsHlgAxXsoIEODnCCqzAW9LgQW0cRq3ncRuAEI0L8trFwx4UCNlDBI5jE73Yu4XGigwOc4Co8F/M4UcDDdiyT0W7nkh4ndjBs8bOcC3vE9T2X9ojTPBf3OHEVngt8nChgAyNutMlzWY8TR+G5JEcLHOAEV2EszXGhgA1UsIMGYlvYFrZVtpilkyhgAxXsoIEODnCC2ASbYBNsgk2wCTbBJtgEm2Br2Bq2hq1ha9gatoatYWvYGjbFptgUm2JTbIpNsSk2xabYOraOrWPr2Dq2jq1j69giC1sPHOAE4xiOZn8uqXOhgA1UsIMGOjjACWIb2CILY8mLc6mdCxXsoIEODnCCqzCW3rkQ27n8zghUsIMGOjjAw6YSuAoj5y8UsIEKdtBABweIbZUtZuwkChhxW6CBDg5wgqswsvtCARuoIDbBJtgEm2ATbA1bw9awNWwNW8PWsDVsDVvDptgUm2JTbIpNsSk2xabYFFvH1rF1bB1bx9axdWwdW8fWsRk2w2bYDJthM2yGzbAZNsPm2BybY3Nsjs2xOTbH5tgc28A2sA1sA9vANrANbAPbwDawTWwT28Q2sU1sE9vENrFNbBPbwrawLWwL28K2sC1sC9vCtsqmtxsoYAMV7KCBDg5wgtioJUotUWqJUkuUWqJnLdFABwc4wVV41pITo8SvQAU7aKCDA5zgKjwfD04UEJtiU2yKTbEpNsWm2Dq2jq1j69g6to6tY+vYOraOzbAZNsNm2AybYTNshs2wGTbH5tgcm2NzbI7NsTk2x+bYBraBbWAb2Aa2gW1gG9gGtoFtYpvYJraJbWKb2Ca2iW1im9gWtoVtYVvYFraFbWFb2Ba2VbZ+u4ECNlDBDhro4AAniE2wCTbBJtgEm2ATbIJNsAm2hq1ho5Z0akmnlvTzYcQDHRxglKsRuArPh5ETBWygglEcw3Y+jJzoYNhm4ARXYdSSY82UFjOQEht42LoGdvCw9R7o4AAPW4/TjFpyYtSSC8MWxxC15EIFO2iggxE3TjPqg90CjwgWhx714UIDHTyO1+KEoj5cuAqjPlwoYByvBSrYwbDFaUZ9uHCAYTv/7SqM+nChgA1UMM4tGkHUhwsdHOAEV2HUhwsFbGDY4lJHfbjQQAcHOMGVGBOSEgVsoIJh64EGOjjACa7CqA8XChi2FahgBw10cIATXIVRHy4UEFvD1rA1bA1bw9awNWyKTbEpNsWm2BSbYlNsik2xdWwdW8fWsXVsHVvH1rF1bB2bYTNshs2wGTbDZtgMm2EzbI7NsTk2x+bYHJtjc2yOzbENbAPbwDawDWwD28A2sA1sA9vENrFNbBPbxDaxTWwT28Q2sS1sC9vCtrAtbAvbwrawLWyrbH67gQI2UMEOGujgACeITbAJNmqJU0ucWuLUEqeWOLXEqSVOLXFqiVNLnFri1BKnlji1xKklTi1xaolTS5xa4tQSp5bErCg5Vp5qMS1KjmWgWsyLSnRwgBNchVFLLhSwgQpi69g6to6tY+vYopYcC+20mCeV2EAFO2hgxD1u2DEL6t63GdjAiDADO2iggwOc4CqM+nBh2OIHiPpwoYKHbcTPEvXhQgcHeNjG8bwTc6DunaqBDVSwgxE3rkNUgnGuzRxx45JEJRhxvOfK43FkUQlmiKMSXNhABQ/bjCOLSnChgwM8bMes+BZzoe59sYGh8MBQjMBQrMBDsVqggQ4OcIKrMNL/wsO24hgi/S/s2UpiPlSigwOc4CqMnL9QwAYqiK1hi5xf58rYA5xgnFD828j5CwVsoIIdNNDBAU4QW8cWOR+DuzFVKjFsM7CDYYtfM9Ywj4HgmBt1YaxjfqGA7UAJVLCDBkadPP9sgBNcheeTwokCNlDBDo5zvkSLeVH3IeDAVRgLm18oYAPjJKKZxQLnFxro4AAnuApjsfMLw9YDG6hg2OLQY9nzGBKOmVIthnljqlTiBFdhLIB+oYD9Wk0/5kZd5EWjaBati2K2Uoux3piulKhgBw10cIATXIWxV8CF2Bq2hq1ha9gatoatYWvYFJtiU2yKTbEpNsWm2BSbYuvYOraOrWPr2Dq2jq1j69g6NsNm2AybYTNshs2wGTbDZtgcm2NzbI7NsTk2x+bYHJtjG9gGtoFtYBvYBraBbWAb2Aa2iW1im9gmtoltYpvYJraJbWJb2Ba2hW1hW9gWtoVtYVvYVtliza5EARuoYAcNdHCAE8Qm2Kgli1qyqCWLWrKoJYtasqgli1qyqCWLWrKoJYtasqgli1qyqCWLWrKoJYtasqgli1qyqCWLWrKoJYtasqgli1qyqCWLWrKoJYtasqgli1qyqCWLWrKoJYtasqgli1qyqCWLWrKoJYtasqgli1qyqCWLWrKoJYtasqgli1qyqCWLWrKoJYtasqgli1qyqCWLWrKoJYtasqgli1qyqCWLWrKoJYtasqgli1qyqCWLWrKoJYtasqgli1qyqCWLWrKoJYtasqgli1qyqCWLWrKoJYtasqgli1qyqCWLWrKqluitaoneqpborWqJ3qqW6K1qid6qluitaoneqpborWqJ3m7YBJtgE2yCTbAJNsEm2ASbYGvYGraGrWFr2Bq2hq1ha9gaNsWm2BSbYlNsik2xKTbFptg6to6tY+vYOraOrWPr2Dq2js2wGTbDZtgMm2EzbIbNsBk2x+bYHJtjc2yOzbE5Nsfm2Aa2gW1gG9gGtoFtYBvYBraBbWKb2Ca2iW1im9gmtoltYpvYFraFbWFb2Ba2hW1hW9gWNmqJUEuEWiLUEqGWCLVEqCVCLRFqiVBLhFoi1BKhlgi1RKglQi0RaolQS4RaItQSoZYItUSoJUItEWqJUEuEWiLUEqGWCLVEqCVCLRFqiVBLhFoi1BKhlgi1RKglQi0RaolQS4RaItQSoZYItUSoJUItEWqJUEuEWiLUEqGWCLVEqCVCLRFqiVBLhFoi1BKhlgi1RKglQi0RaolQS4RaItQSoZYItUTOlNZAARsYih7YQQMdHOAEjxM69wa7FUlRqDxQwQ6GagY6GP0GcQpnx8GJK/Hat/BEARuoYAcNdHCAE8Qm2M7dDFtgAxXsoIEOHrYZNItWUiTzSVLUiiKiBsaR9sA40nPvtRsoYAPjSEdgBw10cIBhi2OI7DwxsvPCw6a3wAYqeNhif7lzf8MLD5vGCUV2XjjBde1KF/MeL5KiVqRFvSgixiWKXLv2oIsj9cAGKtjBONI4wci1Cwc4wVUYe6ud+9xJUSs6DjWOKvZXO8mKvGgUzaKQrAMjty8UsIPHYfa4+JGvFx4XNC5t7KZ2khQdV6TH1Yt8vbCDxxXpcSyRrxeG6tzHb4Ir8dzj8Jgnoucuh30Ehm0GHrZjEofGdMREAx0c4ARXYeTrhVGKWuBhOyZ8aExHbMccAo2Jh83iICMzLQ4yUvPCVRjJeaGADVQwgsVpnhuNnrgKz81GTxSwgQrGn8WFOncSPVHABsafrcDjSh6Dfaq5j5NqbuSkmjs5qeZWTqq5l5Nqbuakmrs5qeZ2Tqq5n5NqbuikauWwclg5rBxWDi+Hl8PL4eXwcng5vBxeDi9HPCT7iVp7Q/YiK/KiUTSLVm0neSuSolZUjtrZkK0N2duQzQ3Z3ZDtDdnfkA0O2eGQLQ7Z45BNDtnlMGbutWPIVGPmXmIDjxZyDExqzNxrx+Coxsy95meEo8Eeg40a8+7aMYCoMe+ujfi3cWe70MGjyR8LPmrMu0tchZE/FwrYQAU7GDYLdHCAcSONc4tUmnE4kUoXHnFn/Nu4611ooIODP5vgKowMvBBbxxYZeGEHHRznxmJ67mN40kqKxDtJilpRBPfADho4C+NWN+Maxq1uxm8et7oLO2iggwOc4CqMV9QZrSZeUS9s4GFb0ZbiFfVCAw/bihYWr6gXTnAVxj3vQgEbqGAHDcQ2sU1sE9vCtrAtbHGLXNHu4hZ5oYER9/jNY75cO8ZuNWbGJcbheGAczgic4CqMu9oxmqoxBy7xqA/H4KLGvDa9hS12+byFIvb5vHCCqzB2+7zFMcR+nxc2UMEOGuhgxI3jjf12LxQw4sahx667F3bQQAcHOMFVGLvp3lbgACe4CmNX3QsFPI7s+DZVY55YYgcNdHCAhy3evGKe2IXxNHihgGGL3y1uRvE+FjPCNF6sYkZY4gRXYdyRLhSwgXEW8RvHXelCA8MWv1vcmC6cYNji6sS96UIBG6hgBw108LC1uGaxF2+8Gfm527UGdtBAB+O94DhNP3e5PlHABirYQQMdjCOzwAmuwth190IBQ+GBHYxgR7OPKVgar0ox2UqPGeUak6003o9islXiPPdM1JhrddKRTBdJUSvSol5kRV4UEgmc4CqMl6wLBWyggh00MOLG7xm5Fe8VMccqHrJjitVFvciKvGgURcQ4/siqEyOrLhSwgQrGZY5gkT/xchdLSSUeN9o45tgm9yQt6kVW5EVxTeOXjcy5cBWe71knCtjAuHrRICIb4kUt1oqKJ/qYH3WRFB0XdARpUS+yIi8aRSFpgasw0uhCBeN1VAMHOMHjMI+LGLOeLpKiVqRFvSjeenuggwOc4CqMG9aFAjZQwQ5iU2yRd/FmGhOeEldh3MbiJTUmPCWGbQUetmN+kMaEJ423zZjwlOjgYYtcjGlQiYctGntMg9Lz6sTGZRE2di47SYt6kRV5UUSMXztua2ejidva+Q/itnahgceRxltTTHVKnOAqjAS8MOLGCUaqxWtGzF/SeLeI+UuJqzAS8EIBG6hgBw0MW1y4SMMLJxi2uJyRhhcK2MCwxTWLG9iFBh6XN04tdi07aRbdVXENzs0DT5KiVqRFvSgkM9DBAc7CuMddGIe5Ag08IsTbW8yPSpzgOvc409oyUGvPQK1NA7V2DdTaNlBr30CtjQO1dg7U2jpQa+9Arc0DtXYP1No+UGv/QK0NBLV2ENTaQlBrD0GtTQS1dhHU2kZQax9BjYlQekyT1ZgIldjB45INDXRwgHHJLHAVRoaOuP5xi7ywgQp2MGzxA8Wj6oWHbcavEjfOGUcW2TujZcSj6oUCHrZ44Y2JUIkdtHMjOT33HTxpFM2ilRR7D54UEXvgcaTxWhzTmjReK2NaU+IqjGy+MI40Tjuy+UIFO2jg3Xa20FxDXWeux6YxIylemmJC0kWjKLpT4uqtlRjTkRIFbKCCHTTQwQFOEJtgE2zxIBrvizEdKbGDBjo4wHVdg5iCdJEURXwNVLCDBjo4wDgbC1yFcZe9MM7GAxvYrx9p5TLpunKZdI0pR9H1EDOOLlpJcVNdJwrYQAU7aGCcygwc4ASPq3a0ppXrq+rK9VV15fqqunJ9VV25vqquXF9VV66vqivXV9WV66vqsnJ4ObwcXg4vh5fDy+Hl8HJ4ObwcoxzxxHtMt9aYWZSo4PHIejv/rYEODnCCq/BI50QBG6ggtoktHpFvkQNzgBNchesGCthABTsYtkiS5eAAj8sY7TEWO7tTPxc7O0mKWpEWRcQT40j7gRJHaoECNlDBONIRaKCDA5xg2NaB7QYK2EAFO2igg9FPLoHRUd4Cozc+jldvoIANVLCDBjo4wAli69g6to6tY+vYOraOrWPr2Do2w2bYDJthM2yGzbAZNsNm2BybY3Nsjs2xOTbH5tgcm2Mb2Aa2gW1gG9gGtoFtYBvYBraJbWKb2Ca2iW1im9gmtoktKsPRBdVjnlBi2CJFojJcqGAH4039FujgACe4EmOeUKKADYxeAQnsYCha4AAnuAqjgBydWD0mByU2UMGedUfOAnKigwOcYJUrOQvIiQI20M6Hrn5uf3jSKLoHHee/W0mxLdpJcfwnNlDBDhro4GGKSxibo520kqJCHB1sPWb/JDZQzx3Geu2D2GsfxF77IPbaB7HXPoi99kHstQ9ir30Qe+2D2GsfxF77IPbaB7HXPoi99kHstQ9iFyuHl8PL4eXwcng5ohYc/Yg95vkkDjCa1/lvV2HUggsFbKCCHTTQwbDNwAmuwthGKVpKbKN0UivSol5kRRHxuDPFhKGu8d9GZmv8/JHZF3bQwONINTIlMvvCCa7EmDGUGLYe2EAF+7k9VW+5KVpvuSlab7kpWm+5KVpvuSlab7kpWm+5KVpvuSlab7kpWm9SDimHlEPKIeWQcrRytHK0csQjwdHb2WMltX50MvaYOpTo4AAnuArjkeBCARuoIDbFptgUWzwSHH2ePSYUXRgJf6GADVTwiHsM9veYGhT1JKYGXXT8UY/fO+7sF3bQQAcHOMFVGHf2Hoq4s1/YwLDF5Y87+4UGOhi2I5tjzlA/psX0mDSU2EAFI25chcjbo/Oxx8yhbnFBIm8tjjfy1uLIIm8txHEPv1DABh42iyOLe/iFBjoYtvhZ48btcThx4/Y4nEhvj8YZ6e1xOJHeHicU6X2hgQ4OcIIrMSYY9aNXrMcEo0TNNhKzihINPBRxq4tZRYkTPBRxN4pZRYkCNlDBDhro4AAniK1hixt33HFjrlFi2DSwg2FrgRHXAldhJPSFAkZcD1SwgwZ6Fms9E/rECa7CM6FPFLCBCsbViV8znuYvnOAqjKf5Eb9xPM1f2EAF+9WJ1WOGUqKDA5zgKjw74U4UMK7OCjTQwQFOcBVGzsfNMFYrS2yggh084s5oGpHHUfZjWlKf0Qgijy9UMCJE24k8vvA43vOEIo8vnOBxvDN++UjpCwVsoIIdNDBs8RNGSl84wZUY85gSBWxXR3ePGUvndYi1xhIHGHFn4CqMPL5QwOMsjt6VHnOeEjt42I4OuR5znhIHeNii6yDmPF0YeXxh2OLQI4+P/rsec5760c/WY85TPzrXesx5SnQw4sZ1iDy+UMAGRtw4t8jYaCUxuylxgqsw0vTCGF440UAHY4Aizi063i5chdFXfqGADVSwgwbGRY1rFjfhE+MmfKGAx2DjLX6sGGW+sIMGxphcXJ0Y6bpwgqvwnM57ooANVLCDMbIYF2pM8BgvvUXzPJI3UcAGxlnEn80OGujgACcY45hxJWP860IBG6hgBw10cIA5FtxjITC7ndhABTsYZ9EDHRzgBOMsjt/NzlHqEwVsoIIdNNDB+C2O1IslvxIFbGCchQd20EAHBzjBVag3MGwjsIEKdjBsM9DBAU4wJzr0cyLWhQI2UMEOGujgKDwni7TAOIsV2EAFj7OQuOpHdptEIzhuwokDnOAqPHI+UcAGHjaJBhPTSKIr6ZyIFe99MeXKoispluZK7KCBESGu+hjgBFdh5PGFAjZQ6xhi5OtCAx0c4AQ5i3M6yYkCxlnEL7/iLOKqLwcHOMHjLKJnKxbhShTwOIvo5IrpWYkdNNDBAU4wbEeDiUlbiQKGTQMV7KCBDg5wgmE72kNM2koUMGwWqGAHDXRwgBMM29F2YhGuRAHDNgMPWzz0xiJcFr0ZMS/MIp1iXljiACd42KKz4pwZFq/759SwyM1zbtiFCnYwbHE4MT8sXqpjgphFcYwJYomr0G5gnNsIbKCCHcw5bz12LEwc4ARXYTx4XyhgAxWMWW5xJWPu2IUTXIUjziKu5BCwgQp20EAHBzgLoxLEbTHmlCUqGHHjJ4w7+oUODnAWrogbP3fkfPQexBSyRAcHOMF1zTnu45yUfKKADVSwgwY6OAoju6OvItbYSmyggnEWMzB+oePXPCeTXShgTCO8BSrYwZg1KIHHGUcfSMwds+gDiblj53WIuWOJDVSwgwZGXAtchZGFFwrYrgn4fZwfBZzYQQMdHOAEV2F0eF14xI3kPZfFutDAmFJ5/tsBxlmc/2AVxj32wpjDFxc17rEXKhjT+FqggQ4OcIIxXzCuTmThhQI2UMEOGuhgxI1fKL4HiBoVc8UseohirljiAOPIovXNVbjiyOI6RL5d2MCYdBmKyMILDXRwgBNciTFfzKKrJyaMJTZQwQ4a6HnGsS2gRQdQ7AuYKGADI64GdtBAB482GbeZc6mtC1dhfNxzoYANVLCDcXV64ARXYWTshXEW8WeRsRcq2MEjA9r5Zw4OcIKr8Pyk9UQBW2HMxYzHupjoldhBAx0c4ARX4ZF6iQJic2wethlooIMDnOAqHDEdMRrMULCDBjo4wCPueUnGKpw3UMCwxc8yFezgrLhzFa4bKCCHvjj01cu2DHRwgLPE56TNA9c5azMwxmGiysU0rMQJHkMx0bcdU7JmlLaYk5XYQAWPMZ/o14hJWYkOjkKNuD0wIsSRxZyrCw0c/Ns4SA9chf0GCthABTsYihno4ADDtgJXod3AsI3ABirYQU7IHBzgBFeh30ABG8jlcy5f5EU8nMYMqwtjFuSFAjZQwQ4a6OAAsQ1sE9vENrFNbBPbxDaxTWxnOsVPeKZT4JlOJwrYQAU7aKCDA8S20ma32w0UsIEKdtBABwc4QWyCTbAJNsEm2ASbYBNsgk2wNWwNW8PWsDVsDVvD1rA1bA2bYlNsik2xKTbFptgUm2JTbB1bx9axdWwdW8fWsXVsHVvHZtgMm2EzbIbNsBk2w2bYDJtjc2yOzbE5Nsfm2BybY3NsA9vANrANbAPbwDawDWwD28A2sU1sE9vENrFNbBPbxDaxTWwL28K2sC1sC9vCtrAtbAsbtUSoJUItEWqJUEuEWiLUEqGWCLVEqCVCLRFqiVBLhFoi1BKhlgi1RKglQi0RaolQS4RaItQSoZYItUSoJUItEWqJUEuEWiLUEqGWCLVEqCVCLRFqiVBLhFoi1BKhlgi1RKglQi0RaolQS4RaItQSoZYItUSoJUItEWqJUEuEWiLUEqGWCLVEqCVCLRFqiVBLhFoi1BKhlgi1RKglQi0RaolQS4RaItQSoZYItUSoJUItEWqJnLVkBsbHSxI4wAmuwqglFwrYQAU7aCC2iW1im9gWtoVtYVvYopYcXaAW88ASHRzgBFdizAPzo+fUYh5YYgPDNgLDNgMNdHCAE1yFUUsuFLCBCmITbIJNsAk2wdawNWwNW8PWsDVsDVvD1rA1bIpNsSk2xabYFJtiU2yKTbF1bB1bx9axdWwdW8fWsXVsHZthM2yGzbAZNsNm2AybYTNsjs2xOTbH5tgcm2NzbI7NsQ1sA9vANrANbAPbwDawDWwD28Q2sU1sE9vENrFNbBPbxDaxLWwL28K2sC1sC9vCtrAtbKtseruBAjZQwQ4a6OAAJ4iNWqLUEqWWKLVEqSVKLVFqiVJLlFqi1BKllii1RKklSi1RaolSS5RaotQSpZYotUSpJUotUWqJUkuUWqLUEqWWKLVEqSVKLVFqiVJLlFqi1BKllii1RKklSi1RaolSS5RaotQSpZYotUSpJUotUWqJUkuUWqLUEqWWKLVEqSVKLVFqiVJLlFqi1BKllii1RKklSi1RaolSS5RaotQSpZYotUSpJUotUWqJUkuUWqLUEqWWKLVEqSVKLVFqiVJLlFqi1BKllii1RKklSi1RaolSS5RaotSSTi3p1JJOLenUkk4t6dSSmAPnxyCAxRy4xAmuwqglFwp42I5hRos5cIkdNNDBAU5wFUYtuVBAbA1bw9awNWwNW8PWsCk2xabYFJtiU2yKTbEpNsXWsXVsHVvH1rF1bB1bx9axdWyGzbAZNsNm2AybYTNshs2wOTbH5tgcm2NzbI7NsTk2xzawDWwD28A2sA1sA9vANrANbBPbxDaxTWwT28Q2sU1sE9vEtrAtbAvbwrawLWwL28K2sK2yxVpjiQI2UMEOGujgACeITbAJNmqJUUuMWmLUEqOWGLXEqCVGLTFqiVFLjFpi1BKjlhi1xKglRi0xaolRS4xaYtQSo5YYtcSoJUYtMWqJUUuMWmLUEqOWGLXEqCVGLTFqiVFLjFpi1BKjlhi1xKglRi0xaolRS4xaYtQSo5YYtcSoJUYtMWqJUUuMWmLUEqOWGLXEqCVGLTFqSUz182Pyh8Waa4kCNlDBDhro4AAniG1im9gmtoktaskxLcViAmCigwOc4CqMWnKhgA1UENvCFrXkmKFiMS0wcYIrMaYFJgrYQAXjucQDDXRwgBNchec7zokCNlBBbIJNsAk2wSbYGraGrWFr2Bq2hq1ha9gatoZNsSk2xabYFJtiU2yKTbEpto6tY+vYOraOrWPr2Dq2jq1jM2yGzbAZNsNm2AybYTNshs2xOTbH5tgcm2NzbI7NsTm2gW1gG9gGtoFtYBvYBraBbWCb2Ca2iW1im9gmtoltYpvYJraFbWFb2Ba2hW1hW9gWtoVtlW3cbqCADVSwgwY6OMAJYqOWDGrJoJYMasmglgxqyaCWDGrJoJYMasmglgxqyaCWDGrJoJaMM9F7YCg0cIKr8EzpEwVsoIIdNNBBbB1bx2bYDJthM2yGzbAZNsNm2AybY3Nsjs2xOTbH5tgcm2NzbAPbwDawDWwD28A2sA1sA9vANrFNbBPbxDaxTWwT28Q2sU1sC9vCtrAtbAvbwrawLWwL2yrbudbdhQI2UMEOGujgACcYDyNHtsSMRj/mp1rMaExsoIIdNNDBAU5wFTZsDVvD1rCdK+TdAg10cIATXIXxeHBMzrVzobwLG+hZKs7V8C6c4CrsN1DAI9gxFc1iSbzEDsahx1WPUnHhAOPQz2CrMErFhQI2UMEOGujgALEZtigVHu0hSsWFDVSwgwY6OMAJrsIoFcdsN4u9QRMbqGAHDXQwbNFgolRcuAqjVFwoYAMV7OBhG/ELRam4cIATXIVRKi4UsIEKdhDbwrawLWyrbOcEywsFbKCCHTTQwbD1wAmuwigKF0YEC3RwgBNchZH+FwrYQAU7eNiOD7UtZnYmDnCCqzDS/0IBD9uMs4i3gws7GLYR6OAAJ7gKoz5cKOBhWyGO+nBhL4zsPpY1tJjOmahg9Uwvxi8W4xeL8YvF+MVi/GIxfrEYv1iMXyzGLxbjF4vxi8X4xWL8YjF+sRi/WIxfLMYvFuMXi/GLxfjFYvxiMX6xGL9YjF8sxi/O+Z7Hx+J2zve8sIEKdtBABwc4wVVIn+M5cXOdOMAJrgv9nLh5oYANVLCDBjo4wAliE2yCTbAJNsEm2CJjj+/n/ZyXeaGADVSwgwY6OMAJhm0cGFl4fGDv51zLCx0c4ARXYWThhQI2MI53BXbQQAcHOMFVGHfpC+VY4EsDG6hgBw10cIATXIWx4NWF2BybY3Nsjs2xOTbH5tgGtoFtYBthiwYzOmjgLJwRIX7u2UAFO2iggwOc4CpcNzBs0aJWAxXsYNiiaSwHBzjBlRjzJxMFbKCCYVuBBjo4wAmuQrmBAh6240Ntj/mTiR000MEBTnAVHjmfKCC2hq1ha9gatoatYWvYFJtiU2yKTcPWAw10cBX2iGCBCnbQQAcHOMFVGEvbXShgxB2B8W/jl4+MvVDABirYQQMdHGAcWTSCyNgTI2MvFLCBCnbQwMPW4lIfd97ECa7C486bKGADFeyggdgmtoltYlvYFraFbWFb2Ba2hW1hi+xu0WAiuwNjRmOighHBAgc4wVUYGXuhgA1UsIMGhm0EDnCCqzAy9piU6jFLMbGBCnbQQAcHOMGwHa0kZikmCthABTtooIOH7eiV8JilmLgKjzt6ooANVLCDBjqIrWPr2AybYTNshs2wGTbDZtgMW9QHjRYV9eFCATsYEaI9RM5fuAoj5y8UsIEKdtBAByNutKjI7gsFbKCCHYy40WAiuy+Ms4ifO7L7wsN2zJLxmGOYeNiOJQU85hgmHrZjFQCPOYaJh63HRY3svvCw9bh8kd0Xhu04zZhjmChg2DxQwbCNQAPDNgMHGLYVuAqjEhyf0nvMMUw8bMfwpcccw8TDdmxr4jHHMPGwHcOMHnMMEw/b8QW+xxzDC6MSWBx6VIILG6hgBw10cIATXIWKTbEpNsWm2BSbYlNsik2xdWwdW8fWsXVsHVvH1rF1bD1HoP2cY3ihgA1UsIMRN1pJZLdF04js9vg14+5/oYIdNNDBAU5wFUYluBDbwDawDWwD28A2sA1sA9vENrFNbFEfPFpq1IcLDZyFkfMe7Tdy/kIFO2iggwOc4EqMuYCJYbPABirYQQMdHOAEV2Hk/PEJrsdcwMQGKthBAx0M2wyc4CqMnL9QwAYq2MHDdqxa4DEXMHGAE1yFkfMXCthABTuITbEpNsWm2Dq2jq1j69g6to6tYzvXrNbACa7Cc4nqEyNCNJhzQeoTBzjBVXguSn2igA1UsINhi58lcv7CsK3Aw3Z0YPo5vy/uAef8vgsFbKCCHTTQwQFOENvEFjk/46JGzl+oYAcNdHCAE1yF8UxwYdhCHPXhQgU7aKAnxpy9cfTIeszZS1SwgwY6GMc7A2dh5Oax/qTH3LrEVRi5Gf1cMbcusYEKdtBABwc4wVWo2BSbYlNsik2xKTbFptgUW8fWsUVuHmsWecytS+yggQ4OcIKrMJ7MLxQQm2EzbIbNsBk2w2bYHJtjc2yOzbE5Nsfm2BybYxvYBra4d0fHaMytS+yggQ4OcIKrMO7dFwrYjlXkb4FhiwYeeXyhgQ4OcIKrMPL4QgFztptfc+tO7KAdhyOBDg5wgisx5tYlCthAPdACO2iggwOc4CqMlekvFLCB2ASbYBNsgk2wCbaGrWFr2Bq2hi3WRTlWxvSYW5c4CmNdlAsjwgjsoIEODnCCqzCWSLlQwAbmDCCPOXAzujVjDtyFsezJhQI2UMEOGujgALEZNsfm2BybY3Nsjs2xOTbH5mE7MiDmwE2JNnnkfGIDFeyggQ4OcIKHLXpOYw5cooBh64EKdtBABwc4wVW4whanuQRsoIIdNNDBAU5wJcYcuEQBG6hgBw10cIATxCbYBFvkfHSixhy4xA6OwnOviVuggA1UsIMGOjjACa7CyO5jLVCP5fkSG6hgBw10cIATDNvxy8eEukQBw2aBCnbQQAcHOMFVGPUh+hFjQl1iAxXsoIEODnCCq9CxOTbH5tgcm2NzbI7NsTm2gW1gi/qg8cNGfbiwg6Mwcv74yN9jklxiAxXsoIEODnCCqzByXqM9RHZf2MGIG+0hsvvCiBu/ZmT3hSsxpsPNuEnEdLjEBirYQQMdHOAEV6FgE2yCTbAJNsEm2ASbYBNsDVvD1rA1bFEJopc1psMlOjjACa7Cc4p8XNRzU8kZOMAJxp8dTS4mviUK2EAFO2iggwOchYYicjO6gmMGW+IAjz+LXuGYwXZh5OaFAjZQwQ4a6OAAUUSSRcdzTEVLdDD+TAMnuAojIS8UsIEKdtBAB1FEvkU3d8wpSzQw/ix++ci3Cye4EmNOWaKADVSwgwaWIvZvndGTHvu3JjYwgo3ADhro4AAnuAojnS4UsIHYInGi3/5cIjA662MiWeIqjFvohQI2UMHoeI64Zzf3iQ4OcIKrsN9AAY/jjbGDc7XACyd4HG/0QZ+rBV4oYAMV7KCBDg5wFjqKyKEYqYh5YokDjD/TwFUY97cLBWyggh000MEBoogcinGRmPCV6GD8WbTUSKcLV2Hc1C4UsIEKdtBAB1MxYsLXPIYcRkz4SjQw/mwEDnCCqzCS7EIBG6hgBw1EEdlyNLkRU7sSOxh/tgIdHOAEV2Hk0IUCNlDBDqKIRjDjGKIRXOjgcQzHrMoRU48SV2HU3wsFbKCCHTTQQWwT28QW7eGYwzliOtE8phCOmEN0/rcxh2ge+9iMmEM0j8l3I+YQJSrYQQMdHGAcjgWuwmglF4bNA8M2AsM2A8O2Ai0PPeYQJdYJxRShOUIcbedCBTtooIMDnOAqjLZzYdjiLKLtjDiLaDsXdtDAsMVpnrtonzjBVRjPOxcK2EAFI25cs6i/I65ZFN2js37EWmlzxs8dRffCDho4C+MZZsb1jWeYCyNCNJiotDMuSVTPGZckqueFCoYtrsOZOCc6OMA4hji3SJwTI3HOfxCJc2EDFex1HSJxLnRwFEZ5Pc94ccaLM47yGtjOBnMLHOAE4weIf3s2mBMFjJ9QAxXshbFVwzpxguvA41LH7JBEARuoB1pgBw10cIATXIWxdPyFAjYQW6wMHykdE0USV2GsDH+hgA1UsIMGOogt9meIAhLTRy6M/VsuFLCBCnbQQAcHiG1iW9hiLvYtjizmYt/O/3YlngtMXSjg0R199MuNc4GpCztooIMDnOAqjLnYFwqITbAJNsEm2ARbfF5xdCmOc4GpE2MvhyhMMc0jsYMGOjjACa7Cc734EwXEptgUW+zwEGUwpnkkDnCCqzD2fbhQwAYq2EFsHVvH1rF1bIbNsBm2SN6oUbE8lEU1iuWhLApeTPNIFLCBCnbQQAcHOEFsA9vANrANbAPbwDawDWwD28A2sU1skcdxZ4hpHokdHIWRm/F8FtM8EhuoYAcNdHCAE1yJMbfDjpHXEXM7EhWMYnMLNNDBAU5wFcYWEBcK2EAFscWGSvHcF7M4LB6eYhbHhbE9y4UCNlDBDlq2yZjFkTjACa7CSOkLBWxgHFlc1EjIeBqMORiJDVSwg1HE4uTj64lbnHzMsDoxZlhdKGADFexgxI2DjK+gLhzgBFdhfAV1oYBh00AFO2iggwOc4CqM6RjHtw/jnI5xoYIdNNDBAU5wFcZ0jAuxTWzx6dMxBjjO5ZYuNNDBAU5wFS5+rMWPtfixVv1Y5zJDx85041xQSOK/jY+GLlSwg5ZN7tz578IBTrCa57nz34UCNlDBDmJTbIpNsZEM5ypC57nFV0XSAg30OqEY37xwgqswMuAYihvnekEXNjAuVA/soIHYDJthM2znfign8rM4P4vzszg/y7kfyonYzk1Q5L//+59++8vf/vWP//jz3/76z//4+5/+9Nsf/qv+i//47Q//679++/c//v1Pf/3Hb3/463/+5S//9Nv/88e//Gf8o//49z/+Nf7zH3/8+/1/vV/RP/31/9z/8x7w//75L3866L//ib++Pf/T+xvx0ZkYf35/Jb5ZhVj2Qwx5HmPMjHAfRKi/9/bD37fnfx9b1sXf33/U+vv7y+arBxB99OcB2Hp2AP3538cW7vH394r01gEcb5PnAaz57AD8+d9HnYi/9+bvHMA6BhMjwL3n9NkBzM0BzGxD996xpwewa0f3p448hPur/71xP2tHmyD3lxG5YtzfFLgQa7wc4v6y4RXi/nd1Krfb6zHklj/n/bVgPY+hm+thI9vUveuhPcToP8bYNctZedH5UaWP149iVmrduxnm86PYtE2LD8nPw7iPbVUMk9dDjKoxx+IsT0NsGmiLXVUjxP3lX56GWJsktTyPe8fWQxv/sX22XfuUKjTt3lFVMfqHUrdJ1DZp4U8D/OJaCtdyPrsQbdsqRpX81fVpq2ibtimxyfUZw0Z7ehh9l+1z1bXQ2/PD8N1hjF6HIQ+nMj/8JGNXfOf/VHzn62dyLzrZLu5dFc/TrG1a571buNWvcu/ce/xZfkx4vX39eqh8+XrszqXdLBvpvY/74aHgp3PRXbaNVtk2H8rw/PGq6q6NjVvFuPdycmf8xLnonHUuvW9+l007bV63peb+UMzv5/VDjLG7LbVZrcweY3w4jk0ZvfcgVUu99x48j7E7jtatbo/z+XH0TTs9diPLKnYfjnsaY//L+I1WNn7Iuw9H0na5u6oit8df5mOMXUvl8el+HOt5jF1Lbbeshu3eSf9ejK51k+zzeUvtu0fRngnjzlHcu9l+jLBpH4t8uT1cjJ9C7B5HZVKDHt4Jfoqxax73gZ5sY3KMhjyNYpuG6jySHpMoHhrqjyF2Lwet19vBfQTiWYjt9Wh1gzm+CH1+Jtu7vlfrOLg/v6q7pj7qZO74PF22iXuvQvWucO97bk+j2O7WLzFT4bph3sexnhR3G7/r7eHe6Wm89/TniWvrd73lHtui13H4Jvl993SqIw+k3V/oHo7kx4dkb1+9ptuj6FLl9N6B+vQoto9k6g83On/6SHZ8c/g0hvOAOvSxrfvLMe6dxXk1upo/jzG+/lDn83d9yO23erPuP2R+/0QM3s775lcZsqvr1UQfHpLvA6g/Rti00Fmv91PX8wi6u8Xlaaz1PML2Stit2ud0eX4lbPcAVN1O95L6+JD9Y54M3x2H1C9yH7R7K8Z9xGTRxtvzGPPrbXx8uYpur6jWy7X4D69gP57JlO2DHNVLn1+NbeuYPKav1t/LtaX1mL7m87f82b+aa9O+mmvTf9dcWysPot9/1udXYtf/NBp3tR/y5EMf664DSpQHwZu/FWPdPHNt3dZ6GmPJ13Nttd/zfnIf1c0f9j5o62+18c4Lz3188PZe/+a40aH28Kt87FncdRg3rSJ6f4J62mG8xvZh41YPG+t549jGiI3Sr5vKki/HWLf2ZoybVgx5nixyk69WDrm1r5aOfQivVLk9XI2PMbZXYwnF/IcXhI9Xw77eC7+N8WpL38ZoxHjopvhcjNeGA25r29XhvCO8G6M61O4x/L0YVj2d7T4G/DSGbGtpPTEcq4q+FeNY2PWVGPtzeW2MRbb3+xoL7uvpIMs+xEvjNCLjywM1sh1xemmkZnsUt0UH9kO+fYyxG3KKKctnCZP2/GLshpzufSPVxXnvV3s4mbb6u63j6cCT7EaeYs2ic5h5PL8ev6gdrwx/SbNvKMi7IP3W6qXnNtcmyPamz0izPz6/fBzj3R6JVCm89zb0zZGsL99td2NPL95t96dCb4nI5qJuj+O1W/b+QJxfdzyOk/x0IP3r19S+fE23Ib7jcszKmPu4wu5yzK8m/+4wTOqV1sQ2Lb1vH9ZXDaPpw53SPxFC6yVM9aE7/mOItmvpdRT3t5+nIV69Gm3zpL4PMqof6tgkZnNJbfdyXN0Vd5xPr4d9/Z1U+pdH8vchanjDH54bPhfCGUGbmxC7q2FSgwr22LM322cuKZ1Z9th9/TGIbdqprIdOxtXWey01ltM4G9my+byRWd8OGVfuP1yRMT+EsK8OTWyPwnj96XNzFLsQNZZnc3Mi2wu6Vt4XjhU230v9Y927auvv1o9jnc0K4puS7F9/1fevv+p7+31vlM60v2ONrM3l2Gb/qv6TOz9Uw0/k3P0eK3W73T2f+nZ81I3Cvpn7t43BW9DcvJn6dspJTY7Qtul1+EWM25djdKmXwt5u78VQnj+6PI+xG396eNs/tlB5K8arvQ4vHsc2xv6a1uzr+/Dg816HMb7heozf+1zGw/jm/HKMx7L8uTbW6g3X5Pn1mLs7Px3SMvqm72F7IINelMf3qJ8OpH/9x93FeLWxv3gc7zcQui90Vwx3Q1H3XhrmvTx2Gnzqh4nVxXKo4HlLXbtJfPXq0NumfWyHoiiGx1cDFUQ/fvqwPQ691bv63FyO/S1Xef9Y9vTZcPV9kPYQxN8J8uKL4a9O5rXj2L3FxMoA5/OD3jZjOGvXh3pjUuPjDLyf+re271M18uE/zDb9zCsZT7pz82LoXx/3mF8fsphfH7GYXx6waLtO2Ber6TbGq9V0fXWsYftBx2s99G3XL/5aD33bjR9/oof+5U9knn8ZshtGeqmPrn3DF0fbD0Nm3eDa+qEX+EOM3SjSa918+xAvdfO13RDQa9V8fzHqUa6t3Vcy8uXe/da+3Lu/D/HaC3b7cvdc+3Lv3Paboxc75/bfLb3WN9e2QV7sm9uN+9hQnq0fDuPlACJe/Vl3Ho+dUR+mhO/CvJiu2xCvpevu06fX0nU3+PRaV/j286uXmvg2wktNfPsZ2YtNfP8p2otNfPet0atNfPsdWathgdba43Ty/noM6zWp84eP1OcnvvBrUaPPc2mPn7X8lCldvpwp2xCvZcruO5AXb2yvXw5/Xjj2H9bxROyPI5MfP6x7Ocb4eozH3pLPfOB38+qhvPnmo7jtZ09zPDxKPv/Cr+2+euKZY3Z5M0QtBzBtvBmis6zD/HII1/cuaDPlhelxdvvngnBbcLE3f9pFwqzN77IduvZ6XTk+b3krxv09lm9aNk3sxQ9A7x2lz3/dlz9m3cV49cPc8Txvm3/1rWl7FIOJ9vOmm6PYvCksrVvlunfNPX2Y237ypHzypI/LZ3xopr47mfUwc+5xXPFjjN19Xx4+q7t3eM5nZ7O9qrMm8bT5w037E3V91kvLHf35L7O901nN1JAfZlb+dOPfffi0+Gjy9tjT8OH9fvuBcMx4vPo7Nt8qtt2gz6yx5x/mdn+8INvvp+jBvt3mJsauE+q1L3Pb7sulFz/N3Z7LvUO0xklufXdBdn36y6sffK1NAdh9//RqAZi3rxeA7RdQLxaA7ejTqwVg/9to3e3uIw3tvcb6Y5D1NMj+6/5aM+E+9vz8yX8foy7r/SV2vhdDa3J1+2H9h48xxu7Jrp6p5nuf999HfOuznf5wJp+Jce/srFFBmc8/Zm9r+6ZcQ0d3lPeCHGvd1kjJw7yzTwapxZ6O9TnfDNJrUG901TeDzBogGI8PzZ/6cXqN+9jjHIdPxRgsf/XYJf65GPVGZbPtGsl2uYIbK/PcX8yeXxK97fpDGmO/d3bZhNmNmJoxLm9+u719NKwGcT+atQmzq409dja4BnDnmO9eYta2ubO922Ievw0Y78VYzM5bD8ONn1q95FYt10XeOw6XenN12WXh+vLTzfaV89i0NH+YYw/Ptw7k6CLmu+p7Jr4ZxekLPDabezPKaJzR6P5ulBoPkWP3rTejzBpIlWPTrHeP5eHqTnl+dXU3VPWJ+rQbhTzqEyvDPQ4qfDrMi2XuFyf1apnbfkLVJ0tN9e0l/kWYF6vlL35ve2h7/nbbq2/kxKe9m03rYcWE5f3dKCzVdOxC9F6U0aTqw53t3SgsK3RsxvA0ym6xpe9ZsGkwCjOHyZtRZj2L3Vlub0ZZD8eyNo/K+8WjBuukbbpB9jEWC1D98J38J2LE5mXZMfT8LWjfqbO4j7S1nnemqH75k5V9iNcGDPXLg+K/WI2Lei3r4Xn9Q33cjUstloRY8nRcah+i1Sce6/7C/M64lJqwOpltXtb3UXzV9dAhz6No//Isln2Ilwb7tH99Fsvrl6O9fVF5trhH8TejjBrRubNtBh7ty+Ow+xCv/TT2+47D/ng5NuOwv/pp5kOUzZp+66ulbBvhtek925X0tDqF7ry5xejuQF6cCGLbRxFn3GB4fzPIeHieGY/r438qSA1Q3Xn6O+2sd2J0a88XBfbtI/S3rHPYFl2pP/SU23sxHsdjPhPD6Ba2x8z7RIz78bfqbf9hNPZDjK9/I75d6PBWs0+79Mcf9xOLJQoLyd2fIZ7G0N3qfi/W5W2I1+ry+PI0/u3FaKxt1X5YVOrjYeyW5puLYfbHjzR+CrL7MuqVOYL7w6BXra/HmS2fOhdGDe3W+ttBWgXp8+0g1dF4e3NRz5cXBp1fvV1uI7x0u9yO1L84VXC/wOlrUwV1fsNUwe16ctwr+2zPJ1vr/PJka11fnmy9D/HiZOvd1WiND7z8vbn8XWtdh/7DV2Ifr+jumyivwXF/6I9pH8aT1+6TqBvd2O2xhfUfG8d2IcrXrugvmka2r3nbfC2/jRG7lp4xxmjvxVjVg3i/QchbMe7v+PmYvG7NnsbYjTW+vJpl+2oR3EZ4qQhuP2N8sQjuV+V8rQjGMsxfLYK770vvD031AXIbz1ei7Ldd//1DH/Xwp5937WOwykV7nCb0Mcbu06oXP1XbxnjxU7VfnEs9AbWbtufHsXvXf+l72y7bBX2X8Lush1vcxy/N9kfy0he32wvSpPY8a/eS/NZFbcJ+F/JQPn66qPPrF3Xb9/HaR8z743jpkm7L2Evfye4jvPKZ7HZi32v3yO2K5L2+INTHb1w/tar5qrGt/sPcsU+tak6M9uaq5i+vjP7lPrHx5T6x7fTEF+9v+xXeX7y/6TcsRzW3aw58wyLvrAP140cBn4nBwif3zp/nS+/33fzk1142uo6vvmzsQ7z4aLy9ojUE3Gw9v6K9b79be23lpO0XzIv+0uXP197vfbsJyksLJ/0ixksLJ21jvLhw0q9i3L4c47WFk/YxXls4qe+nd720lkzfT+967ZHyxePYx9he05cWTurWvn49XozxhXN5aeGkl2NsFk76RRt7aeGkvttU6tWFk/YH8trCSX03avPyj7u+3thfPI73G8hrCyd1336p8drCSfsDeW3hpL4fK3npnWP3kdXL7xy/2Jzqlde4X9xxX1o4qfvaB3llwaJtkNdGXH55Mi8dx27wqE02AxF//v7Sd+9Rry6ctH3sf+l9cB/hlffB7fjCS8ewj/DSMWyfLGtmwh3Xf781UDv0YdvD23sxnAFjX/pejFkTYNq62Vsx7gNPdY+7tefXQ3fZ9uqo8zbIvU3Uu+0cT6eMbUOs6kby1cZ7IXjDXvZ0qPbl1tHfbGGNGPr8gvb55VVU9iFeGvvuS37XEC8On2+vp/+P828+95vwxd1Y71aOh+N4NwZbotzx3Ri9vRLDvnxHsS/fUX4xw7P6olZrb04SrRlvd3w6I0q/vCjhL0K8ci3285jr0+P242Jrn5kLXQ+j916G8WYMdmqf493jYCGGOZ7vof2Lue6dWeqPM7s/OWP+Mcrmm6dfRelEGc+/RjCR7f3ttf2nd/udvvYd2C/m/zeWlVmbr1x/cU0W3yKs29vfIjwei74dhReXufqbX66MpkyMvPfovhvFH745efv7F70xtqetvRulP0Sxd7+iUX2M4u9GeehK0fn2dRkPUda728n3h6+Lenv3l+63xyhvtzqWJBhdN7Wlbaddac3ZvPOmwfwqDNOUu+6+U9rPD37147hfHU29VNx58+nsZ07qC2GqU/KYTL67l+j/H9dmcVL91r/jpL4QpteuUtJ36xXYdonA77o2LM98527fclL93fJ5uzHz/3Z79yPlxUc343Fg7pOrStT0uzuuN4N4PbUPb+PNIFaDv8PWu6czqhd4zLfX/Hg8nfeDsG6Yj3fX/GBT1zGkv3skNU38HsTePRJjGTTr3/DryKYkbDet+sSqFNuVUGqu1Lal7BfcYaKA9udfq+wXMK1Zo609fq76YfFR232g+VrX0D7ES/06ZvN3DfHiGr2766l8daPj+WKututPee2jiN1RdDqX+g/rW308Cv366+Fu3cAXXw/3y+M2lp5/nAv8qSV2HzcmeH492m4E5+V1endBXuuW3od4qVv6FyFe6ZbergP9Wh+VfLWLqn25n6x9uZtst6QdPZ+6Hj6yWx86lnaTAY0nXvvhPfRejl89jPXwcdvDN4c/HcZuC1WWCZyPa0Aey1T8GGQ3HN+qt87uXX5Pz2UfRGvK1x3t3SCvXdXtNblfCK7JQ3fFT9dk7jamHvVV6XhcVeLnIJtCqsbmxQ8hbh9D7HaoFMYI2229GYTu7fujQ3szyKiHn/t41O3NIFrT8e7HJO+eDmOeetuczr6pVY+JPa4V/Mn2yseD/SbvVIFXk+/2+x7F18uZ1eontmkd+68qsq4/vHF8IoDcGgvi+nshhE/95eFr0E+FqNnVIvO9o9C6FPK49uNnQhiL0NrDwr6fCuGs2zXXeyfCUkPa3jsRZdRE7a0TebVy7Y7CnXXZxnsnMgZdtKu9FWJxOR8/3v5EiFGPPo8r3X0iwKpXpceNmz8ToIYXH0eNPhOg8msN++IpvBdAWJz94XOBnxZH2G0l9do0fd/tQPfaNP19iNem6W83dVkPi/s9nWPrX9+2bR/C+ATWn4fYrcX34tZv3nYDqy9t/eZt2xfBN479YULXx3mcr/4m4+lvsm3gj0vEPH6J8uFnbds9DF7ZXH1f7upO+sP2FrdPnEgtuiPrh49IPp7Il7/fd/3y9/v7EF/O1SZ0MD2uhvjTxdiOPB0LrXIrfD4V6xdB9HHFSn0aZPcaMGtATm+im9PZvcezhojfHp5QPuwYso3h5Js/dnZ9Ksao9Y7vzxf6PMbXZ+fuDuOe9w8DKs8vx34zqepdeRjtkg9ldDel3tiDxcbjpNY2PwTZFVIWdtEf9pT4GGTfPOzG6IM8vx67jU1bJcy9dfizfh7v2+0B2UWzP9zhpn6IsdtNSmruhMpjl4Z//Gl2/fW8Y+njC5J+XGxrNw75aq+V2ze8OO+vSRc+mXpYhvqna7L93ulW7xd2e9xExT/RSLyW97b58H3gx0ZifXdZZ22xdXuYS/Xxsu52YfK6RfQh7Z1bbnchRNs8O+w+mXrxlrsbFnrxlrsN8eIqaL+41T18Xfx8uT3fLnL36v3Svz6FWr9hWbjtbdtujMg8Pu9/bB/bHc0Xc9Ie16v5qYJsg/C43m5tF2R8Pel+EeTFgujj6/3ePm5f7/f23cZUr/V7+/ajqRf7vfdBXuz33gd5sfdo/1zVqsvgmJ/x9DliN+Qk42EK0OOaefpxcbZtI+FFWTchdousaE11aV03aTPWN6TN9op4Y+7kD+/9Hz8n2379dGNzqrkL0r7hmmzHnF6+JuOVLgh5nCb289lsB534vOTxi4qfz8a/4Wy2Pw5dbT8sNfDz6czvOJ3vaLAvvk7M23zvTfEhxv36P4+xeze6d8uzCoRsXrDWrrlWTzKXtOvrLdVqKNAeP6D8TDUzxpwfJ8x8DLG2q/qwwmN72Dbsp0uxL4kPu6j5eLckvvgQMNbXHwLG7RseAsbtyw8B0Uf41YeAfZAXHwL2QV59CNi1Vr5SWQ+/zMfWOnbL+dmtVs80eVyq6GNr3ZV3EyHI40qzPwXxb2it07+htcp3tFb5emuV72it8h2tVb6jte437ax38Lbs+bPi2A0w9VsV+X57fNn7qam1b6iu+4erV9urfkN7bd/RXtvX22v7jvbavqO9tt+7varUzjn3h/Hnj4pjN2bVR42+9fn4VPJTe53fUV+/42lgfsfTgH5He9Wvt1f9jvaq39Fe9Tva67ZLX9luTR/6rz906Y/du0Cv0Zr+OJm22+shjKEWfzdEfU1pj99pfypE7eH1wwKMb4Z4GKj5VIjHAbTbmyHY6XP190KMmuQzHjd1fjPE455InwrBouK3N0+Efebmuycyaz3f+bhw2psh7M0fdcn/NGXpcyFYYKK/2TpZon25fznEePNHlZuxu70/v6C/GEV0hu4fv27/MIo4bLeP2agH1GMxuKdB9qOIL05b/0WQ12bPbgdFX75X75ZTefle7bcv36u3i8u8eq/eb6j04r16v9zOq/fq3TCxsuKpPnyj//PQ+S4Iu2fo44zzj0GGf0Ov6i+CvPZsuQ3ycnv172iv4+vtdXxHex3f0V7H795eez1ban94tvypqQ3bvlANpnrYs2kNYz9mxeD3eHy+/FDnx4vbmep7ESbPdfPNCDUN6Pb0GLaTRPqq0Xe7PS639eFq7l5xO8+43R/fTt+Ooc9ifGLWzPOZRGP3cZSw6Kn0x7WqPp7Mpon6rbOQ7NpckO0DzWIP0Xufqn9LlPX8wm6ned34TH03zWvXu9vZFe3xy4/1caHQ7TcsbEMvj5//f3w+2y/ZV2X5YTGltj5c1d3+Ey/fHnbjVC/eHnb7Tb18e9gGefGztX2Ql28PuyZiNapq/rDg509NZPcIXwsq9Ie5tx87L9b6bIRjq8gfi2r/hk6yXZBXH2Tm7Rta6rx9uaXG3LivttR9kBcfZPZBXp438+ocb30+x3tut1qaDwu7PcxZ/fhlh24fhvRh8uzzWY1zN1L16kDGL4K8NtVk7kaIXn2N+EWQ17JvG+Tl7Nt9TfVy9u3Gql7MPhnfkH3bIK9m3zbId2Tf/Y1Va5rIfD4beO5Gql4dDdkGeXUKz2zfML/qF0FebPTtG8YR526s6uVGv1tM48VG3+Y3NPptkFcb/TbI9zT6GgJU3Xx1tg3Sb3U6/WabzFH9nYO8tnbNPsRLa9f8IsQra9fsf9wXl474RTN77Rl8X1tfPZJ9lX/xSLbPWK8eye073ku23VYvHskvOtBePBL/jiPxbziSXwwLvLbEyC+CvLa4x37A+tVrsh86f/HXad9xJO07jkS+40jkO47k9h1HcvuGI9l/6/Dikfziq4vXjuS27aDINv84JXfMVwPcu65ZheX2+LWEfyIEa+Pepvg7IRh8vvPD8PMnQqxaBe/OLm+FYNHsHz4K+EyIh52ybg+f5L4eQtjB6M7t2VHM3SdOr57KNshr38Hdtk+IL6ycuQvw0qKXc8jvGuLL10Ga1S/aHj9Imq8vVdFmDQK0x/UZ3g3hb4VQFprQx0/FPxPCmc31+GXVJ0KYsB9uf+9aKCt/9B92g3ozxHs/6uPH6irvhWC9pe7+ZghO5IdFXT8Rot6Opc/3ftS+2A3vdnuzXbCcS3vrR2UjzD7fupg0TWvPr8PczUVtkx1w53xsFS8fRL1DPo5rfeYs6uOJH0ZPPxHAGDfpbwWoHihb+l6Ammu5+tcC/DDT8jMXkSHXt0ql17ig6/riEXz8Gf/3/f/947/++e///Je//esf//Hnv/31P+5/999HqL//+Y//8pc/Xf/v//3Pv/7rw//6j//33/N/+Ze///kvf/nzv/3zv//9b//6p//zn3//0xHp+N9+u13/53+N2/2JbNxG+9//9Jsc/3+/dxgNk37///X4/+VeQce9J+z4348/6Mf2Qf3++Hf8F/EXxzq/9//T/vd/H4f8/wE=", - "brillig_names": [ - "sync_private_state" - ] - } - ], - "outputs": { - "globals": { - "notes": [ - { - "fields": [ - { - "kind": "integer", - "sign": false, - "value": "0000000000000000000000000000000000000000000000000000000000000000" - }, - { - "kind": "string", - "value": "ValueNote" - }, - { - "fields": [ - { - "name": "value", - "value": { - "fields": [ - { - "name": "index", - "value": { - "kind": "integer", - "sign": false, - "value": "0000000000000000000000000000000000000000000000000000000000000000" - } - }, - { - "name": "nullable", - "value": { - "kind": "boolean", - "value": false - } - } - ], - "kind": "struct" - } - }, - { - "name": "owner", - "value": { - "fields": [ - { - "name": "index", - "value": { - "kind": "integer", - "sign": false, - "value": "0000000000000000000000000000000000000000000000000000000000000001" - } - }, - { - "name": "nullable", - "value": { - "kind": "boolean", - "value": false - } - } - ], - "kind": "struct" - } - }, - { - "name": "randomness", - "value": { - "fields": [ - { - "name": "index", - "value": { - "kind": "integer", - "sign": false, - "value": "0000000000000000000000000000000000000000000000000000000000000002" - } - }, - { - "name": "nullable", - "value": { - "kind": "boolean", - "value": false - } - } - ], - "kind": "struct" - } - } - ], - "kind": "struct" - } - ], - "kind": "tuple" - } - ], - "storage": [ - { - "fields": [ - { - "name": "contract_name", - "value": { - "kind": "string", - "value": "EasyPrivateVoting" - } - }, - { - "name": "fields", - "value": { - "fields": [ - { - "name": "admin", - "value": { - "fields": [ - { - "name": "slot", - "value": { - "kind": "integer", - "sign": false, - "value": "0000000000000000000000000000000000000000000000000000000000000001" - } - } - ], - "kind": "struct" - } - }, - { - "name": "tally", - "value": { - "fields": [ - { - "name": "slot", - "value": { - "kind": "integer", - "sign": false, - "value": "0000000000000000000000000000000000000000000000000000000000000002" - } - } - ], - "kind": "struct" - } - }, - { - "name": "vote_ended", - "value": { - "fields": [ - { - "name": "slot", - "value": { - "kind": "integer", - "sign": false, - "value": "0000000000000000000000000000000000000000000000000000000000000003" - } - } - ], - "kind": "struct" - } - }, - { - "name": "active_at_block", - "value": { - "fields": [ - { - "name": "slot", - "value": { - "kind": "integer", - "sign": false, - "value": "0000000000000000000000000000000000000000000000000000000000000004" - } - } - ], - "kind": "struct" - } - } - ], - "kind": "struct" - } - } - ], - "kind": "struct" - } - ] - }, - "structs": { - "functions": [ - { - "fields": [ - { - "name": "parameters", - "type": { - "fields": [ - { - "name": "candidate", - "type": { - "kind": "field" - } - } - ], - "kind": "struct", - "path": "EasyPrivateVoting::add_to_tally_public_parameters" - } - } - ], - "kind": "struct", - "path": "EasyPrivateVoting::add_to_tally_public_abi" - }, - { - "fields": [ - { - "name": "parameters", - "type": { - "fields": [ - { - "name": "candidate", - "type": { - "kind": "field" - } - } - ], - "kind": "struct", - "path": "EasyPrivateVoting::cast_vote_parameters" - } - } - ], - "kind": "struct", - "path": "EasyPrivateVoting::cast_vote_abi" - }, - { - "fields": [ - { - "name": "parameters", - "type": { - "fields": [ - { - "name": "admin", - "type": { - "fields": [ - { - "name": "inner", - "type": { - "kind": "field" - } - } - ], - "kind": "struct", - "path": "aztec::protocol_types::address::aztec_address::AztecAddress" - } - } - ], - "kind": "struct", - "path": "EasyPrivateVoting::constructor_parameters" - } - } - ], - "kind": "struct", - "path": "EasyPrivateVoting::constructor_abi" - }, - { - "fields": [ - { - "name": "parameters", - "type": { - "fields": [], - "kind": "struct", - "path": "EasyPrivateVoting::end_vote_parameters" - } - } - ], - "kind": "struct", - "path": "EasyPrivateVoting::end_vote_abi" - }, - { - "fields": [ - { - "name": "parameters", - "type": { - "fields": [ - { - "name": "candidate", - "type": { - "kind": "field" - } - } - ], - "kind": "struct", - "path": "EasyPrivateVoting::get_vote_parameters" - } - }, - { - "name": "return_type", - "type": { - "kind": "field" - } - } - ], - "kind": "struct", - "path": "EasyPrivateVoting::get_vote_abi" - }, - { - "fields": [ - { - "name": "parameters", - "type": { - "fields": [], - "kind": "struct", - "path": "EasyPrivateVoting::sync_private_state_parameters" - } - } - ], - "kind": "struct", - "path": "EasyPrivateVoting::sync_private_state_abi" - } - ] - } - }, - "file_map": { - "100": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr", - "source": "use protocol_types::{address::AztecAddress, debug_log::debug_log};\n\npub mod nonce_discovery;\npub mod partial_notes;\npub mod pending_tagged_log;\npub mod private_logs;\npub mod private_notes;\n\nuse private_notes::MAX_NOTE_PACKED_LEN;\n\npub struct NoteHashAndNullifier {\n /// The result of NoteHash::compute_note_hash\n pub note_hash: Field,\n /// The result of NoteHash::compute_nullifier_unconstrained (since all of message discovery is unconstrained)\n pub inner_nullifier: Field,\n}\n\n/// A function which takes a note's packed content, address of the emitting contract, nonce, storage slot and note type\n/// ID and attempts to compute its note hash (not siloed by nonce nor address) and inner nullifier (not siloed by\n/// address).\n///\n/// This function must be user-provided as its implementation requires knowledge of how note type IDs are allocated in a\n/// contract. The `#[aztec]` macro automatically creates such a contract library method called\n/// `_compute_note_hash_and_nullifier`, which looks something like this:\n///\n/// ```\n/// |packed_note, contract_address, nonce, storage_slot, note_type_id| {\n/// if note_type_id == MyNoteType::get_id() {\n/// assert(packed_note.len() == MY_NOTE_TYPE_SERIALIZATION_LENGTH);\n///\n/// let note = MyNoteType::unpack(aztec::utils::array::subarray(packed_note.storage(), 0));\n///\n/// let note_hash = note.compute_note_hash(storage_slot);\n/// let note_hash_for_nullify = aztec::note::utils::compute_note_hash_for_nullify(\n/// RetrievedNote{ note, contract_address, metadata: SettledNoteMetadata::new(nonce).into() },\n/// storage_slot\n/// );\n///\n/// let inner_nullifier = note.compute_nullifier_unconstrained(note_hash_for_nullify);\n///\n/// Option::some(\n/// aztec::messages::discovery::NoteHashAndNullifier {\n/// note_hash, inner_nullifier\n/// }\n/// )\n/// } else if note_type_id == MyOtherNoteType::get_id() {\n/// ... // Similar to above but calling MyOtherNoteType::unpack_content\n/// } else {\n/// Option::none() // Unknown note type ID\n/// };\n/// }\n/// ```\ntype ComputeNoteHashAndNullifier = unconstrained fn[Env](/* packed_note */BoundedVec, /* storage_slot */ Field, /* note_type_id */ Field, /* contract_address */ AztecAddress, /* nonce */ Field) -> Option;\n\n/// Performs the message discovery process, in which private are downloaded and inspected to find new private notes,\n/// partial notes and events, etc., and pending partial notes are processed to search for their completion logs.\n/// This is the mechanism via which a contract updates its knowledge of its private state.\n///\n/// Receives the address of the contract on which discovery is performed along with its\n/// `compute_note_hash_and_nullifier` function.\npub unconstrained fn discover_new_messages(\n contract_address: AztecAddress,\n compute_note_hash_and_nullifier: ComputeNoteHashAndNullifier,\n) {\n debug_log(\"Performing message discovery\");\n\n private_logs::fetch_and_process_private_tagged_logs(\n contract_address,\n compute_note_hash_and_nullifier,\n );\n\n partial_notes::fetch_and_process_public_partial_note_completion_logs(\n contract_address,\n compute_note_hash_and_nullifier,\n );\n}\n" - }, - "101": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/messages/discovery/nonce_discovery.nr", - "source": "use crate::messages::discovery::{ComputeNoteHashAndNullifier, private_notes::MAX_NOTE_PACKED_LEN};\n\nuse dep::protocol_types::{\n address::AztecAddress,\n constants::MAX_NOTE_HASHES_PER_TX,\n debug_log::debug_log_format,\n hash::{compute_note_hash_nonce, compute_siloed_note_hash, compute_unique_note_hash},\n traits::ToField,\n};\n\n/// A struct with the discovered information of a complete note, required for delivery to PXE. Note that this is *not*\n/// the complete note information, since it does not include content, storage slot, etc.\npub struct DiscoveredNoteInfo {\n pub nonce: Field,\n pub note_hash: Field,\n pub inner_nullifier: Field,\n}\n\n/// Searches for note nonces that will result in a note that was emitted in a transaction. While rare, it is possible\n/// for multiple notes to have the exact same packed content and storage slot but different nonces, resulting in\n/// different unique note hashes. Because of this this function returns a *vector* of discovered notes, though in most\n/// cases it will contain a single element.\n///\n/// Due to how nonces are computed, this function requires knowledge of the transaction in which the note was created,\n/// more specifically the list of all unique note hashes in it plus the value of its first nullifier.\npub unconstrained fn attempt_note_nonce_discovery(\n unique_note_hashes_in_tx: BoundedVec,\n first_nullifier_in_tx: Field,\n compute_note_hash_and_nullifier: ComputeNoteHashAndNullifier,\n contract_address: AztecAddress,\n storage_slot: Field,\n note_type_id: Field,\n packed_note: BoundedVec,\n) -> BoundedVec {\n let discovered_notes = &mut BoundedVec::new();\n\n debug_log_format(\n \"Attempting nonce discovery on {0} potential notes on contract {1} for storage slot {2}\",\n [unique_note_hashes_in_tx.len() as Field, contract_address.to_field(), storage_slot],\n );\n\n // We need to find nonces (typically just one) that result in a note hash that, once siloed into a unique note hash,\n // is one of the note hashes created by the transaction.\n unique_note_hashes_in_tx.for_eachi(|i, expected_unique_note_hash| {\n // Nonces are computed by hashing the first nullifier in the transaction with the index of the note in the\n // new note hashes array. We therefore know for each note in every transaction what its nonce is.\n let candidate_nonce = compute_note_hash_nonce(first_nullifier_in_tx, i);\n\n // Given nonce, note content and metadata, we can compute the note hash and silo it to check if it matches\n // the note hash at the array index we're currently processing.\n // TODO(#11157): handle failed note_hash_and_nullifier computation\n let hashes = compute_note_hash_and_nullifier(\n packed_note,\n storage_slot,\n note_type_id,\n contract_address,\n candidate_nonce,\n )\n .expect(f\"Failed to compute a note hash for note type {note_type_id}\");\n\n let siloed_note_hash = compute_siloed_note_hash(contract_address, hashes.note_hash);\n let unique_note_hash = compute_unique_note_hash(candidate_nonce, siloed_note_hash);\n\n if unique_note_hash == expected_unique_note_hash {\n // Note that while we did check that the note hash is the preimage of the expected unique note hash, we\n // perform no validations on the nullifier - we fundamentally cannot, since only the application knows\n // how to compute nullifiers. We simply trust it to have provided the correct one: if it hasn't, then\n // PXE may fail to realize that a given note has been nullified already, and calls to the application\n // could result in invalid transactions (with duplicate nullifiers). This is not a concern because an\n // application already has more direct means of making a call to it fail the transaction.\n discovered_notes.push(\n DiscoveredNoteInfo {\n nonce: candidate_nonce,\n note_hash: hashes.note_hash,\n inner_nullifier: hashes.inner_nullifier,\n },\n );\n\n // We don't exit the loop - it is possible (though rare) for the exact same note content to be present\n // multiple times in the same transaction with different nonces. This typically doesn't happen due to\n // notes containing random values in order to hide their contents.\n }\n });\n\n debug_log_format(\n \"Discovered a total of {0} notes\",\n [discovered_notes.len() as Field],\n );\n\n *discovered_notes\n}\n\nmod test {\n use crate::{\n messages::discovery::{NoteHashAndNullifier, private_notes::MAX_NOTE_PACKED_LEN},\n note::{\n note_interface::{NoteHash, NoteType},\n note_metadata::SettledNoteMetadata,\n retrieved_note::RetrievedNote,\n utils::compute_note_hash_for_nullify,\n },\n oracle::random::random,\n test::mocks::mock_note::MockNote,\n utils::array,\n };\n\n use dep::protocol_types::{\n address::AztecAddress,\n hash::{compute_note_hash_nonce, compute_siloed_note_hash, compute_unique_note_hash},\n traits::{FromField, Packable},\n };\n\n use super::attempt_note_nonce_discovery;\n\n // This implementation could be simpler, but this serves as a nice example of the expected flow in a real\n // implementation, and as a sanity check that the interface is sufficient.\n unconstrained fn compute_note_hash_and_nullifier(\n packed_note: BoundedVec,\n storage_slot: Field,\n note_type_id: Field,\n contract_address: AztecAddress,\n nonce: Field,\n ) -> Option {\n if note_type_id == MockNote::get_id() {\n let note = MockNote::unpack(array::subarray(packed_note.storage(), 0));\n let note_hash = note.compute_note_hash(storage_slot);\n\n let note_hash_for_nullify = compute_note_hash_for_nullify(\n RetrievedNote {\n note,\n contract_address,\n metadata: SettledNoteMetadata::new(nonce).into(),\n },\n storage_slot,\n );\n\n let inner_nullifier = note.compute_nullifier_unconstrained(note_hash_for_nullify);\n\n Option::some(NoteHashAndNullifier { note_hash, inner_nullifier })\n } else {\n Option::none()\n }\n }\n\n global VALUE: Field = 7;\n global FIRST_NULLIFIER_IN_TX: Field = 47;\n global CONTRACT_ADDRESS: AztecAddress = AztecAddress::from_field(13);\n global STORAGE_SLOT: Field = 99;\n\n #[test]\n unconstrained fn no_note_hashes() {\n let unique_note_hashes_in_tx = BoundedVec::new();\n let packed_note = BoundedVec::new();\n\n let discovered_notes = attempt_note_nonce_discovery(\n unique_note_hashes_in_tx,\n FIRST_NULLIFIER_IN_TX,\n compute_note_hash_and_nullifier,\n CONTRACT_ADDRESS,\n STORAGE_SLOT,\n MockNote::get_id(),\n packed_note,\n );\n\n assert_eq(discovered_notes.len(), 0);\n }\n\n #[test(should_fail_with = \"Failed to compute a note hash\")]\n unconstrained fn failed_hash_computation() {\n let unique_note_hashes_in_tx = BoundedVec::from_array([random()]);\n let packed_note = BoundedVec::new();\n let note_type_id = 0; // This note type id is unknown to compute_note_hash_and_nullifier\n\n let discovered_notes = attempt_note_nonce_discovery(\n unique_note_hashes_in_tx,\n FIRST_NULLIFIER_IN_TX,\n compute_note_hash_and_nullifier,\n CONTRACT_ADDRESS,\n STORAGE_SLOT,\n note_type_id,\n packed_note,\n );\n\n assert_eq(discovered_notes.len(), 0);\n }\n\n struct NoteAndData {\n note: MockNote,\n nonce: Field,\n note_hash: Field,\n unique_note_hash: Field,\n inner_nullifier: Field,\n }\n\n unconstrained fn construct_note(value: Field, note_index_in_tx: u32) -> NoteAndData {\n let nonce = compute_note_hash_nonce(FIRST_NULLIFIER_IN_TX, note_index_in_tx);\n\n let retrieved_note = MockNote::new(value)\n .contract_address(CONTRACT_ADDRESS)\n .note_metadata(SettledNoteMetadata::new(nonce).into())\n .build_retrieved_note();\n let note = retrieved_note.note;\n\n let note_hash = note.compute_note_hash(STORAGE_SLOT);\n let unique_note_hash =\n compute_unique_note_hash(nonce, compute_siloed_note_hash(CONTRACT_ADDRESS, note_hash));\n let inner_nullifier = note.compute_nullifier_unconstrained(compute_note_hash_for_nullify(\n retrieved_note,\n STORAGE_SLOT,\n ));\n\n NoteAndData { note, nonce, note_hash, unique_note_hash, inner_nullifier }\n }\n\n #[test]\n unconstrained fn single_note() {\n let note_index_in_tx = 2;\n let note_and_data = construct_note(VALUE, note_index_in_tx);\n\n let mut unique_note_hashes_in_tx = BoundedVec::from_array([\n random(), random(), random(), random(), random(), random(), random(),\n ]);\n unique_note_hashes_in_tx.set(note_index_in_tx, note_and_data.unique_note_hash);\n\n let discovered_notes = attempt_note_nonce_discovery(\n unique_note_hashes_in_tx,\n FIRST_NULLIFIER_IN_TX,\n compute_note_hash_and_nullifier,\n CONTRACT_ADDRESS,\n STORAGE_SLOT,\n MockNote::get_id(),\n BoundedVec::from_array(note_and_data.note.pack()),\n );\n\n assert_eq(discovered_notes.len(), 1);\n let discovered_note = discovered_notes.get(0);\n\n assert_eq(discovered_note.nonce, note_and_data.nonce);\n assert_eq(discovered_note.note_hash, note_and_data.note_hash);\n assert_eq(discovered_note.inner_nullifier, note_and_data.inner_nullifier);\n }\n\n #[test]\n unconstrained fn multiple_notes_same_preimage() {\n let first_note_index_in_tx = 3;\n let first_note_and_data = construct_note(VALUE, first_note_index_in_tx);\n\n let second_note_index_in_tx = 5;\n let second_note_and_data = construct_note(VALUE, second_note_index_in_tx);\n\n // Both notes have the same preimage (and therefore packed representation), so both should be found in the same\n // call.\n assert_eq(first_note_and_data.note, second_note_and_data.note);\n let packed_note = first_note_and_data.note.pack();\n\n let mut unique_note_hashes_in_tx = BoundedVec::from_array([\n random(), random(), random(), random(), random(), random(), random(),\n ]);\n unique_note_hashes_in_tx.set(first_note_index_in_tx, first_note_and_data.unique_note_hash);\n unique_note_hashes_in_tx.set(second_note_index_in_tx, second_note_and_data.unique_note_hash);\n\n let discovered_notes = attempt_note_nonce_discovery(\n unique_note_hashes_in_tx,\n FIRST_NULLIFIER_IN_TX,\n compute_note_hash_and_nullifier,\n CONTRACT_ADDRESS,\n STORAGE_SLOT,\n MockNote::get_id(),\n BoundedVec::from_array(packed_note),\n );\n\n assert_eq(discovered_notes.len(), 2);\n\n assert(discovered_notes.any(|discovered_note| {\n (discovered_note.nonce == first_note_and_data.nonce)\n & (discovered_note.note_hash == first_note_and_data.note_hash)\n & (discovered_note.inner_nullifier == first_note_and_data.inner_nullifier)\n }));\n\n assert(discovered_notes.any(|discovered_note| {\n (discovered_note.nonce == second_note_and_data.nonce)\n & (discovered_note.note_hash == second_note_and_data.note_hash)\n & (discovered_note.inner_nullifier == second_note_and_data.inner_nullifier)\n }));\n }\n}\n" - }, - "102": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/messages/discovery/partial_notes.nr", - "source": "use crate::{\n capsules::CapsuleArray,\n messages::{\n discovery::{ComputeNoteHashAndNullifier, nonce_discovery::attempt_note_nonce_discovery},\n encoding::MAX_MESSAGE_CONTENT_LEN,\n },\n oracle::message_discovery::{deliver_note, get_log_by_tag},\n utils::array,\n};\n\nuse dep::protocol_types::{\n address::AztecAddress,\n constants::PUBLIC_LOG_SIZE_IN_FIELDS,\n debug_log::debug_log_format,\n hash::sha256_to_field,\n traits::{Deserialize, Serialize, ToField},\n};\n\nglobal PARTIAL_NOTE_PRIVATE_MSG_CONTENT_NON_NOTE_FIELDS_LEN: u32 = 2;\n\n/// Partial notes have a maximum packed length of their private fields bound by extra content in their private message\n/// (e.g. the storage slot, note completion log tag, etc.).\npub global MAX_PARTIAL_NOTE_PRIVATE_PACKED_LEN: u32 =\n MAX_MESSAGE_CONTENT_LEN - PARTIAL_NOTE_PRIVATE_MSG_CONTENT_NON_NOTE_FIELDS_LEN;\n\n/// The slot in the PXE capsules where we store a `CapsuleArray` of `DeliveredPendingPartialNote`.\npub global DELIVERED_PENDING_PARTIAL_NOTE_ARRAY_LENGTH_CAPSULES_SLOT: Field = sha256_to_field(\n \"AZTEC_NR::DELIVERED_PENDING_PARTIAL_NOTE_ARRAY_LENGTH_CAPSULES_SLOT\".as_bytes(),\n);\n\n/// Public logs contain an extra field at the beginning with the address of the contract that emitted them, and partial\n/// notes emit their completion tag in the log, resulting in the first two fields in the public log not being part of\n/// the packed public content.\n// TODO(#10273): improve how contract log siloing is handled\npub global NON_PACKED_CONTENT_FIELDS_IN_PUBLIC_LOG: u32 = 2;\n\n/// The maximum length of the packed representation of public fields in a partial note. This is limited by public log\n/// size and extra fields in the log (e.g. the tag).\npub global MAX_PUBLIC_PARTIAL_NOTE_PACKED_CONTENT_LENGTH: u32 =\n PUBLIC_LOG_SIZE_IN_FIELDS - NON_PACKED_CONTENT_FIELDS_IN_PUBLIC_LOG;\n\n/// A partial note that was delivered but is still pending completion. Contains the information necessary to find the\n/// log that will complete it and lead to a note being discovered and delivered.\n#[derive(Serialize, Deserialize)]\npub(crate) struct DeliveredPendingPartialNote {\n pub(crate) note_completion_log_tag: Field,\n pub(crate) storage_slot: Field,\n pub(crate) note_type_id: Field,\n pub(crate) packed_private_note_content: BoundedVec,\n pub(crate) recipient: AztecAddress,\n}\n\npub unconstrained fn process_partial_note_private_msg(\n contract_address: AztecAddress,\n recipient: AztecAddress,\n msg_metadata: u64,\n msg_content: BoundedVec,\n) {\n let (note_type_id, storage_slot, note_completion_log_tag, packed_private_note_content) =\n decode_partial_note_private_msg(msg_metadata, msg_content);\n\n // We store the information of the partial note we found in a persistent capsule in PXE, so that we can later search\n // for the public log that will complete it.\n let pending = DeliveredPendingPartialNote {\n note_completion_log_tag,\n storage_slot,\n note_type_id,\n packed_private_note_content,\n recipient,\n };\n\n CapsuleArray::at(\n contract_address,\n DELIVERED_PENDING_PARTIAL_NOTE_ARRAY_LENGTH_CAPSULES_SLOT,\n )\n .push(pending);\n}\n\n/// Searches for public logs that would result in the completion of pending partial notes, ultimately resulting in the\n/// notes being delivered to PXE if completed.\npub unconstrained fn fetch_and_process_public_partial_note_completion_logs(\n contract_address: AztecAddress,\n compute_note_hash_and_nullifier: ComputeNoteHashAndNullifier,\n) {\n let pending_partial_notes = CapsuleArray::at(\n contract_address,\n DELIVERED_PENDING_PARTIAL_NOTE_ARRAY_LENGTH_CAPSULES_SLOT,\n );\n\n debug_log_format(\n \"{} pending partial notes\",\n [pending_partial_notes.len() as Field],\n );\n\n pending_partial_notes.for_each(|i, pending_partial_note: DeliveredPendingPartialNote| {\n let maybe_log = get_log_by_tag(pending_partial_note.note_completion_log_tag);\n if maybe_log.is_none() {\n debug_log_format(\n \"Found no completion logs for partial note with tag {}\",\n [pending_partial_note.note_completion_log_tag],\n );\n\n // Note that we're not removing the pending partial note from the capsule array, so we will continue\n // searching for this tagged log when performing message discovery in the future until we either find it or\n // the entry is somehow removed from the array.\n } else {\n debug_log_format(\n \"Completion log found for partial note with tag {}\",\n [pending_partial_note.note_completion_log_tag],\n );\n let log = maybe_log.unwrap();\n\n // Public logs have an extra field at the beginning with the contract address, which we use to verify\n // that we're getting the logs from the expected contract.\n // TODO(#10273): improve how contract log siloing is handled\n assert_eq(\n log.log_content.get(0),\n contract_address.to_field(),\n \"Got a public log emitted by a different contract\",\n );\n\n // Public fields are assumed to all be placed at the end of the packed representation, so we combine the\n // private and public packed fields (i.e. the contents of the private message and public log sans the extra\n // fields) to get the complete packed content.\n let packed_public_note_content: BoundedVec<_, MAX_PUBLIC_PARTIAL_NOTE_PACKED_CONTENT_LENGTH> =\n array::subbvec(log.log_content, NON_PACKED_CONTENT_FIELDS_IN_PUBLIC_LOG);\n let complete_packed_note = array::append(\n pending_partial_note.packed_private_note_content,\n packed_public_note_content,\n );\n\n let discovered_notes = attempt_note_nonce_discovery(\n log.unique_note_hashes_in_tx,\n log.first_nullifier_in_tx,\n compute_note_hash_and_nullifier,\n contract_address,\n pending_partial_note.storage_slot,\n pending_partial_note.note_type_id,\n complete_packed_note,\n );\n\n debug_log_format(\n \"Discovered {0} notes for partial note with tag {1}\",\n [discovered_notes.len() as Field, pending_partial_note.note_completion_log_tag],\n );\n\n discovered_notes.for_each(|discovered_note| {\n // TODO:(#10728): decide how to handle notes that fail delivery. This could be due to e.g. a\n // temporary node connectivity issue - is simply throwing good enough here?\n assert(\n deliver_note(\n contract_address,\n pending_partial_note.storage_slot,\n discovered_note.nonce,\n complete_packed_note,\n discovered_note.note_hash,\n discovered_note.inner_nullifier,\n log.tx_hash,\n pending_partial_note.recipient,\n ),\n \"Failed to deliver note\",\n );\n });\n\n // Because there is only a single log for a given tag, once we've processed the tagged log then we\n // simply delete the pending work entry, regardless of whether it was actually completed or not.\n // TODO(#11627): only remove the pending entry if we actually process a log that results in the note\n // being completed.\n pending_partial_notes.remove(i);\n }\n });\n}\n\nfn decode_partial_note_private_msg(\n msg_metadata: u64,\n msg_content: BoundedVec,\n) -> (Field, Field, Field, BoundedVec) {\n let note_type_id = msg_metadata as Field; // TODO: make note type id not be a full field\n\n assert(\n msg_content.len() > PARTIAL_NOTE_PRIVATE_MSG_CONTENT_NON_NOTE_FIELDS_LEN,\n f\"Invalid private note message: all partial note private messages must have at least {PARTIAL_NOTE_PRIVATE_MSG_CONTENT_NON_NOTE_FIELDS_LEN} fields\",\n );\n\n // If PARTIAL_NOTE_PRIVATE_MSG_CONTENT_NON_NOTE_FIELDS_LEN is changed, causing the assertion below to fail, then the\n // destructuring of the partial note private message encoding below must be updated as well.\n std::static_assert(\n PARTIAL_NOTE_PRIVATE_MSG_CONTENT_NON_NOTE_FIELDS_LEN == 2,\n \"unexpected value for PARTIAL_NOTE_PRIVATE_MSG_CONTENT_NON_NOTE_FIELDS_LEN\",\n );\n\n // We currently have two fields that are not the partial note's packed representation, which are the storage slot\n // and the note completion log tag.\n let storage_slot = msg_content.get(0);\n let note_completion_log_tag = msg_content.get(1);\n\n let packed_private_note_content = array::subbvec(msg_content, 2);\n\n (note_type_id, storage_slot, note_completion_log_tag, packed_private_note_content)\n}\n" - }, - "104": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/messages/discovery/private_logs.nr", - "source": "use crate::{\n capsules::CapsuleArray,\n messages::{\n discovery::{\n ComputeNoteHashAndNullifier,\n partial_notes::process_partial_note_private_msg,\n pending_tagged_log::{PENDING_TAGGED_LOG_ARRAY_BASE_SLOT, PendingTaggedLog},\n private_notes::process_private_note_msg,\n },\n encoding::decode_message,\n encryption::{aes128::AES128, log_encryption::LogEncryption},\n msg_type::{\n PARTIAL_NOTE_PRIVATE_MSG_TYPE_ID, PRIVATE_EVENT_MSG_TYPE_ID, PRIVATE_NOTE_MSG_TYPE_ID,\n },\n },\n oracle::{logs::store_private_event_log, message_discovery::fetch_tagged_logs},\n utils::array,\n};\n\nuse protocol_types::{\n abis::event_selector::EventSelector,\n address::AztecAddress,\n debug_log::{debug_log, debug_log_format},\n traits::FromField,\n};\n\n/// Searches for private logs that signal new private notes that are then delivered to PXE, or new partial notes that\n/// are stored in the PXE capsules so that `fetch_and_process_public_partial_note_completion_logs` can later search for\n/// public logs that will complete them.\npub unconstrained fn fetch_and_process_private_tagged_logs(\n contract_address: AztecAddress,\n compute_note_hash_and_nullifier: ComputeNoteHashAndNullifier,\n) {\n // We will eventually perform log discovery via tagging here, but for now we simply call the `fetchTaggedLogs` oracle.\n // This makes PXE synchronize tags, download logs and store the pending tagged logs in capsule array which are then\n // retrieved and processed here.\n fetch_tagged_logs(PENDING_TAGGED_LOG_ARRAY_BASE_SLOT);\n\n // Get the logs from the capsule array and process them one by one\n let logs =\n CapsuleArray::::at(contract_address, PENDING_TAGGED_LOG_ARRAY_BASE_SLOT);\n logs.for_each(|i, log: PendingTaggedLog| {\n process_log(contract_address, compute_note_hash_and_nullifier, log);\n logs.remove(i);\n });\n}\n\n/// Processes a log's ciphertext by decrypting it and then searching the plaintext for private notes or partial notes.\n///\n/// Private notes result in nonce discovery being performed prior to delivery, which requires knowledge of the\n/// transaction hash in which the notes would've been created (typically the same transaction in which the log was\n/// emitted), along with the list of unique note hashes in said transaction and the `compute_note_hash_and_nullifier`\n/// function.\n///\n/// Partial notes result in a pending partial note entry being stored in a PXE capsule, which will later be retrieved to\n/// search for the note's completion public log.\nunconstrained fn process_log(\n contract_address: AztecAddress,\n compute_note_hash_and_nullifier: ComputeNoteHashAndNullifier,\n pending_tagged_log: PendingTaggedLog,\n) {\n debug_log_format(\n \"Processing log with tag {0}\",\n [pending_tagged_log.log.get(0)],\n );\n\n // The tag is ignored for now.\n let ciphertext = array::subbvec(pending_tagged_log.log, 1);\n\n let log_plaintext = AES128::decrypt_log(ciphertext, pending_tagged_log.recipient);\n\n // The first thing to do after decrypting the log is to determine what type of private log we're processing. We\n // have 3 log types: private note logs, partial note logs and event logs.\n\n let (msg_type_id, msg_metadata, msg_content) = decode_message(log_plaintext);\n\n if msg_type_id == PRIVATE_NOTE_MSG_TYPE_ID {\n debug_log(\"Processing private note msg\");\n\n process_private_note_msg(\n contract_address,\n pending_tagged_log.tx_hash,\n pending_tagged_log.unique_note_hashes_in_tx,\n pending_tagged_log.first_nullifier_in_tx,\n pending_tagged_log.recipient,\n compute_note_hash_and_nullifier,\n msg_metadata,\n msg_content,\n );\n } else if msg_type_id == PARTIAL_NOTE_PRIVATE_MSG_TYPE_ID {\n debug_log(\"Processing partial note private msg\");\n\n process_partial_note_private_msg(\n contract_address,\n pending_tagged_log.recipient,\n msg_metadata,\n msg_content,\n );\n } else if msg_type_id == PRIVATE_EVENT_MSG_TYPE_ID {\n debug_log(\"Processing private event msg\");\n\n // In the case of events, the msg metadata is the event selector.\n let event_selector = EventSelector::from_field(msg_metadata as Field);\n\n store_private_event_log(\n contract_address,\n pending_tagged_log.recipient,\n event_selector,\n msg_content,\n pending_tagged_log.tx_hash,\n pending_tagged_log.log_index_in_tx,\n pending_tagged_log.tx_index_in_block,\n );\n } else {\n debug_log_format(\"Unknown msg type id {0}\", [msg_type_id as Field]);\n }\n}\n" - }, - "105": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/messages/discovery/private_notes.nr", - "source": "use crate::{\n messages::{\n discovery::{ComputeNoteHashAndNullifier, nonce_discovery::attempt_note_nonce_discovery},\n encoding::MAX_MESSAGE_CONTENT_LEN,\n },\n oracle,\n utils::array,\n};\nuse protocol_types::{\n address::AztecAddress, constants::MAX_NOTE_HASHES_PER_TX, debug_log::debug_log_format,\n};\n\n/// The number of fields in a private note message content that are not the note's packed representation.\nglobal PRIVATE_NOTE_MSG_CONTENT_NON_NOTE_FIELDS_LEN: u32 = 1;\n\n/// The maximum length of the packed representation of a note's contents. This is limited by private log size,\n/// encryption overhead and extra fields in the message (e.g. message type id, storage slot, etc.).\npub global MAX_NOTE_PACKED_LEN: u32 =\n MAX_MESSAGE_CONTENT_LEN - PRIVATE_NOTE_MSG_CONTENT_NON_NOTE_FIELDS_LEN;\n\npub unconstrained fn process_private_note_msg(\n contract_address: AztecAddress,\n tx_hash: Field,\n unique_note_hashes_in_tx: BoundedVec,\n first_nullifier_in_tx: Field,\n recipient: AztecAddress,\n compute_note_hash_and_nullifier: ComputeNoteHashAndNullifier,\n msg_metadata: u64,\n msg_content: BoundedVec,\n) {\n let (note_type_id, storage_slot, packed_note) =\n decode_private_note_msg(msg_metadata, msg_content);\n\n attempt_note_discovery(\n contract_address,\n tx_hash,\n unique_note_hashes_in_tx,\n first_nullifier_in_tx,\n recipient,\n compute_note_hash_and_nullifier,\n storage_slot,\n note_type_id,\n packed_note,\n );\n}\n\n/// Attempts discovery of a note given information about its contents and the transaction in which it is\n/// suspected the note was created.\npub unconstrained fn attempt_note_discovery(\n contract_address: AztecAddress,\n tx_hash: Field,\n unique_note_hashes_in_tx: BoundedVec,\n first_nullifier_in_tx: Field,\n recipient: AztecAddress,\n compute_note_hash_and_nullifier: ComputeNoteHashAndNullifier,\n storage_slot: Field,\n note_type_id: Field,\n packed_note: BoundedVec,\n) {\n let discovered_notes = attempt_note_nonce_discovery(\n unique_note_hashes_in_tx,\n first_nullifier_in_tx,\n compute_note_hash_and_nullifier,\n contract_address,\n storage_slot,\n note_type_id,\n packed_note,\n );\n\n debug_log_format(\n \"Discovered {0} notes from a private message\",\n [discovered_notes.len() as Field],\n );\n\n discovered_notes.for_each(|discovered_note| {\n // TODO:(#10728): handle notes that fail delivery. This could be due to e.g. a temporary node connectivity\n // issue, and we should perhaps not have marked the tag index as taken.\n assert(\n oracle::message_discovery::deliver_note(\n contract_address,\n storage_slot,\n discovered_note.nonce,\n packed_note,\n discovered_note.note_hash,\n discovered_note.inner_nullifier,\n tx_hash,\n recipient,\n ),\n \"Failed to deliver note\",\n );\n });\n}\n\nfn decode_private_note_msg(\n msg_metadata: u64,\n msg_content: BoundedVec,\n) -> (Field, Field, BoundedVec) {\n let note_type_id = msg_metadata as Field; // TODO: make note type id not be a full field\n\n assert(\n msg_content.len() > PRIVATE_NOTE_MSG_CONTENT_NON_NOTE_FIELDS_LEN,\n f\"Invalid private note message: all private note messages must have at least {PRIVATE_NOTE_MSG_CONTENT_NON_NOTE_FIELDS_LEN} fields\",\n );\n\n // If PRIVATE_NOTE_MSG_CONTENT_NON_NOTE_FIELDS_LEN is changed, causing the assertion below to fail, then the\n // destructuring of the private note message encoding below must be updated as well.\n std::static_assert(\n PRIVATE_NOTE_MSG_CONTENT_NON_NOTE_FIELDS_LEN == 1,\n \"unexpected value for PRIVATE_NOTE_MSG_CONTENT_NON_NOTE_FIELDS_LEN\",\n );\n\n // We currently have a single field that is not the note's packed representation, which is the storage slot.\n let storage_slot = msg_content.get(0);\n let packed_note = array::subbvec(msg_content, PRIVATE_NOTE_MSG_CONTENT_NON_NOTE_FIELDS_LEN);\n\n (note_type_id, storage_slot, packed_note)\n}\n" - }, - "106": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/messages/encoding.nr", - "source": "// TODO(#12750): don't make these values assume we're using AES.\nuse crate::{\n messages::encryption::log_encryption::PRIVATE_LOG_PLAINTEXT_SIZE_IN_FIELDS, utils::array,\n};\n\nglobal MAX_MESSAGE_LEN: u32 = PRIVATE_LOG_PLAINTEXT_SIZE_IN_FIELDS;\n\nglobal MESSAGE_EXPANDED_METADATA_LEN: u32 = 1;\n\n// The standard message layout is composed of:\n// - an initial field called the 'expanded metadata'\n// - an arbitrary number of fields following that called the 'message content'\n//\n// ```\n// message: [ msg_expanded_metadata, ...msg_content ]\n// ```\n//\n// The expanded metadata itself is interpreted as a u128, of which:\n// - the upper 64 bits are the message type id\n// - the lower 64 bits are called the 'message metadata'\n//\n// ```\n// msg_expanded_metadata: [ msg_type_id | msg_metadata ]\n// <--- 64 bits --->|<--- 64 bits --->\n// ```\n//\n// The meaning of the message metadata and message content depend on the value of the message type id. Note that there\n// is nothing special about the message metadata, it _can_ be considered part of the content. It just has a different\n// name to make it distinct from the message content given that it is not a full field.\n\n/// The maximum length of a message's content, i.e. not including the expanded message metadata.\npub global MAX_MESSAGE_CONTENT_LEN: u32 = MAX_MESSAGE_LEN - MESSAGE_EXPANDED_METADATA_LEN;\n\n/// Encodes a message following aztec-nr's standard message encoding. This message can later be decoded with\n/// `decode_message` to retrieve the original values.\n///\n/// - The `msg_type` is an identifier that groups types of messages that are all processed the same way, e.g. private\n/// notes or events. Possible values are defined in `aztec::messages::msg_type`.\n/// - The `msg_metadata` and `msg_content` are the values stored in the message, whose meaning depends on the\n/// `msg_type`. The only special thing about `msg_metadata` that separates it from `msg_content` is that it is a u64\n/// instead of a full Field (due to details of how messages are encoded), allowing applications that can fit values into\n/// this smaller variable to achieve higher data efficiency.\npub fn encode_message(\n msg_type: u64,\n msg_metadata: u64,\n msg_content: [Field; N],\n) -> [Field; (N + MESSAGE_EXPANDED_METADATA_LEN)] {\n std::static_assert(\n msg_content.len() <= MAX_MESSAGE_CONTENT_LEN,\n \"Invalid message content: it must have a length of at most MAX_MESSAGE_CONTENT_LEN\",\n );\n\n // If MESSAGE_EXPANDED_METADATA_LEN is changed, causing the assertion below to fail, then the destructuring of\n // the message encoding below must be updated as well.\n std::static_assert(\n MESSAGE_EXPANDED_METADATA_LEN == 1,\n \"unexpected value for MESSAGE_EXPANDED_METADATA_LEN\",\n );\n let mut message: [Field; (N + MESSAGE_EXPANDED_METADATA_LEN)] = std::mem::zeroed();\n\n message[0] = to_expanded_metadata(msg_type, msg_metadata);\n for i in 0..msg_content.len() {\n message[MESSAGE_EXPANDED_METADATA_LEN + i] = msg_content[i];\n }\n\n message\n}\n\n/// Decodes a standard aztec-nr message, i.e. one created via `encode_message`, returning the original encoded values.\n///\n/// Note that `encode_message` returns a fixed size array while this function takes a `BoundedVec`: this is because\n/// prior to decoding the message type is unknown, and consequentially not known at compile time. If working with\n/// fixed-size messages, consider using `BoundedVec::from_array` to convert them.\npub unconstrained fn decode_message(\n message: BoundedVec,\n) -> (u64, u64, BoundedVec) {\n assert(\n message.len() >= MESSAGE_EXPANDED_METADATA_LEN,\n f\"Invalid message: it must have at least {MESSAGE_EXPANDED_METADATA_LEN} fields\",\n );\n\n // If MESSAGE_EXPANDED_METADATA_LEN is changed, causing the assertion below to fail, then the destructuring of\n // the message encoding below must be updated as well.\n std::static_assert(\n MESSAGE_EXPANDED_METADATA_LEN == 1,\n \"unexpected value for MESSAGE_EXPANDED_METADATA_LEN\",\n );\n\n let msg_expanded_metadata = message.get(0);\n let (msg_type_id, msg_metadata) = from_expanded_metadata(msg_expanded_metadata);\n let msg_content = array::subbvec(message, MESSAGE_EXPANDED_METADATA_LEN);\n\n (msg_type_id, msg_metadata, msg_content)\n}\n\nglobal U64_SHIFT_MULTIPLIER: Field = 2.pow_32(64);\n\nfn to_expanded_metadata(msg_type: u64, msg_metadata: u64) -> Field {\n // We use multiplication instead of bit shifting operations to shift the type bits as bit shift operations are\n // expensive in circuits.\n let type_field: Field = (msg_type as Field) * U64_SHIFT_MULTIPLIER;\n let msg_metadata_field = msg_metadata as Field;\n\n type_field + msg_metadata_field\n}\n\nfn from_expanded_metadata(input: Field) -> (u64, u64) {\n input.assert_max_bit_size::<128>();\n let msg_metadata = (input as u64);\n let msg_type = ((input - (msg_metadata as Field)) / U64_SHIFT_MULTIPLIER) as u64;\n // Use division instead of bit shift since bit shifts are expensive in circuits\n (msg_type, msg_metadata)\n}\n\nmod tests {\n use crate::utils::array::subarray::subarray;\n use super::{\n decode_message, encode_message, from_expanded_metadata, MAX_MESSAGE_CONTENT_LEN,\n to_expanded_metadata,\n };\n\n global U64_MAX: u64 = (2.pow_32(64) - 1) as u64;\n global U128_MAX: Field = (2.pow_32(128) - 1);\n\n #[test]\n unconstrained fn encode_decode_empty_message(msg_type: u64, msg_metadata: u64) {\n let encoded = encode_message(msg_type, msg_metadata, []);\n let (decoded_msg_type, decoded_msg_metadata, decoded_msg_content) =\n decode_message(BoundedVec::from_array(encoded));\n\n assert_eq(decoded_msg_type, msg_type);\n assert_eq(decoded_msg_metadata, msg_metadata);\n assert_eq(decoded_msg_content.len(), 0);\n }\n\n #[test]\n unconstrained fn encode_decode_short_message(\n msg_type: u64,\n msg_metadata: u64,\n msg_content: [Field; MAX_MESSAGE_CONTENT_LEN / 2],\n ) {\n let encoded = encode_message(msg_type, msg_metadata, msg_content);\n let (decoded_msg_type, decoded_msg_metadata, decoded_msg_content) =\n decode_message(BoundedVec::from_array(encoded));\n\n assert_eq(decoded_msg_type, msg_type);\n assert_eq(decoded_msg_metadata, msg_metadata);\n assert_eq(decoded_msg_content.len(), msg_content.len());\n assert_eq(subarray(decoded_msg_content.storage(), 0), msg_content);\n }\n\n #[test]\n unconstrained fn encode_decode_full_message(\n msg_type: u64,\n msg_metadata: u64,\n msg_content: [Field; MAX_MESSAGE_CONTENT_LEN],\n ) {\n let encoded = encode_message(msg_type, msg_metadata, msg_content);\n let (decoded_msg_type, decoded_msg_metadata, decoded_msg_content) =\n decode_message(BoundedVec::from_array(encoded));\n\n assert_eq(decoded_msg_type, msg_type);\n assert_eq(decoded_msg_metadata, msg_metadata);\n assert_eq(decoded_msg_content.len(), msg_content.len());\n assert_eq(subarray(decoded_msg_content.storage(), 0), msg_content);\n }\n\n #[test]\n unconstrained fn to_expanded_metadata_packing() {\n // Test case 1: All bits set\n let packed = to_expanded_metadata(U64_MAX, U64_MAX);\n let (msg_type, msg_metadata) = from_expanded_metadata(packed);\n assert_eq(msg_type, U64_MAX);\n assert_eq(msg_metadata, U64_MAX);\n\n // Test case 2: Only log type bits set\n let packed = to_expanded_metadata(U64_MAX, 0);\n let (msg_type, msg_metadata) = from_expanded_metadata(packed);\n assert_eq(msg_type, U64_MAX);\n assert_eq(msg_metadata, 0);\n\n // Test case 3: Only msg_metadata bits set\n let packed = to_expanded_metadata(0, U64_MAX);\n let (msg_type, msg_metadata) = from_expanded_metadata(packed);\n assert_eq(msg_type, 0);\n assert_eq(msg_metadata, U64_MAX);\n\n // Test case 4: No bits set\n let packed = to_expanded_metadata(0, 0);\n let (msg_type, msg_metadata) = from_expanded_metadata(packed);\n assert_eq(msg_type, 0);\n assert_eq(msg_metadata, 0);\n }\n\n #[test]\n unconstrained fn from_expanded_metadata_packing() {\n // Test case 1: All bits set\n let input = U128_MAX as Field;\n let (msg_type, msg_metadata) = from_expanded_metadata(input);\n assert_eq(msg_type, U64_MAX);\n assert_eq(msg_metadata, U64_MAX);\n\n // Test case 2: Only log type bits set\n let input = (U128_MAX - U64_MAX as Field);\n let (msg_type, msg_metadata) = from_expanded_metadata(input);\n assert_eq(msg_type, U64_MAX);\n assert_eq(msg_metadata, 0);\n\n // Test case 3: Only msg_metadata bits set\n let input = U64_MAX as Field;\n let (msg_type, msg_metadata) = from_expanded_metadata(input);\n assert_eq(msg_type, 0);\n assert_eq(msg_metadata, U64_MAX);\n\n // Test case 4: No bits set\n let input = 0;\n let (msg_type, msg_metadata) = from_expanded_metadata(input);\n assert_eq(msg_type, 0);\n assert_eq(msg_metadata, 0);\n }\n\n #[test]\n unconstrained fn to_from_expanded_metadata(original_msg_type: u64, original_msg_metadata: u64) {\n let packed = to_expanded_metadata(original_msg_type, original_msg_metadata);\n let (unpacked_msg_type, unpacked_msg_metadata) = from_expanded_metadata(packed);\n\n assert_eq(original_msg_type, unpacked_msg_type);\n assert_eq(original_msg_metadata, unpacked_msg_metadata);\n }\n}\n" - }, - "107": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/messages/encryption/aes128.nr", - "source": "use dep::protocol_types::{\n constants::{GENERATOR_INDEX__SYMMETRIC_KEY, GENERATOR_INDEX__SYMMETRIC_KEY_2},\n hash::poseidon2_hash_with_separator,\n point::Point,\n};\n\nuse crate::{\n keys::{\n ecdh_shared_secret::derive_ecdh_shared_secret_using_aztec_address,\n ephemeral::generate_ephemeral_key_pair,\n },\n messages::{\n encryption::log_encryption::{\n EPH_PK_SIGN_BYTE_SIZE_IN_BYTES, EPH_PK_X_SIZE_IN_FIELDS,\n HEADER_CIPHERTEXT_SIZE_IN_BYTES, LogEncryption, PRIVATE_LOG_CIPHERTEXT_LEN,\n PRIVATE_LOG_PLAINTEXT_SIZE_IN_FIELDS,\n },\n logs::arithmetic_generics_utils::{\n get_arr_of_size__log_bytes__from_PT, get_arr_of_size__log_bytes_padding__from_PT,\n },\n },\n oracle::{aes128_decrypt::aes128_decrypt_oracle, shared_secret::get_shared_secret},\n prelude::AztecAddress,\n utils::{\n array,\n conversion::{\n bytes_to_fields::{bytes_from_fields, bytes_to_fields},\n fields_to_bytes::{fields_from_bytes, fields_to_bytes},\n },\n point::{get_sign_of_point, point_from_x_coord_and_sign, point_to_bytes},\n random::get_random_bytes,\n },\n};\n\nuse std::aes128::aes128_encrypt;\n\nfn extract_close_to_uniformly_random_256_bits_from_ecdh_shared_secret_using_poseidon2(\n shared_secret: Point,\n) -> [u8; 32] {\n let rand1: Field = poseidon2_hash_with_separator(\n [shared_secret.x, shared_secret.y],\n GENERATOR_INDEX__SYMMETRIC_KEY,\n );\n let rand2: Field = poseidon2_hash_with_separator(\n [shared_secret.x, shared_secret.y],\n GENERATOR_INDEX__SYMMETRIC_KEY_2,\n );\n let rand1_bytes: [u8; 16] = rand1.to_le_bytes();\n let rand2_bytes: [u8; 16] = rand2.to_le_bytes();\n let mut bytes: [u8; 32] = [0; 32];\n for i in 0..16 {\n bytes[i] = rand1_bytes[i];\n bytes[i + 1] = rand2_bytes[i];\n }\n bytes\n}\n\n// TODO(#10537): Consider nuking this function.\nfn extract_close_to_uniformly_random_256_bits_from_ecdh_shared_secret_using_sha256(\n shared_secret: Point,\n) -> [u8; 32] {\n let shared_secret_bytes: [u8; 32] = point_to_bytes(shared_secret);\n\n let mut shared_secret_bytes_with_separator: [u8; 33] = std::mem::zeroed();\n for i in 0..shared_secret_bytes.len() {\n shared_secret_bytes_with_separator[i] = shared_secret_bytes[i];\n }\n shared_secret_bytes_with_separator[32] = GENERATOR_INDEX__SYMMETRIC_KEY;\n\n sha256::digest(shared_secret_bytes_with_separator)\n}\n\nfn derive_aes_symmetric_key_and_iv_from_ecdh_shared_secret(\n shared_secret: Point,\n randomness_extraction_fn: fn(Point) -> [u8; 32],\n) -> ([u8; 16], [u8; 16]) {\n let random_256_bits = randomness_extraction_fn(shared_secret);\n let mut sym_key = [0; 16];\n let mut iv = [0; 16];\n for i in 0..16 {\n sym_key[i] = random_256_bits[i];\n iv[i] = random_256_bits[i + 16];\n }\n (sym_key, iv)\n}\n\n// TODO(#10537): Consider nuking this function.\npub fn derive_aes_symmetric_key_and_iv_from_ecdh_shared_secret_using_sha256(\n shared_secret: Point,\n) -> ([u8; 16], [u8; 16]) {\n derive_aes_symmetric_key_and_iv_from_ecdh_shared_secret(\n shared_secret,\n extract_close_to_uniformly_random_256_bits_from_ecdh_shared_secret_using_sha256,\n )\n}\n\n// TODO(#10537): This function is currently unused. Consider using it instead of the sha256 one.\npub fn derive_aes_symmetric_key_and_iv_from_ecdh_shared_secret_using_poseidon2(\n shared_secret: Point,\n) -> ([u8; 16], [u8; 16]) {\n derive_aes_symmetric_key_and_iv_from_ecdh_shared_secret(\n shared_secret,\n extract_close_to_uniformly_random_256_bits_from_ecdh_shared_secret_using_poseidon2,\n )\n}\n\npub struct AES128 {}\n\nimpl LogEncryption for AES128 {\n fn encrypt_log(\n plaintext: [Field; PLAINTEXT_LEN],\n recipient: AztecAddress,\n ) -> [Field; PRIVATE_LOG_CIPHERTEXT_LEN] {\n // AES 128 operates on bytes, not fields, so we need to convert the fields to bytes.\n // (This process is then reversed when processing the log in `do_process_log`)\n let plaintext_bytes = fields_to_bytes(plaintext);\n\n // *****************************************************************************\n // Compute the shared secret\n // *****************************************************************************\n\n let (eph_sk, eph_pk) = generate_ephemeral_key_pair();\n\n let eph_pk_sign_byte: u8 = get_sign_of_point(eph_pk) as u8;\n\n // (not to be confused with the tagging shared secret)\n let ciphertext_shared_secret =\n derive_ecdh_shared_secret_using_aztec_address(eph_sk, recipient);\n\n // TODO: also use this shared secret for deriving note randomness.\n\n // *****************************************************************************\n // Convert the plaintext into whatever format the encryption function expects\n // *****************************************************************************\n\n // Already done for this strategy: AES expects bytes.\n\n // *****************************************************************************\n // Encrypt the plaintext\n // *****************************************************************************\n\n let (sym_key, iv) = derive_aes_symmetric_key_and_iv_from_ecdh_shared_secret_using_sha256(\n ciphertext_shared_secret,\n );\n\n let ciphertext_bytes = aes128_encrypt(plaintext_bytes, iv, sym_key);\n\n // |full_pt| = |pt_length| + |pt|\n // |pt_aes_padding| = 16 - (|full_pt| % 16)\n // or... since a % b is the same as a - b * (a // b) (integer division), so:\n // |pt_aes_padding| = 16 - (|full_pt| - 16 * (|full_pt| // 16))\n // |ct| = |full_pt| + |pt_aes_padding|\n // = |full_pt| + 16 - (|full_pt| - 16 * (|full_pt| // 16))\n // = 16 + 16 * (|full_pt| // 16)\n // = 16 * (1 + |full_pt| // 16)\n assert(ciphertext_bytes.len() == 16 * (1 + (PLAINTEXT_LEN * 32) / 16));\n\n // *****************************************************************************\n // Compute the header ciphertext\n // *****************************************************************************\n\n // Header contains only the length of the ciphertext stored in 2 bytes.\n // TODO: consider nuking the header altogether and just have a fixed-size ciphertext by padding the plaintext.\n // This would be more costly constraint-wise but cheaper DA-wise.\n let mut header_plaintext: [u8; 2] = [0 as u8; 2];\n let ciphertext_bytes_length = ciphertext_bytes.len();\n header_plaintext[0] = (ciphertext_bytes_length >> 8) as u8;\n header_plaintext[1] = ciphertext_bytes_length as u8;\n\n // TODO: this is insecure and wasteful:\n // \"Insecure\", because the esk shouldn't be used twice (once for the header,\n // and again for the proper ciphertext) (at least, I never got the\n // \"go ahead\" that this would be safe, unfortunately).\n // \"Wasteful\", because the exact same computation is happening further down.\n // I'm leaving that 2nd computation where it is, because this 1st computation\n // will be imminently deleted, when the header logic is deleted.\n let (sym_key, iv) = derive_aes_symmetric_key_and_iv_from_ecdh_shared_secret_using_sha256(\n ciphertext_shared_secret,\n );\n\n // Note: the aes128_encrypt builtin fn automatically appends bytes to the\n // input, according to pkcs#7; hence why the output `header_ciphertext_bytes` is 16\n // bytes larger than the input in this case.\n let header_ciphertext_bytes = aes128_encrypt(header_plaintext, iv, sym_key);\n // I recall that converting a slice to an array incurs constraints, so I'll check the length this way instead:\n assert(header_ciphertext_bytes.len() == HEADER_CIPHERTEXT_SIZE_IN_BYTES);\n\n // *****************************************************************************\n // Prepend / append more bytes of data to the ciphertext, before converting back\n // to fields.\n // *****************************************************************************\n\n let mut log_bytes_padding_to_mult_31 =\n get_arr_of_size__log_bytes_padding__from_PT::();\n // Safety: this randomness won't be constrained to be random. It's in the\n // interest of the executor of this fn to encrypt with random bytes.\n log_bytes_padding_to_mult_31 = unsafe { get_random_bytes() };\n\n let mut log_bytes = get_arr_of_size__log_bytes__from_PT::();\n\n assert(\n log_bytes.len() % 31 == 0,\n \"Unexpected error: log_bytes.len() should be divisible by 31, by construction.\",\n );\n\n log_bytes[0] = eph_pk_sign_byte;\n let mut offset = 1;\n for i in 0..header_ciphertext_bytes.len() {\n log_bytes[offset + i] = header_ciphertext_bytes[i];\n }\n offset += header_ciphertext_bytes.len();\n\n for i in 0..ciphertext_bytes.len() {\n log_bytes[offset + i] = ciphertext_bytes[i];\n }\n offset += ciphertext_bytes.len();\n\n for i in 0..log_bytes_padding_to_mult_31.len() {\n log_bytes[offset + i] = log_bytes_padding_to_mult_31[i];\n }\n\n assert(\n offset + log_bytes_padding_to_mult_31.len() == log_bytes.len(),\n \"Something has gone wrong\",\n );\n\n // *****************************************************************************\n // Convert bytes back to fields\n // *****************************************************************************\n\n // TODO(#12749): As Mike pointed out, we need to make logs produced by different encryption schemes\n // indistinguishable from each other and for this reason the output here and in the last for-loop of this function\n // should cover a full field.\n let log_bytes_as_fields = bytes_to_fields(log_bytes);\n\n // *****************************************************************************\n // Prepend / append fields, to create the final log\n // *****************************************************************************\n\n let mut ciphertext: [Field; PRIVATE_LOG_CIPHERTEXT_LEN] = [0; PRIVATE_LOG_CIPHERTEXT_LEN];\n\n ciphertext[0] = eph_pk.x;\n\n let mut offset = 1;\n for i in 0..log_bytes_as_fields.len() {\n ciphertext[offset + i] = log_bytes_as_fields[i];\n }\n offset += log_bytes_as_fields.len();\n\n for i in offset..PRIVATE_LOG_CIPHERTEXT_LEN {\n // We need to get a random value that fits in 31 bytes to not leak information about the size of the log\n // (all the \"real\" log fields contain at most 31 bytes because of the way we convert the bytes to fields).\n // TODO(#12749): Long term, this is not a good solution.\n\n // Safety: we assume that the sender wants for the log to be private - a malicious one could simply reveal its\n // contents publicly. It is therefore fine to trust the sender to provide random padding.\n let field_bytes = unsafe { get_random_bytes::<31>() };\n ciphertext[i] = Field::from_be_bytes::<31>(field_bytes);\n }\n\n ciphertext\n }\n\n unconstrained fn decrypt_log(\n ciphertext: BoundedVec,\n recipient: AztecAddress,\n ) -> BoundedVec {\n let eph_pk_x = ciphertext.get(0);\n\n let ciphertext_without_eph_pk_x_fields = array::subbvec::(\n ciphertext,\n EPH_PK_X_SIZE_IN_FIELDS,\n );\n\n // Convert the ciphertext represented as fields to a byte representation (its original format)\n let ciphertext_without_eph_pk_x = bytes_from_fields(ciphertext_without_eph_pk_x_fields);\n\n // First byte of the ciphertext represents the ephemeral public key sign\n let eph_pk_sign_bool = ciphertext_without_eph_pk_x.get(0) as bool;\n // With the sign and the x-coordinate of the ephemeral public key, we can reconstruct the point\n let eph_pk = point_from_x_coord_and_sign(eph_pk_x, eph_pk_sign_bool);\n\n // Derive shared secret and symmetric key\n let ciphertext_shared_secret = get_shared_secret(recipient, eph_pk);\n let (sym_key, iv) = derive_aes_symmetric_key_and_iv_from_ecdh_shared_secret_using_sha256(\n ciphertext_shared_secret,\n );\n\n // Extract the header ciphertext\n let header_start = EPH_PK_SIGN_BYTE_SIZE_IN_BYTES; // Skip eph_pk_sign byte\n let header_ciphertext: [u8; HEADER_CIPHERTEXT_SIZE_IN_BYTES] =\n array::subarray(ciphertext_without_eph_pk_x.storage(), header_start);\n // We need to convert the array to a BoundedVec because the oracle expects a BoundedVec as it's designed to work\n // with logs with unknown length at compile time. This would not be necessary here as the header ciphertext length\n // is fixed. But we do it anyway to not have to have duplicate oracles.\n let header_ciphertext_bvec =\n BoundedVec::::from_array(header_ciphertext);\n\n // Decrypt header\n let header_plaintext = aes128_decrypt_oracle(header_ciphertext_bvec, iv, sym_key);\n\n // Extract ciphertext length from header (2 bytes, big-endian)\n let ciphertext_length =\n ((header_plaintext.get(0) as u32) << 8) | (header_plaintext.get(1) as u32);\n\n // Extract and decrypt main ciphertext\n let ciphertext_start = header_start + HEADER_CIPHERTEXT_SIZE_IN_BYTES;\n let ciphertext_with_padding: [u8; (PRIVATE_LOG_CIPHERTEXT_LEN - EPH_PK_X_SIZE_IN_FIELDS) * 31 - HEADER_CIPHERTEXT_SIZE_IN_BYTES - EPH_PK_SIGN_BYTE_SIZE_IN_BYTES] =\n array::subarray(ciphertext_without_eph_pk_x.storage(), ciphertext_start);\n let ciphertext: BoundedVec =\n BoundedVec::from_parts(ciphertext_with_padding, ciphertext_length);\n\n // Decrypt main ciphertext and return it\n let plaintext_bytes = aes128_decrypt_oracle(ciphertext, iv, sym_key);\n\n // Each field of the original note log was serialized to 32 bytes so we convert the bytes back to fields.\n fields_from_bytes(plaintext_bytes)\n }\n}\n\nmod test {\n use crate::{\n keys::ecdh_shared_secret::derive_ecdh_shared_secret_using_aztec_address,\n messages::encryption::log_encryption::{LogEncryption, PRIVATE_LOG_PLAINTEXT_SIZE_IN_FIELDS},\n test::helpers::test_environment::TestEnvironment,\n };\n use super::AES128;\n use protocol_types::{\n address::AztecAddress,\n indexed_tagging_secret::IndexedTaggingSecret,\n traits::{Deserialize, FromField},\n };\n use std::{embedded_curve_ops::EmbeddedCurveScalar, test::OracleMock};\n\n #[test]\n unconstrained fn encrypt_decrypt_log() {\n let mut env = TestEnvironment::new();\n // Advance 1 block so we can read historic state from private\n env.advance_block_by(1);\n\n let plaintext = [1, 2, 3];\n\n let recipient = AztecAddress::from_field(\n 0x25afb798ea6d0b8c1618e50fdeafa463059415013d3b7c75d46abf5e242be70c,\n );\n\n // Mock random values for deterministic test\n let eph_sk = 0x1358d15019d4639393d62b97e1588c095957ce74a1c32d6ec7d62fe6705d9538;\n let _ = OracleMock::mock(\"getRandomField\").returns(eph_sk).times(1);\n\n let randomness = 0x0101010101010101010101010101010101010101010101010101010101010101;\n let _ = OracleMock::mock(\"getRandomField\").returns(randomness).times(1000000);\n\n let _ = OracleMock::mock(\"getIndexedTaggingSecretAsSender\").returns(\n IndexedTaggingSecret::deserialize([69420, 1337]),\n );\n let _ = OracleMock::mock(\"incrementAppTaggingSecretIndexAsSender\").returns(());\n\n // Encrypt the log\n let encrypted_log = BoundedVec::from_array(AES128::encrypt_log(plaintext, recipient));\n\n // Mock shared secret for deterministic test\n let shared_secret = derive_ecdh_shared_secret_using_aztec_address(\n EmbeddedCurveScalar::from_field(eph_sk),\n recipient,\n );\n let _ = OracleMock::mock(\"getSharedSecret\").returns(shared_secret);\n\n // Decrypt the log\n let decrypted = AES128::decrypt_log(encrypted_log, recipient);\n\n // The decryption function spits out a BoundedVec because it's designed to work with logs with unknown length\n // at compile time. For this reason we need to convert the original input to a BoundedVec.\n let plaintext_bvec =\n BoundedVec::::from_array(plaintext);\n\n // Verify decryption matches original plaintext\n assert_eq(decrypted, plaintext_bvec, \"Decrypted bytes should match original plaintext\");\n\n // The following is a workaround of \"struct is never constructed\" Noir compilation error (we only ever use\n // static methods of the struct).\n let _ = AES128 {};\n }\n}\n" - }, - "12": { - "path": "std/convert.nr", - "source": "// docs:start:from-trait\npub trait From {\n fn from(input: T) -> Self;\n}\n// docs:end:from-trait\n\nimpl From for T {\n fn from(input: T) -> T {\n input\n }\n}\n\n// docs:start:into-trait\npub trait Into {\n fn into(self) -> T;\n}\n\nimpl Into for U\nwhere\n T: From,\n{\n fn into(self) -> T {\n T::from(self)\n }\n}\n// docs:end:into-trait\n\n// docs:start:from-impls\n// Unsigned integers\n\nimpl From for u32 {\n fn from(value: u8) -> u32 {\n value as u32\n }\n}\n\nimpl From for u64 {\n fn from(value: u8) -> u64 {\n value as u64\n }\n}\nimpl From for u64 {\n fn from(value: u32) -> u64 {\n value as u64\n }\n}\n\nimpl From for u128 {\n fn from(value: u8) -> u128 {\n value as u128\n }\n}\nimpl From for u128 {\n fn from(value: u32) -> u128 {\n value as u128\n }\n}\nimpl From for u128 {\n fn from(value: u64) -> u128 {\n value as u128\n }\n}\n\nimpl From for Field {\n fn from(value: u8) -> Field {\n value as Field\n }\n}\nimpl From for Field {\n fn from(value: u32) -> Field {\n value as Field\n }\n}\nimpl From for Field {\n fn from(value: u64) -> Field {\n value as Field\n }\n}\n\nimpl From for Field {\n fn from(value: u128) -> Field {\n value as Field\n }\n}\n\n// Signed integers\n\nimpl From for i32 {\n fn from(value: i8) -> i32 {\n value as i32\n }\n}\n\nimpl From for i64 {\n fn from(value: i8) -> i64 {\n value as i64\n }\n}\nimpl From for i64 {\n fn from(value: i32) -> i64 {\n value as i64\n }\n}\n\n// Booleans\nimpl From for u8 {\n fn from(value: bool) -> u8 {\n value as u8\n }\n}\nimpl From for u32 {\n fn from(value: bool) -> u32 {\n value as u32\n }\n}\nimpl From for u64 {\n fn from(value: bool) -> u64 {\n value as u64\n }\n}\nimpl From for i8 {\n fn from(value: bool) -> i8 {\n value as i8\n }\n}\nimpl From for i32 {\n fn from(value: bool) -> i32 {\n value as i32\n }\n}\nimpl From for i64 {\n fn from(value: bool) -> i64 {\n value as i64\n }\n}\nimpl From for Field {\n fn from(value: bool) -> Field {\n value as Field\n }\n}\n// docs:end:from-impls\n\n/// A generic interface for casting between primitive types,\n/// equivalent of using the `as` keyword between values.\n///\n/// # Example\n///\n/// ```\n/// let x: Field = 1234567890;\n/// let y: u8 = x as u8;\n/// let z: u8 = x.as_();\n/// assert_eq(y, z);\n/// ```\npub trait AsPrimitive {\n /// The equivalent of doing `self as T`.\n fn as_(self) -> T;\n}\n\n#[generate_as_primitive_impls]\ncomptime fn generate_as_primitive_impls(_: FunctionDefinition) -> Quoted {\n let types = [\n quote { bool },\n quote { u8 },\n quote { u16 },\n quote { u32 },\n quote { u64 },\n quote { u128 },\n quote { i8 },\n quote { i16 },\n quote { i32 },\n quote { i64 },\n quote { Field },\n ];\n\n let mut impls = &[];\n for type1 in types {\n for type2 in types {\n impls = impls.push_back(\n quote {\n impl AsPrimitive<$type1> for $type2 {\n fn as_(self) -> $type1 {\n self as $type1\n }\n }\n },\n );\n }\n }\n impls.join(quote {})\n}\n" - }, - "128": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/note/note_metadata.nr", - "source": "use protocol_types::traits::Serialize;\n\n// There's temporarily quite a bit of boilerplate here because Noir does not yet support enums. This file will\n// eventually be simplified into something closer to:\n//\n// pub enum NoteMetadata {\n// PendingSamePhase{ note_hash_counter: u32 },\n// PendingOtherPhase{ note_hash_counter: u32, nonce: Field },\n// Settled{ nonce: Field },\n// }\n//\n// For now, we have `NoteMetadata` acting as a sort of tagged union.\n\nstruct NoteStageEnum {\n /// A note that was created in the transaction that is currently being executed, during the current execution phase,\n /// i.e. non-revertible or revertible.\n ///\n /// These notes are not yet in the note hash tree, though they will be inserted unless nullified in this transaction\n /// (becoming a transient note).\n PENDING_SAME_PHASE: u8,\n /// A note that was created in the transaction that is currently being executed, during the previous execution\n /// phase. Because there are only two phases and their order is always the same (first non-revertible and then\n /// revertible) this implies that the note was created in the non-revertible phase, and that the current phase is\n /// the revertible phase.\n ///\n /// These notes are not yet in the note hash tree, though they will be inserted **even if nullified in this\n /// transaction**. This means that they must be nullified as if they were settled (i.e. using the unique note hash)\n /// in order to avoid double spends once they become settled.\n PENDING_PREVIOUS_PHASE: u8,\n /// A note that was created in a prior transaction and is therefore already in the note hash tree.\n SETTLED: u8,\n}\n\nglobal NoteStage: NoteStageEnum =\n NoteStageEnum { PENDING_SAME_PHASE: 1, PENDING_PREVIOUS_PHASE: 2, SETTLED: 3 };\n\n/// The metadata required to both prove a note's existence and destroy it, by computing the correct note hash for kernel\n/// read requests, as well as the correct nullifier to avoid double-spends.\n///\n/// This represents a note in any of the three valid stages (pending same phase, pending previous phase, or settled). In\n/// order to access the underlying fields callers must first find the appropriate stage (e.g. via `is_settled()`) and\n/// then convert this into the appropriate type (e.g. via `to_settled()`).\n#[derive(Eq, Serialize)]\npub struct NoteMetadata {\n stage: u8,\n maybe_nonce: Field,\n}\n\nimpl NoteMetadata {\n /// Constructs a `NoteMetadata` object from optional note hash counter and nonce. Both a zero note hash counter and\n /// a zero nonce are invalid, so those are used to signal non-existent values.\n pub fn from_raw_data(nonzero_note_hash_counter: bool, maybe_nonce: Field) -> Self {\n if nonzero_note_hash_counter {\n if maybe_nonce == 0 {\n Self { stage: NoteStage.PENDING_SAME_PHASE, maybe_nonce }\n } else {\n Self { stage: NoteStage.PENDING_PREVIOUS_PHASE, maybe_nonce }\n }\n } else if maybe_nonce != 0 {\n Self { stage: NoteStage.SETTLED, maybe_nonce }\n } else {\n panic(\n f\"Note has a zero note hash counter and no nonce - existence cannot be proven\",\n )\n }\n }\n\n /// Returns true if the note is pending **and** from the same phase, i.e. if it's been created in the current\n /// transaction during the current execution phase (either non-revertible or revertible).\n pub fn is_pending_same_phase(self) -> bool {\n self.stage == NoteStage.PENDING_SAME_PHASE\n }\n\n /// Returns true if the note is pending **and** from the previous phase, i.e. if it's been created in the current\n /// transaction during an execution phase prior to the current one. Because private execution only has two phases\n /// with strict ordering, this implies that the note was created in the non-revertible phase, and that the current\n /// phase is the revertible phase.\n pub fn is_pending_previous_phase(self) -> bool {\n self.stage == NoteStage.PENDING_PREVIOUS_PHASE\n }\n\n /// Returns true if the note is settled, i.e. if it's been created in a prior transaction and is therefore already\n /// in the note hash tree.\n pub fn is_settled(self) -> bool {\n self.stage == NoteStage.SETTLED\n }\n\n /// Asserts that the metadata is that of a pending note from the same phase and converts it accordingly.\n pub fn to_pending_same_phase(self) -> PendingSamePhaseNoteMetadata {\n assert_eq(self.stage, NoteStage.PENDING_SAME_PHASE);\n PendingSamePhaseNoteMetadata::new()\n }\n\n /// Asserts that the metadata is that of a pending note from a previous phase and converts it accordingly.\n pub fn to_pending_previous_phase(self) -> PendingPreviousPhaseNoteMetadata {\n assert_eq(self.stage, NoteStage.PENDING_PREVIOUS_PHASE);\n PendingPreviousPhaseNoteMetadata::new(self.maybe_nonce)\n }\n\n /// Asserts that the metadata is that of a settled note and converts it accordingly.\n pub fn to_settled(self) -> SettledNoteMetadata {\n assert_eq(self.stage, NoteStage.SETTLED);\n SettledNoteMetadata::new(self.maybe_nonce)\n }\n}\n\nimpl From for NoteMetadata {\n fn from(_value: PendingSamePhaseNoteMetadata) -> Self {\n NoteMetadata::from_raw_data(true, std::mem::zeroed())\n }\n}\n\nimpl From for NoteMetadata {\n fn from(value: PendingPreviousPhaseNoteMetadata) -> Self {\n NoteMetadata::from_raw_data(true, value.nonce())\n }\n}\n\nimpl From for NoteMetadata {\n fn from(value: SettledNoteMetadata) -> Self {\n NoteMetadata::from_raw_data(false, value.nonce())\n }\n}\n\n/// The metadata required to both prove a note's existence and destroy it, by computing the correct note hash for kernel\n/// read requests, as well as the correct nullifier to avoid double-spends.\n///\n/// This represents a pending same phase note, i.e. a note that was created in the transaction that is currently being\n/// executed during the current execution phase (either non-revertible or revertible).\npub struct PendingSamePhaseNoteMetadata {\n // This struct contains no fields since there is no metadata associated with a pending same phase note: it has no\n // nonce (since it may get squashed by a nullifier emitted in the same phase), and while it does have a note hash\n // counter we cannot constrain its value (and don't need to - only that it is non-zero).\n}\n\nimpl PendingSamePhaseNoteMetadata {\n pub fn new() -> Self {\n Self {}\n }\n}\n\n/// The metadata required to both prove a note's existence and destroy it, by computing the correct note hash for kernel\n/// read requests, as well as the correct nullifier to avoid double-spends.\n///\n/// This represents a pending previous phase note, i.e. a note that was created in the transaction that is currently\n/// being executed, during the previous execution phase. Because there are only two phases and their order is always the\n/// same (first non-revertible and then revertible) this implies that the note was created in the non-revertible phase,\n/// and that the current phase is the revertible phase.\npub struct PendingPreviousPhaseNoteMetadata {\n nonce: Field,\n // This struct does not contain a note hash counter, even though one exists for this note, because we cannot\n // constrain its value (and don't need to - only that it is non-zero).\n}\n\nimpl PendingPreviousPhaseNoteMetadata {\n pub fn new(nonce: Field) -> Self {\n Self { nonce }\n }\n\n pub fn nonce(self) -> Field {\n self.nonce\n }\n}\n\n/// The metadata required to both prove a note's existence and destroy it, by computing the correct note hash for kernel\n/// read requests, as well as the correct nullifier to avoid double-spends.\n///\n/// This represents a settled note, i.e. a note that was created in a prior transaction and is therefore already in the\n/// note hash tree.\npub struct SettledNoteMetadata {\n nonce: Field,\n}\n\nimpl SettledNoteMetadata {\n pub fn new(nonce: Field) -> Self {\n Self { nonce }\n }\n\n pub fn nonce(self) -> Field {\n self.nonce\n }\n}\n" - }, - "131": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/note/utils.nr", - "source": "use crate::{\n context::PrivateContext,\n note::{note_interface::NoteHash, retrieved_note::RetrievedNote},\n};\n\nuse dep::protocol_types::hash::{\n compute_siloed_note_hash, compute_siloed_nullifier, compute_unique_note_hash,\n};\n\n/// Returns the note hash that must be used to issue a private kernel read request for a note.\npub fn compute_note_hash_for_read_request(\n retrieved_note: RetrievedNote,\n storage_slot: Field,\n) -> Field\nwhere\n Note: NoteHash,\n{\n let note_hash = retrieved_note.note.compute_note_hash(storage_slot);\n\n if retrieved_note.metadata.is_settled() {\n // Settled notes are read by siloing with contract address and nonce (resulting in the final unique note hash,\n // which is already in the note hash tree).\n let siloed_note_hash = compute_siloed_note_hash(retrieved_note.contract_address, note_hash);\n compute_unique_note_hash(\n retrieved_note.metadata.to_settled().nonce(),\n siloed_note_hash,\n )\n } else {\n // Pending notes (both same phase and previous phase ones) re read by their non-siloed hash (not even by\n // contract address), which is what is stored in the new note hashes array (at the position hinted by note hash\n // counter).\n note_hash\n }\n}\n\n/// Returns the note hash that must be used to compute a note's nullifier when calling `NoteHash::compute_nullifier` or\n/// `NoteHash::compute_nullifier_unconstrained`.\npub fn compute_note_hash_for_nullify(\n retrieved_note: RetrievedNote,\n storage_slot: Field,\n) -> Field\nwhere\n Note: NoteHash,\n{\n compute_note_hash_for_nullify_from_read_request(\n retrieved_note,\n compute_note_hash_for_read_request(retrieved_note, storage_slot),\n )\n}\n\n/// Same as `compute_note_hash_for_nullify`, except it takes the note hash used in a read request (i.e. what\n/// `compute_note_hash_for_read_request` would return). This is useful in scenarios where that hash has already been\n/// computed to reduce constraints by reusing this value.\npub fn compute_note_hash_for_nullify_from_read_request(\n retrieved_note: RetrievedNote,\n note_hash_for_read_request: Field,\n) -> Field {\n // There is just one instance in which the note hash for nullification does not match the note hash used for a read\n // request, which is when dealing with pending previous phase notes. These had their existence proven using their\n // non-siloed note hash along with the note hash counter (like all pending notes), but since they will be\n // unconditionally inserted in the note hash tree (since they cannot be squashed) they must be nullified using the\n // *unique* note hash.\n // If we didn't, it'd be possible to emit a second different nullifier for the same note in a follow up transaction,\n // once the note is settled, resulting in a double spend.\n\n if retrieved_note.metadata.is_pending_previous_phase() {\n let siloed_note_hash =\n compute_siloed_note_hash(retrieved_note.contract_address, note_hash_for_read_request);\n let nonce = retrieved_note.metadata.to_pending_previous_phase().nonce();\n\n compute_unique_note_hash(nonce, siloed_note_hash)\n } else {\n note_hash_for_read_request\n }\n}\n\n/// Computes a note's siloed nullifier, i.e. the one that will be inserted into the nullifier tree.\npub fn compute_siloed_note_nullifier(\n retrieved_note: RetrievedNote,\n storage_slot: Field,\n context: &mut PrivateContext,\n) -> Field\nwhere\n Note: NoteHash,\n{\n let note_hash_for_nullify = compute_note_hash_for_nullify(retrieved_note, storage_slot);\n let inner_nullifier = retrieved_note.note.compute_nullifier(context, note_hash_for_nullify);\n\n compute_siloed_nullifier(retrieved_note.contract_address, inner_nullifier)\n}\n" - }, - "135": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/oracle/capsules.nr", - "source": "use protocol_types::{address::AztecAddress, traits::{Deserialize, Serialize}};\n\n/// Stores arbitrary information in a per-contract non-volatile database, which can later be retrieved with `load`. If\n/// data was already stored at this slot, it is overwritten.\npub unconstrained fn store(contract_address: AztecAddress, slot: Field, value: T)\nwhere\n T: Serialize,\n{\n let serialized = value.serialize();\n store_oracle(contract_address, slot, serialized);\n}\n\n/// Returns data previously stored via `storeCapsule` in the per-contract non-volatile database. Returns Option::none() if\n/// nothing was stored at the given slot.\npub unconstrained fn load(contract_address: AztecAddress, slot: Field) -> Option\nwhere\n T: Deserialize,\n{\n let serialized_option = load_oracle::(contract_address, slot, N);\n serialized_option.map(|arr| Deserialize::deserialize(arr))\n}\n\n/// Deletes data in the per-contract non-volatile database. Does nothing if no data was present.\npub unconstrained fn delete(contract_address: AztecAddress, slot: Field) {\n delete_oracle(contract_address, slot);\n}\n\n/// Copies a number of contiguous entries in the per-contract non-volatile database. This allows for efficient data\n/// structures by avoiding repeated calls to `loadCapsule` and `storeCapsule`.\n/// Supports overlapping source and destination regions (which will result in the overlapped source values being\n/// overwritten). All copied slots must exist in the database (i.e. have been stored and not deleted)\npub unconstrained fn copy(\n contract_address: AztecAddress,\n src_slot: Field,\n dst_slot: Field,\n num_entries: u32,\n) {\n copy_oracle(contract_address, src_slot, dst_slot, num_entries);\n}\n\n#[oracle(storeCapsule)]\nunconstrained fn store_oracle(\n contract_address: AztecAddress,\n slot: Field,\n values: [Field; N],\n) {}\n\n/// We need to pass in `array_len` (the value of N) as a parameter to tell the oracle how many fields the response must\n/// have.\n///\n/// Note that the oracle returns an Option<[Field; N]> because we cannot return an Option directly. That would\n/// require for the oracle resolver to know the shape of T (e.g. if T were a struct of 3 u32 values then the expected\n/// response shape would be 3 single items, whereas it were a struct containing `u32, [Field;10], u32` then the expected\n/// shape would be single, array, single.). Instead, we return the serialization and deserialize in Noir.\n#[oracle(loadCapsule)]\nunconstrained fn load_oracle(\n contract_address: AztecAddress,\n slot: Field,\n array_len: u32,\n) -> Option<[Field; N]> {}\n\n#[oracle(deleteCapsule)]\nunconstrained fn delete_oracle(contract_address: AztecAddress, slot: Field) {}\n\n#[oracle(copyCapsule)]\nunconstrained fn copy_oracle(\n contract_address: AztecAddress,\n src_slot: Field,\n dst_slot: Field,\n num_entries: u32,\n) {}\n\nmod test {\n // These tests are sort of redundant since we already test the oracle implementation directly in TypeScript, but\n // they are cheap regardless and help ensure both that the TXE implementation works accordingly and that the Noir\n // oracles are hooked up correctly.\n\n use crate::{\n oracle::capsules::{copy, delete, load, store},\n test::{helpers::test_environment::TestEnvironment, mocks::mock_struct::MockStruct},\n };\n use protocol_types::{address::AztecAddress, traits::{FromField, ToField}};\n\n unconstrained fn setup() -> AztecAddress {\n let env = TestEnvironment::new();\n env.contract_address()\n }\n\n global SLOT: Field = 1;\n\n #[test]\n unconstrained fn stores_and_loads() {\n let contract_address = setup();\n\n let value = MockStruct::new(5, 6);\n store(contract_address, SLOT, value);\n\n assert_eq(load(contract_address, SLOT).unwrap(), value);\n }\n\n #[test]\n unconstrained fn store_overwrites() {\n let contract_address = setup();\n\n let value = MockStruct::new(5, 6);\n store(contract_address, SLOT, value);\n\n let new_value = MockStruct::new(7, 8);\n store(contract_address, SLOT, new_value);\n\n assert_eq(load(contract_address, SLOT).unwrap(), new_value);\n }\n\n #[test]\n unconstrained fn loads_empty_slot() {\n let contract_address = setup();\n\n let loaded_value: Option = load(contract_address, SLOT);\n assert_eq(loaded_value, Option::none());\n }\n\n #[test]\n unconstrained fn deletes_stored_value() {\n let contract_address = setup();\n\n let value = MockStruct::new(5, 6);\n store(contract_address, SLOT, value);\n delete(contract_address, SLOT);\n\n let loaded_value: Option = load(contract_address, SLOT);\n assert_eq(loaded_value, Option::none());\n }\n\n #[test]\n unconstrained fn deletes_empty_slot() {\n let contract_address = setup();\n\n delete(contract_address, SLOT);\n let loaded_value: Option = load(contract_address, SLOT);\n assert_eq(loaded_value, Option::none());\n }\n\n #[test]\n unconstrained fn copies_non_overlapping_values() {\n let contract_address = setup();\n\n let src = 5;\n\n let values = [MockStruct::new(5, 6), MockStruct::new(7, 8), MockStruct::new(9, 10)];\n store(contract_address, src, values[0]);\n store(contract_address, src + 1, values[1]);\n store(contract_address, src + 2, values[2]);\n\n let dst = 10;\n copy(contract_address, src, dst, 3);\n\n assert_eq(load(contract_address, dst).unwrap(), values[0]);\n assert_eq(load(contract_address, dst + 1).unwrap(), values[1]);\n assert_eq(load(contract_address, dst + 2).unwrap(), values[2]);\n }\n\n #[test]\n unconstrained fn copies_overlapping_values_with_src_ahead() {\n let contract_address = setup();\n\n let src = 1;\n\n let values = [MockStruct::new(5, 6), MockStruct::new(7, 8), MockStruct::new(9, 10)];\n store(contract_address, src, values[0]);\n store(contract_address, src + 1, values[1]);\n store(contract_address, src + 2, values[2]);\n\n let dst = 2;\n copy(contract_address, src, dst, 3);\n\n assert_eq(load(contract_address, dst).unwrap(), values[0]);\n assert_eq(load(contract_address, dst + 1).unwrap(), values[1]);\n assert_eq(load(contract_address, dst + 2).unwrap(), values[2]);\n\n // src[1] and src[2] should have been overwritten since they are also dst[0] and dst[1]\n assert_eq(load(contract_address, src).unwrap(), values[0]); // src[0] (unchanged)\n assert_eq(load(contract_address, src + 1).unwrap(), values[0]); // dst[0]\n assert_eq(load(contract_address, src + 2).unwrap(), values[1]); // dst[1]\n }\n\n #[test]\n unconstrained fn copies_overlapping_values_with_dst_ahead() {\n let contract_address = setup();\n\n let src = 2;\n\n let values = [MockStruct::new(5, 6), MockStruct::new(7, 8), MockStruct::new(9, 10)];\n store(contract_address, src, values[0]);\n store(contract_address, src + 1, values[1]);\n store(contract_address, src + 2, values[2]);\n\n let dst = 1;\n copy(contract_address, src, dst, 3);\n\n assert_eq(load(contract_address, dst).unwrap(), values[0]);\n assert_eq(load(contract_address, dst + 1).unwrap(), values[1]);\n assert_eq(load(contract_address, dst + 2).unwrap(), values[2]);\n\n // src[0] and src[1] should have been overwritten since they are also dst[1] and dst[2]\n assert_eq(load(contract_address, src).unwrap(), values[1]); // dst[1]\n assert_eq(load(contract_address, src + 1).unwrap(), values[2]); // dst[2]\n assert_eq(load(contract_address, src + 2).unwrap(), values[2]); // src[2] (unchanged)\n }\n\n #[test(should_fail_with = \"copy empty slot\")]\n unconstrained fn cannot_copy_empty_values() {\n let contract_address = setup();\n\n copy(contract_address, SLOT, SLOT, 1);\n }\n\n #[test(should_fail_with = \"not allowed to access\")]\n unconstrained fn cannot_store_other_contract() {\n let contract_address = setup();\n let other_contract_address = AztecAddress::from_field(contract_address.to_field() + 1);\n\n let value = MockStruct::new(5, 6);\n store(other_contract_address, SLOT, value);\n }\n\n #[test(should_fail_with = \"not allowed to access\")]\n unconstrained fn cannot_load_other_contract() {\n let contract_address = setup();\n let other_contract_address = AztecAddress::from_field(contract_address.to_field() + 1);\n\n let _: Option = load(other_contract_address, SLOT);\n }\n\n #[test(should_fail_with = \"not allowed to access\")]\n unconstrained fn cannot_delete_other_contract() {\n let contract_address = setup();\n let other_contract_address = AztecAddress::from_field(contract_address.to_field() + 1);\n\n delete(other_contract_address, SLOT);\n }\n\n #[test(should_fail_with = \"not allowed to access\")]\n unconstrained fn cannot_copy_other_contract() {\n let contract_address = setup();\n let other_contract_address = AztecAddress::from_field(contract_address.to_field() + 1);\n\n copy(other_contract_address, SLOT, SLOT, 0);\n }\n}\n" - }, - "136": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/oracle/enqueue_public_function_call.nr", - "source": "use dep::protocol_types::address::AztecAddress;\n\n#[oracle(notifyEnqueuedPublicFunctionCall)]\nunconstrained fn notify_enqueued_public_function_call_oracle(\n _contract_address: AztecAddress,\n _calldata_hash: Field,\n _side_effect_counter: u32,\n _is_static_call: bool,\n) {}\n\nunconstrained fn notify_enqueued_public_function_call_wrapper(\n contract_address: AztecAddress,\n calldata_hash: Field,\n side_effect_counter: u32,\n is_static_call: bool,\n) {\n notify_enqueued_public_function_call_oracle(\n contract_address,\n calldata_hash,\n side_effect_counter,\n is_static_call,\n )\n}\n\npub fn notify_enqueued_public_function_call(\n contract_address: AztecAddress,\n calldata_hash: Field,\n side_effect_counter: u32,\n is_static_call: bool,\n) {\n // Safety: Notifies the simulator that a public call has been enqueued, allowing it to prepare hints for the AVM to process this call.\n unsafe {\n notify_enqueued_public_function_call_wrapper(\n contract_address,\n calldata_hash,\n side_effect_counter,\n is_static_call,\n )\n }\n}\n\n#[oracle(notifySetPublicTeardownFunctionCall)]\nunconstrained fn notify_set_public_teardown_function_call_oracle(\n _contract_address: AztecAddress,\n _calldata_hash: Field,\n _side_effect_counter: u32,\n _is_static_call: bool,\n) {}\n\nunconstrained fn notify_set_public_teardown_function_call_wrapper(\n contract_address: AztecAddress,\n calldata_hash: Field,\n side_effect_counter: u32,\n is_static_call: bool,\n) {\n notify_set_public_teardown_function_call_oracle(\n contract_address,\n calldata_hash,\n side_effect_counter,\n is_static_call,\n )\n}\n\npub fn notify_set_public_teardown_function_call(\n contract_address: AztecAddress,\n calldata_hash: Field,\n side_effect_counter: u32,\n is_static_call: bool,\n) {\n // Safety: Notifies the simulator that a teardown call has been set, allowing it to prepare hints for the AVM to process this call.\n unsafe {\n notify_set_public_teardown_function_call_wrapper(\n contract_address,\n calldata_hash,\n side_effect_counter,\n is_static_call,\n )\n }\n}\n\npub fn notify_set_min_revertible_side_effect_counter(counter: u32) {\n // Safety: This oracle call returns nothing: we only call it for its side effects. It is therefore always safe\n // to call.\n unsafe { notify_set_min_revertible_side_effect_counter_oracle_wrapper(counter) };\n}\n\npub unconstrained fn notify_set_min_revertible_side_effect_counter_oracle_wrapper(counter: u32) {\n notify_set_min_revertible_side_effect_counter_oracle(counter);\n}\n\n#[oracle(notifySetMinRevertibleSideEffectCounter)]\nunconstrained fn notify_set_min_revertible_side_effect_counter_oracle(_counter: u32) {}\n" - }, - "137": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/oracle/execution.nr", - "source": "use dep::protocol_types::address::AztecAddress;\n\n#[oracle(getContractAddress)]\nunconstrained fn get_contract_address_oracle() -> AztecAddress {}\n\n#[oracle(getBlockNumber)]\nunconstrained fn get_block_number_oracle() -> u32 {}\n\n#[oracle(getChainId)]\nunconstrained fn get_chain_id_oracle() -> Field {}\n\n#[oracle(getVersion)]\nunconstrained fn get_version_oracle() -> Field {}\n\npub unconstrained fn get_contract_address() -> AztecAddress {\n get_contract_address_oracle()\n}\n\npub unconstrained fn get_block_number() -> u32 {\n get_block_number_oracle()\n}\n\npub unconstrained fn get_chain_id() -> Field {\n get_chain_id_oracle()\n}\n\npub unconstrained fn get_version() -> Field {\n get_version_oracle()\n}\n" - }, - "138": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/oracle/execution_cache.nr", - "source": "/// Stores values represented as slice in execution cache to be later obtained by its hash.\npub fn store(values: [Field], hash: Field) {\n // Safety: This oracle call returns nothing: we only call it for its side effects. It is therefore always safe\n // to call. When loading the values, however, the caller must check that the values are indeed the preimage.\n unsafe { store_in_execution_cache_oracle_wrapper(values, hash) };\n}\n\nunconstrained fn store_in_execution_cache_oracle_wrapper(values: [Field], hash: Field) {\n store_in_execution_cache_oracle(values, hash);\n}\n\npub unconstrained fn load(hash: Field) -> [Field; N] {\n load_from_execution_cache_oracle(hash)\n}\n\n#[oracle(storeInExecutionCache)]\nunconstrained fn store_in_execution_cache_oracle(_values: [Field], _hash: Field) {}\n\n#[oracle(loadFromExecutionCache)]\nunconstrained fn load_from_execution_cache_oracle(_hash: Field) -> [Field; N] {}\n" - }, - "139": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/oracle/get_contract_instance.nr", - "source": "use protocol_types::{\n address::AztecAddress, contract_class_id::ContractClassId, contract_instance::ContractInstance,\n traits::FromField,\n};\n\n// NOTE: this is for use in private only\n#[oracle(getContractInstance)]\nunconstrained fn get_contract_instance_oracle(_address: AztecAddress) -> ContractInstance {}\n\n// NOTE: this is for use in private only\nunconstrained fn get_contract_instance_internal(address: AztecAddress) -> ContractInstance {\n get_contract_instance_oracle(address)\n}\n\n// NOTE: this is for use in private only\npub fn get_contract_instance(address: AztecAddress) -> ContractInstance {\n // Safety: The to_address function combines all values in the instance object to produce an address,\n // so by checking that we get the expected address we validate the entire struct.\n let instance = unsafe { get_contract_instance_internal(address) };\n assert_eq(instance.to_address(), address);\n\n instance\n}\n\nstruct GetContractInstanceResult {\n exists: bool,\n member: Field,\n}\n\n// These oracles each return a ContractInstance member\n// plus a boolean indicating whether the instance was found.\n#[oracle(avmOpcodeGetContractInstanceDeployer)]\nunconstrained fn get_contract_instance_deployer_oracle_avm(\n _address: AztecAddress,\n) -> [GetContractInstanceResult; 1] {}\n#[oracle(avmOpcodeGetContractInstanceClassId)]\nunconstrained fn get_contract_instance_class_id_oracle_avm(\n _address: AztecAddress,\n) -> [GetContractInstanceResult; 1] {}\n#[oracle(avmOpcodeGetContractInstanceInitializationHash)]\nunconstrained fn get_contract_instance_initialization_hash_oracle_avm(\n _address: AztecAddress,\n) -> [GetContractInstanceResult; 1] {}\n\nunconstrained fn get_contract_instance_deployer_internal_avm(\n address: AztecAddress,\n) -> [GetContractInstanceResult; 1] {\n get_contract_instance_deployer_oracle_avm(address)\n}\nunconstrained fn get_contract_instance_class_id_internal_avm(\n address: AztecAddress,\n) -> [GetContractInstanceResult; 1] {\n get_contract_instance_class_id_oracle_avm(address)\n}\nunconstrained fn get_contract_instance_initialization_hash_internal_avm(\n address: AztecAddress,\n) -> [GetContractInstanceResult; 1] {\n get_contract_instance_initialization_hash_oracle_avm(address)\n}\n\npub fn get_contract_instance_deployer_avm(address: AztecAddress) -> Option {\n // Safety: AVM opcodes are constrained by the AVM itself\n let GetContractInstanceResult { exists, member } =\n unsafe { get_contract_instance_deployer_internal_avm(address)[0] };\n if exists {\n Option::some(AztecAddress::from_field(member))\n } else {\n Option::none()\n }\n}\npub fn get_contract_instance_class_id_avm(address: AztecAddress) -> Option {\n // Safety: AVM opcodes are constrained by the AVM itself\n let GetContractInstanceResult { exists, member } =\n unsafe { get_contract_instance_class_id_internal_avm(address)[0] };\n if exists {\n Option::some(ContractClassId::from_field(member))\n } else {\n Option::none()\n }\n}\npub fn get_contract_instance_initialization_hash_avm(address: AztecAddress) -> Option {\n // Safety: AVM opcodes are constrained by the AVM itself\n let GetContractInstanceResult { exists, member } =\n unsafe { get_contract_instance_initialization_hash_internal_avm(address)[0] };\n if exists {\n Option::some(member)\n } else {\n Option::none()\n }\n}\n" - }, - "144": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/oracle/key_validation_request.nr", - "source": "use protocol_types::abis::validation_requests::KeyValidationRequest;\n\n#[oracle(getKeyValidationRequest)]\nunconstrained fn get_key_validation_request_oracle(\n _pk_m_hash: Field,\n _key_index: Field,\n) -> KeyValidationRequest {}\n\npub unconstrained fn get_key_validation_request(\n pk_m_hash: Field,\n key_index: Field,\n) -> KeyValidationRequest {\n get_key_validation_request_oracle(pk_m_hash, key_index)\n}\n" - }, - "145": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/oracle/keys.nr", - "source": "use dep::protocol_types::{\n address::{AztecAddress, PartialAddress},\n point::Point,\n public_keys::{IvpkM, NpkM, OvpkM, PublicKeys, TpkM},\n};\n\n#[oracle(getPublicKeysAndPartialAddress)]\nunconstrained fn get_public_keys_and_partial_address_oracle(_address: AztecAddress) -> [Field; 13] {}\n\npub unconstrained fn get_public_keys_and_partial_address(\n address: AztecAddress,\n) -> (PublicKeys, PartialAddress) {\n let result = get_public_keys_and_partial_address_oracle(address);\n\n let keys = PublicKeys {\n npk_m: NpkM { inner: Point { x: result[0], y: result[1], is_infinite: result[2] as bool } },\n ivpk_m: IvpkM {\n inner: Point { x: result[3], y: result[4], is_infinite: result[5] as bool },\n },\n ovpk_m: OvpkM {\n inner: Point { x: result[6], y: result[7], is_infinite: result[8] as bool },\n },\n tpk_m: TpkM {\n inner: Point { x: result[9], y: result[10], is_infinite: result[11] as bool },\n },\n };\n\n let partial_address = PartialAddress::from_field(result[12]);\n\n (keys, partial_address)\n}\n" - }, - "146": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/oracle/logs.nr", - "source": "use crate::messages::encoding::MAX_MESSAGE_CONTENT_LEN;\nuse protocol_types::{abis::event_selector::EventSelector, address::AztecAddress};\n\n/// The below only exists to broadcast the raw log, so we can provide it to the base rollup later to be constrained.\npub unconstrained fn notify_created_contract_class_log(\n contract_address: AztecAddress,\n message: [Field; N],\n length: u32,\n counter: u32,\n) {\n notify_created_contract_class_log_private_oracle(contract_address, message, length, counter)\n}\n\n#[oracle(notifyCreatedContractClassLog)]\nunconstrained fn notify_created_contract_class_log_private_oracle(\n contract_address: AztecAddress,\n message: [Field; N],\n length: u32,\n counter: u32,\n) {}\n\npub unconstrained fn store_private_event_log(\n contract_address: AztecAddress,\n recipient: AztecAddress,\n event_selector: EventSelector,\n msg_content: BoundedVec,\n tx_hash: Field,\n log_index_in_tx: Field,\n tx_index_in_block: Field,\n) {\n store_private_event_log_oracle(\n contract_address,\n recipient,\n event_selector,\n msg_content,\n tx_hash,\n log_index_in_tx,\n tx_index_in_block,\n )\n}\n\n#[oracle(storePrivateEventLog)]\nunconstrained fn store_private_event_log_oracle(\n contract_address: AztecAddress,\n recipient: AztecAddress,\n event_selector: EventSelector,\n msg_content: BoundedVec,\n tx_hash: Field,\n log_index_in_tx: Field,\n tx_index_in_block: Field,\n) {}\n" - }, - "147": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/oracle/message_discovery.nr", - "source": "use crate::messages::discovery::private_notes::MAX_NOTE_PACKED_LEN;\nuse dep::protocol_types::{\n address::AztecAddress,\n constants::{MAX_NOTE_HASHES_PER_TX, PUBLIC_LOG_SIZE_IN_FIELDS},\n};\n\n/// Finds new private logs that may have been sent to all registered accounts in PXE in the current contract and makes\n/// them available for later processing in Noir by storing them in a capsule array.\npub unconstrained fn fetch_tagged_logs(pending_tagged_log_array_base_slot: Field) {\n fetch_tagged_logs_oracle(pending_tagged_log_array_base_slot);\n}\n\n#[oracle(fetchTaggedLogs)]\nunconstrained fn fetch_tagged_logs_oracle(pending_tagged_log_array_base_slot: Field) {}\n\n/// Informs PXE of a note's existence so that it can later be retrieved by the `getNotes` oracle. The note will be\n/// scoped to `contract_address`, meaning other contracts will not be able to access it unless authorized.\n///\n/// The packed note is what `getNotes` will later return. PXE indexes notes by `storage_slot`, so this value\n/// is typically used to filter notes that correspond to different state variables. `note_hash` and `nullifier` are\n/// the inner hashes, i.e. the raw hashes returned by `NoteHash::compute_note_hash` and\n/// `NoteHash::compute_nullifier`. PXE will verify that the siloed unique note hash was inserted into the tree\n/// at `tx_hash`, and will store the nullifier to later check for nullification.\n///\n/// `recipient` is the account to which the note was sent to. Other accounts will not be able to access this note (e.g.\n/// other accounts will not be able to see one another's token balance notes, even in the same PXE) unless authorized.\n///\n/// Returns true if the note was successfully delivered and added to PXE's database.\npub unconstrained fn deliver_note(\n contract_address: AztecAddress,\n storage_slot: Field,\n nonce: Field,\n packed_note: BoundedVec,\n note_hash: Field,\n nullifier: Field,\n tx_hash: Field,\n recipient: AztecAddress,\n) -> bool {\n deliver_note_oracle(\n contract_address,\n storage_slot,\n nonce,\n packed_note,\n note_hash,\n nullifier,\n tx_hash,\n recipient,\n )\n}\n\n/// The contents of a public log, plus contextual information about the transaction in which the log was emitted. This\n/// is the data required in order to discover notes that are being delivered in a log.\n// TODO(#11639): this could also be used to fetch private logs, but the `BoundedVec` maximum length is that of a public\n// log.\npub struct LogWithTxData {\n // The log fields length is PUBLIC_LOG_SIZE_IN_FIELDS. + 1 because the contract address is prepended to the content.\n pub log_content: BoundedVec,\n pub tx_hash: Field,\n /// The array of new note hashes created by `tx_hash`\n pub unique_note_hashes_in_tx: BoundedVec,\n /// The first nullifier created by `tx_hash`\n pub first_nullifier_in_tx: Field,\n}\n\n/// Fetches a log from the node that has the corresponding `tag`. The log can be either a public or a private log, and\n/// the tag is the first field in the log's content. Returns `Option::none` if no such log exists. Throws if more than\n/// one log with that tag exists.\n/// Public logs have an extra field included at the beginning with the address of the contract that emitted them.\n// TODO(#11627): handle multiple logs with the same tag.\n// TODO(#10273): improve contract siloing of logs, don't introduce an extra field.\npub unconstrained fn get_log_by_tag(tag: Field) -> Option {\n get_log_by_tag_oracle(tag)\n}\n\n#[oracle(deliverNote)]\nunconstrained fn deliver_note_oracle(\n contract_address: AztecAddress,\n storage_slot: Field,\n nonce: Field,\n packed_note: BoundedVec,\n note_hash: Field,\n nullifier: Field,\n tx_hash: Field,\n recipient: AztecAddress,\n) -> bool {}\n\n#[oracle(getLogByTag)]\nunconstrained fn get_log_by_tag_oracle(tag: Field) -> Option {}\n" - }, - "149": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/oracle/notes.nr", - "source": "use crate::note::{\n note_interface::NoteType,\n retrieved_note::{RETRIEVED_NOTE_OVERHEAD, RetrievedNote, unpack_retrieved_note},\n};\n\nuse dep::protocol_types::{\n address::AztecAddress, indexed_tagging_secret::IndexedTaggingSecret, traits::Packable,\n};\n\n/// Notifies the simulator that a note has been created, so that it can be returned in future read requests in the same\n/// transaction. This note should only be added to the non-volatile database if found in an actual block.\npub fn notify_created_note(\n storage_slot: Field,\n note_type_id: Field,\n packed_note: [Field; N],\n note_hash: Field,\n counter: u32,\n) {\n // Safety: This oracle call returns nothing: we only call it for its side effects. It is therefore always safe\n // to call.\n unsafe {\n notify_created_note_oracle_wrapper(\n storage_slot,\n note_type_id,\n packed_note,\n note_hash,\n counter,\n )\n };\n}\n\n/// Notifies the simulator that a note has been nullified, so that it is no longer returned in future read requests in\n/// the same transaction. This note should only be removed to the non-volatile database if its nullifier is found in an\n/// actual block.\npub fn notify_nullified_note(nullifier: Field, note_hash: Field, counter: u32) {\n // Safety: This oracle call returns nothing: we only call it for its side effects. It is therefore always safe to\n // call.\n unsafe { notify_nullified_note_oracle_wrapper(nullifier, note_hash, counter) };\n}\n\n/// Notifies the simulator that a non-note nullifier has been created, so that it can be used for note nonces.\npub fn notify_created_nullifier(nullifier: Field) {\n // Safety: This oracle call returns nothing: we only call it for its side effects. It is therefore always safe to\n // call.\n unsafe { notify_created_nullifier_oracle_wrapper(nullifier) };\n}\n\nunconstrained fn notify_created_note_oracle_wrapper(\n storage_slot: Field,\n note_type_id: Field,\n packed_note: [Field; N],\n note_hash: Field,\n counter: u32,\n) {\n notify_created_note_oracle(storage_slot, note_type_id, packed_note, note_hash, counter);\n}\n\n#[oracle(notifyCreatedNote)]\nunconstrained fn notify_created_note_oracle(\n _storage_slot: Field,\n _note_type_id: Field,\n _packed_note: [Field; N],\n _note_hash: Field,\n _counter: u32,\n) {}\n\nunconstrained fn notify_nullified_note_oracle_wrapper(\n nullifier: Field,\n note_hash: Field,\n counter: u32,\n) {\n notify_nullified_note_oracle(nullifier, note_hash, counter);\n}\n\n#[oracle(notifyNullifiedNote)]\nunconstrained fn notify_nullified_note_oracle(_nullifier: Field, _note_hash: Field, _counter: u32) {}\n\nunconstrained fn notify_created_nullifier_oracle_wrapper(nullifier: Field) {\n notify_created_nullifier_oracle(nullifier);\n}\n\n#[oracle(notifyCreatedNullifier)]\nunconstrained fn notify_created_nullifier_oracle(_nullifier: Field) {}\n\n#[oracle(getNotes)]\nunconstrained fn get_notes_oracle(\n _storage_slot: Field,\n _num_selects: u8,\n _select_by_indexes: [u8; M],\n _select_by_offsets: [u8; M],\n _select_by_lengths: [u8; M],\n _select_values: [Field; M],\n _select_comparators: [u8; M],\n _sort_by_indexes: [u8; M],\n _sort_by_offsets: [u8; M],\n _sort_by_lengths: [u8; M],\n _sort_order: [u8; M],\n _limit: u32,\n _offset: u32,\n _status: u8,\n // This is always set to MAX_NOTES. We need to pass it to TS in order to correctly construct the BoundedVec\n _max_notes: u32,\n // This is always set to NOTE_PCKD_LEN + RETRIEVED_NOTE_OVERHEAD. We need to pass it to TS in order to be able to\n // correctly construct the BoundedVec there.\n _packed_retrieved_note_length: u32,\n) -> BoundedVec<[Field; NOTE_PCKD_LEN + RETRIEVED_NOTE_OVERHEAD], MAX_NOTES> {}\n\npub unconstrained fn get_notes(\n storage_slot: Field,\n num_selects: u8,\n select_by_indexes: [u8; M],\n select_by_offsets: [u8; M],\n select_by_lengths: [u8; M],\n select_values: [Field; M],\n select_comparators: [u8; M],\n sort_by_indexes: [u8; M],\n sort_by_offsets: [u8; M],\n sort_by_lengths: [u8; M],\n sort_order: [u8; M],\n limit: u32,\n offset: u32,\n status: u8,\n) -> [Option>; MAX_NOTES]\nwhere\n Note: NoteType + Packable,\n{\n // N + 3 because of the contract address, nonce, and note_hash_counter that are stored out of the packed note.\n let packed_retrieved_notes: BoundedVec<[Field; NOTE_PCKD_LEN + RETRIEVED_NOTE_OVERHEAD], MAX_NOTES> = get_notes_oracle(\n storage_slot,\n num_selects,\n select_by_indexes,\n select_by_offsets,\n select_by_lengths,\n select_values,\n select_comparators,\n sort_by_indexes,\n sort_by_offsets,\n sort_by_lengths,\n sort_order,\n limit,\n offset,\n status,\n MAX_NOTES,\n NOTE_PCKD_LEN + RETRIEVED_NOTE_OVERHEAD,\n );\n\n let mut notes = BoundedVec::<_, MAX_NOTES>::new();\n for i in 0..packed_retrieved_notes.len() {\n let retrieved_note = unpack_retrieved_note(packed_retrieved_notes.get(i));\n notes.push(retrieved_note);\n }\n\n // At last we convert the bounded vector to an array of options. We do this because that is what the filter\n // function needs to have on the output and we've decided to have the same type on the input and output of\n // the filter and preprocessor functions.\n //\n // We have decided to have the same type on the input and output of the filter and preprocessor functions because\n // it allows us to chain multiple filters and preprocessors together.\n //\n // So why do we want the array of options on the output of the filter function?\n //\n // Filter returns an array of options rather than a BoundedVec for performance reasons. Using an array of options\n // allows setting values at known indices in the output array which is much more efficient than pushing to a\n // BoundedVec where the write position depends on previous iterations. The array can then be efficiently converted\n // to a BoundedVec using utils/array/collapse.nr::collapse function from Aztec.nr. This avoids expensive dynamic\n // memory access patterns that would be required when building up a BoundedVec incrementally. For preprocessor\n // functions we could use BoundedVec return value as there the optimization does not matter since it is applied in\n // an unconstrained context. We, however, use the same return value type to be able to use the same function as\n // both a preprocessor and a filter.\n let mut notes_array = [Option::none(); MAX_NOTES];\n for i in 0..notes.len() {\n if i < notes.len() {\n notes_array[i] = Option::some(notes.get_unchecked(i));\n }\n }\n\n notes_array\n}\n\n/// Returns true if the nullifier exists. Note that a `true` value can be constrained by proving existence of the\n/// nullifier, but a `false` value should not be relied upon since other transactions may emit this nullifier before the\n/// current transaction is included in a block. While this might seem of little use at first, certain design patterns\n/// benefit from this abstraction (see e.g. `PrivateMutable`).\npub unconstrained fn check_nullifier_exists(inner_nullifier: Field) -> bool {\n check_nullifier_exists_oracle(inner_nullifier)\n}\n\n#[oracle(checkNullifierExists)]\nunconstrained fn check_nullifier_exists_oracle(_inner_nullifier: Field) -> bool {}\n\n/// Returns the derived app tagging secret ready to be included in a log for a given sender and recipient pair,\n/// siloed for the current contract address.\npub unconstrained fn get_app_tag_as_sender(sender: AztecAddress, recipient: AztecAddress) -> Field {\n get_indexed_tagging_secret_as_sender_oracle(sender, recipient).compute_tag(recipient)\n}\n\n#[oracle(getIndexedTaggingSecretAsSender)]\nunconstrained fn get_indexed_tagging_secret_as_sender_oracle(\n _sender: AztecAddress,\n _recipient: AztecAddress,\n) -> IndexedTaggingSecret {}\n\n/// Notifies the simulator that a tag has been used in a note, and to therefore increment the associated index so that\n/// future notes get a different tag and can be discovered by the recipient.\n/// This change should only be persisted in a non-volatile database if the tagged log is found in an actual block -\n/// otherwise e.g. a reverting transaction can cause the sender to accidentally skip indices and later produce notes\n/// that are not found by the recipient.\npub fn increment_app_tagging_secret_index_as_sender(sender: AztecAddress, recipient: AztecAddress) {\n // Safety: This oracle call returns nothing: we only call it for its side effects. It is therefore always safe\n // to call.\n unsafe {\n increment_app_tagging_secret_index_as_sender_wrapper(sender, recipient);\n }\n}\n\nunconstrained fn increment_app_tagging_secret_index_as_sender_wrapper(\n sender: AztecAddress,\n recipient: AztecAddress,\n) {\n increment_app_tagging_secret_index_as_sender_oracle(sender, recipient);\n}\n\n#[oracle(incrementAppTaggingSecretIndexAsSender)]\nunconstrained fn increment_app_tagging_secret_index_as_sender_oracle(\n _sender: AztecAddress,\n _recipient: AztecAddress,\n) {}\n" - }, - "151": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/oracle/shared_secret.nr", - "source": "use protocol_types::{address::aztec_address::AztecAddress, point::Point};\n\n// TODO(#12656): return an app-siloed secret + document this\n#[oracle(getSharedSecret)]\nunconstrained fn get_shared_secret_oracle(address: AztecAddress, ephPk: Point) -> Point {}\n\n/// Returns an app-siloed shared secret between `address` and someone who knows the secret key behind an\n/// ephemeral public key `ephPk`. The app-siloing means that contracts cannot retrieve secrets that belong to\n/// other contracts, and therefore cannot e.g. decrypt their messages. This is an important security consideration\n/// given that both the `address` and `ephPk` are public information.\n///\n/// The shared secret `S` is computed as:\n/// `let S = (ivsk + h) * ephPk`\n/// where `ivsk + h` is the 'preaddress' i.e. the preimage of the address, also called the address secret.\n/// TODO(#12656): app-silo this secret\npub unconstrained fn get_shared_secret(address: AztecAddress, ephPk: Point) -> Point {\n get_shared_secret_oracle(address, ephPk)\n}\n" - }, - "153": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/oracle/storage.nr", - "source": "use dep::protocol_types::{address::AztecAddress, traits::{Packable, ToField}};\n\n#[oracle(storageRead)]\nunconstrained fn storage_read_oracle(\n address: Field,\n storage_slot: Field,\n block_number: Field,\n length: Field,\n) -> [Field; N] {}\n\npub unconstrained fn raw_storage_read(\n address: AztecAddress,\n storage_slot: Field,\n block_number: u32,\n) -> [Field; N] {\n storage_read_oracle(\n address.to_field(),\n storage_slot,\n block_number as Field,\n N as Field,\n )\n}\n\npub unconstrained fn storage_read(\n address: AztecAddress,\n storage_slot: Field,\n block_number: u32,\n) -> T\nwhere\n T: Packable,\n{\n T::unpack(raw_storage_read(address, storage_slot, block_number))\n}\n\nmod tests {\n use crate::oracle::storage::{raw_storage_read, storage_read};\n use dep::protocol_types::{address::AztecAddress, traits::{FromField, Packable}};\n\n use crate::test::mocks::mock_struct::MockStruct;\n use std::test::OracleMock;\n\n global address: AztecAddress = AztecAddress::from_field(29);\n global slot: Field = 7;\n global block_number: u32 = 17;\n\n #[test]\n unconstrained fn test_raw_storage_read() {\n let written = MockStruct { a: 13, b: 42 };\n\n let _ = OracleMock::mock(\"storageRead\").returns(written.pack());\n\n let read: [Field; 2] = raw_storage_read(address, slot, block_number);\n assert_eq(read[0], 13);\n assert_eq(read[1], 42);\n }\n\n #[test]\n unconstrained fn test_storage_read() {\n let written = MockStruct { a: 13, b: 42 };\n\n let _ = OracleMock::mock(\"storageRead\").returns(written.pack());\n\n let read: MockStruct = storage_read(address, slot, block_number);\n assert_eq(read.a, 13);\n assert_eq(read.b, 42);\n }\n}\n" - }, - "155": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/state_vars/map.nr", - "source": "use crate::state_vars::storage::Storage;\nuse dep::protocol_types::{storage::map::derive_storage_slot_in_map, traits::ToField};\n\n// docs:start:map\npub struct Map {\n context: Context,\n storage_slot: Field,\n state_var_constructor: fn(Context, Field) -> V,\n}\n// docs:end:map\n\n// Map reserves a single storage slot regardless of what it stores because nothing is stored at said slot: it is only\n// used to derive the storage slots of nested state variables, which is expected to never result in collisions or slots\n// being close to one another due to these being hashes. This mirrors the strategy adopted by Solidity mappings.\nimpl Storage<1> for Map {\n fn get_storage_slot(self) -> Field {\n self.storage_slot\n }\n}\n\nimpl Map {\n // docs:start:new\n pub fn new(\n context: Context,\n storage_slot: Field,\n state_var_constructor: fn(Context, Field) -> V,\n ) -> Self {\n assert(storage_slot != 0, \"Storage slot 0 not allowed. Storage slots must start from 1.\");\n Map { context, storage_slot, state_var_constructor }\n }\n // docs:end:new\n\n // docs:start:at\n pub fn at(self, key: K) -> V\n where\n K: ToField,\n {\n // TODO(#1204): use a generator index for the storage slot\n let derived_storage_slot = derive_storage_slot_in_map(self.storage_slot, key);\n\n let state_var_constructor = self.state_var_constructor;\n state_var_constructor(self.context, derived_storage_slot)\n }\n // docs:end:at\n}\n" - }, - "16": { - "path": "std/embedded_curve_ops.nr", - "source": "use crate::cmp::Eq;\nuse crate::hash::Hash;\nuse crate::ops::arith::{Add, Neg, Sub};\n\n/// A point on the embedded elliptic curve\n/// By definition, the base field of the embedded curve is the scalar field of the proof system curve, i.e the Noir Field.\n/// x and y denotes the Weierstrass coordinates of the point, if is_infinite is false.\npub struct EmbeddedCurvePoint {\n pub x: Field,\n pub y: Field,\n pub is_infinite: bool,\n}\n\nimpl EmbeddedCurvePoint {\n /// Elliptic curve point doubling operation\n /// returns the doubled point of a point P, i.e P+P\n pub fn double(self) -> EmbeddedCurvePoint {\n embedded_curve_add(self, self)\n }\n\n /// Returns the null element of the curve; 'the point at infinity'\n pub fn point_at_infinity() -> EmbeddedCurvePoint {\n EmbeddedCurvePoint { x: 0, y: 0, is_infinite: true }\n }\n\n /// Returns the curve's generator point.\n pub fn generator() -> EmbeddedCurvePoint {\n // Generator point for the grumpkin curve (y^2 = x^3 - 17)\n EmbeddedCurvePoint {\n x: 1,\n y: 17631683881184975370165255887551781615748388533673675138860, // sqrt(-16)\n is_infinite: false,\n }\n }\n}\n\nimpl Add for EmbeddedCurvePoint {\n /// Adds two points P+Q, using the curve addition formula, and also handles point at infinity\n fn add(self, other: EmbeddedCurvePoint) -> EmbeddedCurvePoint {\n embedded_curve_add(self, other)\n }\n}\n\nimpl Sub for EmbeddedCurvePoint {\n /// Points subtraction operation, using addition and negation\n fn sub(self, other: EmbeddedCurvePoint) -> EmbeddedCurvePoint {\n self + other.neg()\n }\n}\n\nimpl Neg for EmbeddedCurvePoint {\n /// Negates a point P, i.e returns -P, by negating the y coordinate.\n /// If the point is at infinity, then the result is also at infinity.\n fn neg(self) -> EmbeddedCurvePoint {\n EmbeddedCurvePoint { x: self.x, y: -self.y, is_infinite: self.is_infinite }\n }\n}\n\nimpl Eq for EmbeddedCurvePoint {\n /// Checks whether two points are equal\n fn eq(self: Self, b: EmbeddedCurvePoint) -> bool {\n (self.is_infinite & b.is_infinite)\n | ((self.is_infinite == b.is_infinite) & (self.x == b.x) & (self.y == b.y))\n }\n}\n\nimpl Hash for EmbeddedCurvePoint {\n fn hash(self, state: &mut H)\n where\n H: crate::hash::Hasher,\n {\n if self.is_infinite {\n self.is_infinite.hash(state);\n } else {\n self.x.hash(state);\n self.y.hash(state);\n }\n }\n}\n\n/// Scalar for the embedded curve represented as low and high limbs\n/// By definition, the scalar field of the embedded curve is base field of the proving system curve.\n/// It may not fit into a Field element, so it is represented with two Field elements; its low and high limbs.\npub struct EmbeddedCurveScalar {\n pub lo: Field,\n pub hi: Field,\n}\n\nimpl EmbeddedCurveScalar {\n pub fn new(lo: Field, hi: Field) -> Self {\n EmbeddedCurveScalar { lo, hi }\n }\n\n #[field(bn254)]\n pub fn from_field(scalar: Field) -> EmbeddedCurveScalar {\n let (a, b) = crate::field::bn254::decompose(scalar);\n EmbeddedCurveScalar { lo: a, hi: b }\n }\n\n //Bytes to scalar: take the first (after the specified offset) 16 bytes of the input as the lo value, and the next 16 bytes as the hi value\n #[field(bn254)]\n pub(crate) fn from_bytes(bytes: [u8; 64], offset: u32) -> EmbeddedCurveScalar {\n let mut v = 1;\n let mut lo = 0 as Field;\n let mut hi = 0 as Field;\n for i in 0..16 {\n lo = lo + (bytes[offset + 31 - i] as Field) * v;\n hi = hi + (bytes[offset + 15 - i] as Field) * v;\n v = v * 256;\n }\n let sig_s = crate::embedded_curve_ops::EmbeddedCurveScalar { lo, hi };\n sig_s\n }\n}\n\nimpl Eq for EmbeddedCurveScalar {\n fn eq(self, other: Self) -> bool {\n (other.hi == self.hi) & (other.lo == self.lo)\n }\n}\n\nimpl Hash for EmbeddedCurveScalar {\n fn hash(self, state: &mut H)\n where\n H: crate::hash::Hasher,\n {\n self.hi.hash(state);\n self.lo.hash(state);\n }\n}\n\n// Computes a multi scalar multiplication over the embedded curve.\n// For bn254, We have Grumpkin and Baby JubJub.\n// For bls12-381, we have JubJub and Bandersnatch.\n//\n// The embedded curve being used is decided by the\n// underlying proof system.\n// docs:start:multi_scalar_mul\npub fn multi_scalar_mul(\n points: [EmbeddedCurvePoint; N],\n scalars: [EmbeddedCurveScalar; N],\n) -> EmbeddedCurvePoint\n// docs:end:multi_scalar_mul\n{\n multi_scalar_mul_array_return(points, scalars)[0]\n}\n\n#[foreign(multi_scalar_mul)]\npub(crate) fn multi_scalar_mul_array_return(\n points: [EmbeddedCurvePoint; N],\n scalars: [EmbeddedCurveScalar; N],\n) -> [EmbeddedCurvePoint; 1] {}\n\n// docs:start:fixed_base_scalar_mul\npub fn fixed_base_scalar_mul(scalar: EmbeddedCurveScalar) -> EmbeddedCurvePoint\n// docs:end:fixed_base_scalar_mul\n{\n multi_scalar_mul([EmbeddedCurvePoint::generator()], [scalar])\n}\n\n/// This function only assumes that the points are on the curve\n/// It handles corner cases around the infinity point causing some overhead compared to embedded_curve_add_not_nul and embedded_curve_add_unsafe\n// docs:start:embedded_curve_add\npub fn embedded_curve_add(\n point1: EmbeddedCurvePoint,\n point2: EmbeddedCurvePoint,\n) -> EmbeddedCurvePoint {\n // docs:end:embedded_curve_add\n if crate::runtime::is_unconstrained() {\n // `embedded_curve_add_unsafe` requires the inputs not to be the infinity point, so we check it here.\n // This is because `embedded_curve_add_unsafe` uses the `embedded_curve_add` opcode.\n // For efficiency, the backend does not check the inputs for the infinity point, but it assumes that they are not the infinity point\n // so that it can apply the ec addition formula directly.\n if point1.is_infinite {\n point2\n } else if point2.is_infinite {\n point1\n } else {\n embedded_curve_add_unsafe(point1, point2)\n }\n } else {\n // In a constrained context, we also need to check the inputs are not the infinity point because we also use `embedded_curve_add_unsafe`\n // However we also need to identify the case where the two inputs are the same, because then\n // the addition formula does not work and we need to use the doubling formula instead.\n // In unconstrained context, we can check directly if the input values are the same when solving the opcode, so it is not an issue.\n\n // x_coordinates_match is true if both abscissae are the same\n let x_coordinates_match = point1.x == point2.x;\n // y_coordinates_match is true if both ordinates are the same\n let y_coordinates_match = point1.y == point2.y;\n // double_predicate is true if both abscissae and ordinates are the same\n let double_predicate = (x_coordinates_match & y_coordinates_match);\n // If the abscissae are the same, but not the ordinates, then one point is the opposite of the other\n let infinity_predicate = (x_coordinates_match & !y_coordinates_match);\n let point1_1 = EmbeddedCurvePoint {\n x: point1.x + (x_coordinates_match as Field),\n y: point1.y,\n is_infinite: false,\n };\n let point2_1 = EmbeddedCurvePoint { x: point2.x, y: point2.y, is_infinite: false };\n // point1_1 is guaranteed to have a different abscissa than point2:\n // - if x_coordinates_match is 0, that means point1.x != point2.x, and point1_1.x = point1.x + 0\n // - if x_coordinates_match is 1, that means point1.x = point2.x, but point1_1.x = point1.x + 1 in this case\n // Because the abscissa is different, the addition formula is guaranteed to succeed, so we can safely use `embedded_curve_add_unsafe`\n // Note that this computation may be garbage: if x_coordinates_match is 1, or if one of the input is the point at infinity.\n let mut result = embedded_curve_add_unsafe(point1_1, point2_1);\n\n // `embedded_curve_add_unsafe` is doing a doubling if the input is the same variable, because in this case it is guaranteed (at 'compile time') that the input is the same.\n let double = embedded_curve_add_unsafe(point1, point1);\n // `embedded_curve_add_unsafe` would not perform doubling, even if the inputs point1 and point2 are the same, because it cannot know this without adding some logic (and some constraints)\n // However we did this logic when we computed `double_predicate`, so we set the result to 2*point1 if point1 and point2 are the same\n result = if double_predicate { double } else { result };\n\n // Same logic as above for unconstrained context, we set the proper result when one of the inputs is the infinity point\n if point1.is_infinite {\n result = point2;\n }\n if point2.is_infinite {\n result = point1;\n }\n\n // Finally, we set the is_infinity flag of the result:\n // Opposite points should sum into the infinity point, however, if one of them is point at infinity, their coordinates are not meaningful\n // so we should not use the fact that the inputs are opposite in this case:\n let mut result_is_infinity =\n infinity_predicate & (!point1.is_infinite & !point2.is_infinite);\n // However, if both of them are at infinity, then the result is also at infinity\n result.is_infinite = result_is_infinity | (point1.is_infinite & point2.is_infinite);\n result\n }\n}\n\n#[foreign(embedded_curve_add)]\nfn embedded_curve_add_array_return(\n _point1: EmbeddedCurvePoint,\n _point2: EmbeddedCurvePoint,\n) -> [EmbeddedCurvePoint; 1] {}\n\n/// This function assumes that:\n/// The points are on the curve, and\n/// The points don't share an x-coordinate, and\n/// Neither point is the infinity point.\n/// If it is used with correct input, the function ensures the correct non-zero result is returned.\n/// Except for points on the curve, the other assumptions are checked by the function. It will cause assertion failure if they are not respected.\npub fn embedded_curve_add_not_nul(\n point1: EmbeddedCurvePoint,\n point2: EmbeddedCurvePoint,\n) -> EmbeddedCurvePoint {\n assert(point1.x != point2.x);\n assert(!point1.is_infinite);\n assert(!point2.is_infinite);\n embedded_curve_add_unsafe(point1, point2)\n}\n\n/// Unsafe ec addition\n/// If the inputs are the same, it will perform a doubling, but only if point1 and point2 are the same variable.\n/// If they have the same value but are different variables, the result will be incorrect because in this case\n/// it assumes (but does not check) that the points' x-coordinates are not equal.\n/// It also assumes neither point is the infinity point.\npub fn embedded_curve_add_unsafe(\n point1: EmbeddedCurvePoint,\n point2: EmbeddedCurvePoint,\n) -> EmbeddedCurvePoint {\n embedded_curve_add_array_return(point1, point2)[0]\n}\n" - }, - "163": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable.nr", - "source": "use crate::{\n context::{PrivateContext, PublicContext, UtilityContext},\n state_vars::storage::Storage,\n utils::with_hash::WithHash,\n};\nuse dep::protocol_types::{constants::INITIALIZATION_SLOT_SEPARATOR, traits::Packable};\n\n/// Stores an immutable value in public state which can be read from public, private and unconstrained execution\n/// contexts.\n///\n/// Leverages `WithHash` to enable efficient private reads of public storage. `WithHash` wrapper allows for\n/// efficient reads by verifying large values through a single hash check and then proving inclusion only of the hash\n/// in the public storage. This reduces the number of required tree inclusion proofs from O(M) to O(1).\n///\n/// This is valuable when T packs to multiple fields, as it maintains \"almost constant\" verification overhead\n/// regardless of the original data size.\n///\n/// # Optimizing private reads in your contract\n/// Given that reading T from public immutable in private has \"almost constant\" constraints cost for different sizes\n/// of T it is recommended to group multiple values into a single struct when they are being read together. This can\n/// typically be some kind of configuration set up during contract initialization. E.g.:\n///\n/// ```noir\n/// use dep::aztec::protocol_types::{address::AztecAddress, traits::Packable};\n/// use std::meta::derive;\n///\n/// #[derive(Eq, Packable)]\n/// pub struct Config \\{\n/// pub address_1: AztecAddress,\n/// pub value_1: u128,\n/// pub value_2: u64,\n/// ...\n/// }\n/// ```\n///\n// docs:start:public_immutable_struct\npub struct PublicImmutable {\n context: Context,\n storage_slot: Field,\n}\n// docs:end:public_immutable_struct\n\n/// `WithHash` stores both the packed value (using N fields) and its hash (1 field), requiring N = M + 1 total\n/// fields.\nimpl Storage for PublicImmutable\nwhere\n WithHash: Packable,\n{\n fn get_storage_slot(self) -> Field {\n self.storage_slot\n }\n}\n\nimpl PublicImmutable {\n // docs:start:public_immutable_struct_new\n pub fn new(\n // Note: Passing the contexts to new(...) just to have an interface compatible with a Map.\n context: Context,\n storage_slot: Field,\n ) -> Self {\n assert(storage_slot != 0, \"Storage slot 0 not allowed. Storage slots must start from 1.\");\n PublicImmutable { context, storage_slot }\n }\n // docs:end:public_immutable_struct_new\n}\n\nimpl PublicImmutable {\n // docs:start:public_immutable_struct_write\n pub fn initialize(self, value: T)\n where\n T: Packable + Eq,\n {\n // We check that the struct is not yet initialized by checking if the initialization slot is 0\n let initialization_slot = INITIALIZATION_SLOT_SEPARATOR + self.storage_slot;\n let init_field: Field = self.context.storage_read(initialization_slot);\n assert(init_field == 0, \"PublicImmutable already initialized\");\n\n // We populate the initialization slot with a non-zero value to indicate that the struct is initialized\n self.context.storage_write(initialization_slot, 0xdead);\n self.context.storage_write(self.storage_slot, WithHash::new(value));\n }\n // docs:end:public_immutable_struct_write\n\n // Note that we don't access the context, but we do call oracles that are only available in public\n // docs:start:public_immutable_struct_read\n pub fn read(self) -> T\n where\n T: Packable + Eq,\n {\n WithHash::public_storage_read(*self.context, self.storage_slot)\n }\n // docs:end:public_immutable_struct_read\n}\n\nimpl PublicImmutable {\n pub unconstrained fn read(self) -> T\n where\n T: Packable + Eq,\n {\n WithHash::utility_public_storage_read(self.context, self.storage_slot)\n }\n}\n\nimpl PublicImmutable {\n pub fn read(self) -> T\n where\n T: Packable + Eq,\n {\n WithHash::historical_public_storage_read(\n self.context.get_block_header(),\n self.context.this_address(),\n self.storage_slot,\n )\n }\n}\n" - }, - "164": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/state_vars/public_mutable.nr", - "source": "use crate::context::{PublicContext, UtilityContext};\nuse crate::state_vars::storage::Storage;\nuse dep::protocol_types::traits::Packable;\n\n// docs:start:public_mutable_struct\npub struct PublicMutable {\n context: Context,\n storage_slot: Field,\n}\n// docs:end:public_mutable_struct\n\nimpl Storage for PublicMutable\nwhere\n T: Packable,\n{\n fn get_storage_slot(self) -> Field {\n self.storage_slot\n }\n}\n\nimpl PublicMutable {\n // docs:start:public_mutable_struct_new\n pub fn new(\n // Note: Passing the contexts to new(...) just to have an interface compatible with a Map.\n context: Context,\n storage_slot: Field,\n ) -> Self {\n assert(storage_slot != 0, \"Storage slot 0 not allowed. Storage slots must start from 1.\");\n PublicMutable { context, storage_slot }\n }\n // docs:end:public_mutable_struct_new\n}\n\nimpl PublicMutable {\n // docs:start:public_mutable_struct_read\n pub fn read(self) -> T\n where\n T: Packable,\n {\n self.context.storage_read(self.storage_slot)\n }\n // docs:end:public_mutable_struct_read\n\n // docs:start:public_mutable_struct_write\n pub fn write(self, value: T)\n where\n T: Packable,\n {\n self.context.storage_write(self.storage_slot, value);\n }\n // docs:end:public_mutable_struct_write\n}\n\nimpl PublicMutable {\n pub unconstrained fn read(self) -> T\n where\n T: Packable,\n {\n self.context.storage_read(self.storage_slot)\n }\n}\n" - }, - "17": { - "path": "std/field/bn254.nr", - "source": "use crate::field::field_less_than;\nuse crate::runtime::is_unconstrained;\n\n// The low and high decomposition of the field modulus\nglobal PLO: Field = 53438638232309528389504892708671455233;\nglobal PHI: Field = 64323764613183177041862057485226039389;\n\npub(crate) global TWO_POW_128: Field = 0x100000000000000000000000000000000;\n\n// Decomposes a single field into two 16 byte fields.\nfn compute_decomposition(x: Field) -> (Field, Field) {\n // Here's we're taking advantage of truncating 128 bit limbs from the input field\n // and then subtracting them from the input such the field division is equivalent to integer division.\n let low = (x as u128) as Field;\n let high = (x - low) / TWO_POW_128;\n\n (low, high)\n}\n\npub(crate) unconstrained fn decompose_hint(x: Field) -> (Field, Field) {\n compute_decomposition(x)\n}\n\nunconstrained fn lte_hint(x: Field, y: Field) -> bool {\n if x == y {\n true\n } else {\n field_less_than(x, y)\n }\n}\n\n// Assert that (alo > blo && ahi >= bhi) || (alo <= blo && ahi > bhi)\nfn assert_gt_limbs(a: (Field, Field), b: (Field, Field)) {\n let (alo, ahi) = a;\n let (blo, bhi) = b;\n // Safety: borrow is enforced to be boolean due to its type.\n // if borrow is 0, it asserts that (alo > blo && ahi >= bhi)\n // if borrow is 1, it asserts that (alo <= blo && ahi > bhi)\n unsafe {\n let borrow = lte_hint(alo, blo);\n\n let rlo = alo - blo - 1 + (borrow as Field) * TWO_POW_128;\n let rhi = ahi - bhi - (borrow as Field);\n\n rlo.assert_max_bit_size::<128>();\n rhi.assert_max_bit_size::<128>();\n }\n}\n\n/// Decompose a single field into two 16 byte fields.\npub fn decompose(x: Field) -> (Field, Field) {\n if is_unconstrained() {\n compute_decomposition(x)\n } else {\n // Safety: decomposition is properly checked below\n unsafe {\n // Take hints of the decomposition\n let (xlo, xhi) = decompose_hint(x);\n\n // Range check the limbs\n xlo.assert_max_bit_size::<128>();\n xhi.assert_max_bit_size::<128>();\n\n // Check that the decomposition is correct\n assert_eq(x, xlo + TWO_POW_128 * xhi);\n\n // Assert that the decomposition of P is greater than the decomposition of x\n assert_gt_limbs((PLO, PHI), (xlo, xhi));\n (xlo, xhi)\n }\n }\n}\n\npub fn assert_gt(a: Field, b: Field) {\n if is_unconstrained() {\n assert(\n // Safety: already unconstrained\n unsafe { field_less_than(b, a) },\n );\n } else {\n // Decompose a and b\n let a_limbs = decompose(a);\n let b_limbs = decompose(b);\n\n // Assert that a_limbs is greater than b_limbs\n assert_gt_limbs(a_limbs, b_limbs)\n }\n}\n\npub fn assert_lt(a: Field, b: Field) {\n assert_gt(b, a);\n}\n\npub fn gt(a: Field, b: Field) -> bool {\n if is_unconstrained() {\n // Safety: unsafe in unconstrained\n unsafe {\n field_less_than(b, a)\n }\n } else if a == b {\n false\n } else {\n // Safety: Take a hint of the comparison and verify it\n unsafe {\n if field_less_than(a, b) {\n assert_gt(b, a);\n false\n } else {\n assert_gt(a, b);\n true\n }\n }\n }\n}\n\npub fn lt(a: Field, b: Field) -> bool {\n gt(b, a)\n}\n\nmod tests {\n // TODO: Allow imports from \"super\"\n use crate::field::bn254::{assert_gt, decompose, gt, lte_hint, PHI, PLO, TWO_POW_128};\n\n #[test]\n fn check_decompose() {\n assert_eq(decompose(TWO_POW_128), (0, 1));\n assert_eq(decompose(TWO_POW_128 + 0x1234567890), (0x1234567890, 1));\n assert_eq(decompose(0x1234567890), (0x1234567890, 0));\n }\n\n #[test]\n unconstrained fn check_decompose_unconstrained() {\n assert_eq(decompose(TWO_POW_128), (0, 1));\n assert_eq(decompose(TWO_POW_128 + 0x1234567890), (0x1234567890, 1));\n assert_eq(decompose(0x1234567890), (0x1234567890, 0));\n }\n\n #[test]\n unconstrained fn check_lte_hint() {\n assert(lte_hint(0, 1));\n assert(lte_hint(0, 0x100));\n assert(lte_hint(0x100, TWO_POW_128 - 1));\n assert(!lte_hint(0 - 1, 0));\n\n assert(lte_hint(0, 0));\n assert(lte_hint(0x100, 0x100));\n assert(lte_hint(0 - 1, 0 - 1));\n }\n\n #[test]\n fn check_assert_gt() {\n assert_gt(1, 0);\n assert_gt(0x100, 0);\n assert_gt((0 - 1), (0 - 2));\n assert_gt(TWO_POW_128, 0);\n assert_gt(0 - 1, 0);\n }\n\n #[test]\n unconstrained fn check_assert_gt_unconstrained() {\n assert_gt(1, 0);\n assert_gt(0x100, 0);\n assert_gt((0 - 1), (0 - 2));\n assert_gt(TWO_POW_128, 0);\n assert_gt(0 - 1, 0);\n }\n\n #[test]\n fn check_gt() {\n assert(gt(1, 0));\n assert(gt(0x100, 0));\n assert(gt((0 - 1), (0 - 2)));\n assert(gt(TWO_POW_128, 0));\n assert(!gt(0, 0));\n assert(!gt(0, 0x100));\n assert(gt(0 - 1, 0 - 2));\n assert(!gt(0 - 2, 0 - 1));\n }\n\n #[test]\n unconstrained fn check_gt_unconstrained() {\n assert(gt(1, 0));\n assert(gt(0x100, 0));\n assert(gt((0 - 1), (0 - 2)));\n assert(gt(TWO_POW_128, 0));\n assert(!gt(0, 0));\n assert(!gt(0, 0x100));\n assert(gt(0 - 1, 0 - 2));\n assert(!gt(0 - 2, 0 - 1));\n }\n\n #[test]\n fn check_plo_phi() {\n assert_eq(PLO + PHI * TWO_POW_128, 0);\n let p_bytes = crate::field::modulus_le_bytes();\n let mut p_low: Field = 0;\n let mut p_high: Field = 0;\n\n let mut offset = 1;\n for i in 0..16 {\n p_low += (p_bytes[i] as Field) * offset;\n p_high += (p_bytes[i + 16] as Field) * offset;\n offset *= 256;\n }\n assert_eq(p_low, PLO);\n assert_eq(p_high, PHI);\n }\n}\n" - }, - "178": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/utils/array/append.nr", - "source": "/// Appends two `BoundedVec`s together, returning one that contains all of the elements of the first one followed by all\n/// of the elements of the second one. The resulting `BoundedVec` can have any arbitrary maximum length, but it must be\n/// large enough to fit all of the elements of both the first and second vectors.\npub fn append(\n a: BoundedVec,\n b: BoundedVec,\n) -> BoundedVec {\n let mut dst = BoundedVec::new();\n\n dst.extend_from_bounded_vec(a);\n dst.extend_from_bounded_vec(b);\n\n dst\n}\n\nmod test {\n use super::append;\n\n #[test]\n unconstrained fn append_empty_vecs() {\n let a: BoundedVec<_, 3> = BoundedVec::new();\n let b: BoundedVec<_, 14> = BoundedVec::new();\n\n let result: BoundedVec = append(a, b);\n\n assert_eq(result.len(), 0);\n assert_eq(result.storage(), std::mem::zeroed());\n }\n\n #[test]\n unconstrained fn append_non_empty_vecs() {\n let a: BoundedVec<_, 3> = BoundedVec::from_array([1, 2, 3]);\n let b: BoundedVec<_, 14> = BoundedVec::from_array([4, 5, 6]);\n\n let result: BoundedVec = append(a, b);\n\n assert_eq(result.len(), 6);\n assert_eq(result.storage(), [1, 2, 3, 4, 5, 6, std::mem::zeroed(), std::mem::zeroed()]);\n }\n\n #[test(should_fail_with = \"out of bounds\")]\n unconstrained fn append_non_empty_vecs_insufficient_max_len() {\n let a: BoundedVec<_, 3> = BoundedVec::from_array([1, 2, 3]);\n let b: BoundedVec<_, 14> = BoundedVec::from_array([4, 5, 6]);\n\n let _: BoundedVec = append(a, b);\n }\n}\n" - }, - "18": { - "path": "std/field/mod.nr", - "source": "pub mod bn254;\nuse crate::{runtime::is_unconstrained, static_assert};\nuse bn254::lt as bn254_lt;\n\nimpl Field {\n /// Asserts that `self` can be represented in `bit_size` bits.\n ///\n /// # Failures\n /// Causes a constraint failure for `Field` values exceeding `2^{bit_size}`.\n // docs:start:assert_max_bit_size\n pub fn assert_max_bit_size(self) {\n // docs:end:assert_max_bit_size\n static_assert(\n BIT_SIZE < modulus_num_bits() as u32,\n \"BIT_SIZE must be less than modulus_num_bits\",\n );\n self.__assert_max_bit_size(BIT_SIZE);\n }\n\n #[builtin(apply_range_constraint)]\n fn __assert_max_bit_size(self, bit_size: u32) {}\n\n /// Decomposes `self` into its little endian bit decomposition as a `[u1; N]` array.\n /// This slice will be zero padded should not all bits be necessary to represent `self`.\n ///\n /// # Failures\n /// Causes a constraint failure for `Field` values exceeding `2^N` as the resulting slice will not\n /// be able to represent the original `Field`.\n ///\n /// # Safety\n /// Values of `N` equal to or greater than the number of bits necessary to represent the `Field` modulus\n /// (e.g. 254 for the BN254 field) allow for multiple bit decompositions. This is due to how the `Field` will\n /// wrap around due to overflow when verifying the decomposition.\n #[builtin(to_le_bits)]\n fn _to_le_bits(self: Self) -> [u1; N] {}\n\n /// Decomposes `self` into its big endian bit decomposition as a `[u1; N]` array.\n /// This array will be zero padded should not all bits be necessary to represent `self`.\n ///\n /// # Failures\n /// Causes a constraint failure for `Field` values exceeding `2^N` as the resulting slice will not\n /// be able to represent the original `Field`.\n ///\n /// # Safety\n /// Values of `N` equal to or greater than the number of bits necessary to represent the `Field` modulus\n /// (e.g. 254 for the BN254 field) allow for multiple bit decompositions. This is due to how the `Field` will\n /// wrap around due to overflow when verifying the decomposition.\n #[builtin(to_be_bits)]\n fn _to_be_bits(self: Self) -> [u1; N] {}\n\n /// Decomposes `self` into its little endian bit decomposition as a `[u1; N]` array.\n /// This slice will be zero padded should not all bits be necessary to represent `self`.\n ///\n /// # Failures\n /// Causes a constraint failure for `Field` values exceeding `2^N` as the resulting slice will not\n /// be able to represent the original `Field`.\n ///\n /// # Safety\n /// The bit decomposition returned is canonical and is guaranteed to not overflow the modulus.\n // docs:start:to_le_bits\n pub fn to_le_bits(self: Self) -> [u1; N] {\n // docs:end:to_le_bits\n let bits = self._to_le_bits();\n\n if !is_unconstrained() {\n // Ensure that the byte decomposition does not overflow the modulus\n let p = modulus_le_bits();\n assert(bits.len() <= p.len());\n let mut ok = bits.len() != p.len();\n for i in 0..N {\n if !ok {\n if (bits[N - 1 - i] != p[N - 1 - i]) {\n assert(p[N - 1 - i] == 1);\n ok = true;\n }\n }\n }\n assert(ok);\n }\n bits\n }\n\n /// Decomposes `self` into its big endian bit decomposition as a `[u1; N]` array.\n /// This array will be zero padded should not all bits be necessary to represent `self`.\n ///\n /// # Failures\n /// Causes a constraint failure for `Field` values exceeding `2^N` as the resulting slice will not\n /// be able to represent the original `Field`.\n ///\n /// # Safety\n /// The bit decomposition returned is canonical and is guaranteed to not overflow the modulus.\n // docs:start:to_be_bits\n pub fn to_be_bits(self: Self) -> [u1; N] {\n // docs:end:to_be_bits\n let bits = self._to_be_bits();\n\n if !is_unconstrained() {\n // Ensure that the decomposition does not overflow the modulus\n let p = modulus_be_bits();\n assert(bits.len() <= p.len());\n let mut ok = bits.len() != p.len();\n for i in 0..N {\n if !ok {\n if (bits[i] != p[i]) {\n assert(p[i] == 1);\n ok = true;\n }\n }\n }\n assert(ok);\n }\n bits\n }\n\n /// Decomposes `self` into its little endian byte decomposition as a `[u8;N]` array\n /// This array will be zero padded should not all bytes be necessary to represent `self`.\n ///\n /// # Failures\n /// The length N of the array must be big enough to contain all the bytes of the 'self',\n /// and no more than the number of bytes required to represent the field modulus\n ///\n /// # Safety\n /// The result is ensured to be the canonical decomposition of the field element\n // docs:start:to_le_bytes\n pub fn to_le_bytes(self: Self) -> [u8; N] {\n // docs:end:to_le_bytes\n static_assert(\n N <= modulus_le_bytes().len(),\n \"N must be less than or equal to modulus_le_bytes().len()\",\n );\n // Compute the byte decomposition\n let bytes = self.to_le_radix(256);\n\n if !is_unconstrained() {\n // Ensure that the byte decomposition does not overflow the modulus\n let p = modulus_le_bytes();\n assert(bytes.len() <= p.len());\n let mut ok = bytes.len() != p.len();\n for i in 0..N {\n if !ok {\n if (bytes[N - 1 - i] != p[N - 1 - i]) {\n assert(bytes[N - 1 - i] < p[N - 1 - i]);\n ok = true;\n }\n }\n }\n assert(ok);\n }\n bytes\n }\n\n /// Decomposes `self` into its big endian byte decomposition as a `[u8;N]` array of length required to represent the field modulus\n /// This array will be zero padded should not all bytes be necessary to represent `self`.\n ///\n /// # Failures\n /// The length N of the array must be big enough to contain all the bytes of the 'self',\n /// and no more than the number of bytes required to represent the field modulus\n ///\n /// # Safety\n /// The result is ensured to be the canonical decomposition of the field element\n // docs:start:to_be_bytes\n pub fn to_be_bytes(self: Self) -> [u8; N] {\n // docs:end:to_be_bytes\n static_assert(\n N <= modulus_le_bytes().len(),\n \"N must be less than or equal to modulus_le_bytes().len()\",\n );\n // Compute the byte decomposition\n let bytes = self.to_be_radix(256);\n\n if !is_unconstrained() {\n // Ensure that the byte decomposition does not overflow the modulus\n let p = modulus_be_bytes();\n assert(bytes.len() <= p.len());\n let mut ok = bytes.len() != p.len();\n for i in 0..N {\n if !ok {\n if (bytes[i] != p[i]) {\n assert(bytes[i] < p[i]);\n ok = true;\n }\n }\n }\n assert(ok);\n }\n bytes\n }\n\n // docs:start:to_le_radix\n pub fn to_le_radix(self: Self, radix: u32) -> [u8; N] {\n // Brillig does not need an immediate radix\n if !crate::runtime::is_unconstrained() {\n static_assert(1 < radix, \"radix must be greater than 1\");\n static_assert(radix <= 256, \"radix must be less than or equal to 256\");\n static_assert(radix & (radix - 1) == 0, \"radix must be a power of 2\");\n }\n self.__to_le_radix(radix)\n }\n // docs:end:to_le_radix\n\n // docs:start:to_be_radix\n pub fn to_be_radix(self: Self, radix: u32) -> [u8; N] {\n // Brillig does not need an immediate radix\n if !crate::runtime::is_unconstrained() {\n crate::assert_constant(radix);\n }\n self.__to_be_radix(radix)\n }\n // docs:end:to_be_radix\n\n // `_radix` must be less than 256\n #[builtin(to_le_radix)]\n fn __to_le_radix(self, radix: u32) -> [u8; N] {}\n\n // `_radix` must be less than 256\n #[builtin(to_be_radix)]\n fn __to_be_radix(self, radix: u32) -> [u8; N] {}\n\n // Returns self to the power of the given exponent value.\n // Caution: we assume the exponent fits into 32 bits\n // using a bigger bit size impacts negatively the performance and should be done only if the exponent does not fit in 32 bits\n pub fn pow_32(self, exponent: Field) -> Field {\n let mut r: Field = 1;\n let b: [u1; 32] = exponent.to_le_bits();\n\n for i in 1..33 {\n r *= r;\n r = (b[32 - i] as Field) * (r * self) + (1 - b[32 - i] as Field) * r;\n }\n r\n }\n\n // Parity of (prime) Field element, i.e. sgn0(x mod p) = 0 if x `elem` {0, ..., p-1} is even, otherwise sgn0(x mod p) = 1.\n pub fn sgn0(self) -> u1 {\n self as u1\n }\n\n pub fn lt(self, another: Field) -> bool {\n if crate::compat::is_bn254() {\n bn254_lt(self, another)\n } else {\n lt_fallback(self, another)\n }\n }\n\n /// Convert a little endian byte array to a field element.\n /// If the provided byte array overflows the field modulus then the Field will silently wrap around.\n pub fn from_le_bytes(bytes: [u8; N]) -> Field {\n static_assert(\n N <= modulus_le_bytes().len(),\n \"N must be less than or equal to modulus_le_bytes().len()\",\n );\n let mut v = 1;\n let mut result = 0;\n\n for i in 0..N {\n result += (bytes[i] as Field) * v;\n v = v * 256;\n }\n result\n }\n\n /// Convert a big endian byte array to a field element.\n /// If the provided byte array overflows the field modulus then the Field will silently wrap around.\n pub fn from_be_bytes(bytes: [u8; N]) -> Field {\n let mut v = 1;\n let mut result = 0;\n\n for i in 0..N {\n result += (bytes[N - 1 - i] as Field) * v;\n v = v * 256;\n }\n result\n }\n}\n\n#[builtin(modulus_num_bits)]\npub comptime fn modulus_num_bits() -> u64 {}\n\n#[builtin(modulus_be_bits)]\npub comptime fn modulus_be_bits() -> [u1] {}\n\n#[builtin(modulus_le_bits)]\npub comptime fn modulus_le_bits() -> [u1] {}\n\n#[builtin(modulus_be_bytes)]\npub comptime fn modulus_be_bytes() -> [u8] {}\n\n#[builtin(modulus_le_bytes)]\npub comptime fn modulus_le_bytes() -> [u8] {}\n\n/// An unconstrained only built in to efficiently compare fields.\n#[builtin(field_less_than)]\nunconstrained fn __field_less_than(x: Field, y: Field) -> bool {}\n\npub(crate) unconstrained fn field_less_than(x: Field, y: Field) -> bool {\n __field_less_than(x, y)\n}\n\n// Convert a 32 byte array to a field element by modding\npub fn bytes32_to_field(bytes32: [u8; 32]) -> Field {\n // Convert it to a field element\n let mut v = 1;\n let mut high = 0 as Field;\n let mut low = 0 as Field;\n\n for i in 0..16 {\n high = high + (bytes32[15 - i] as Field) * v;\n low = low + (bytes32[16 + 15 - i] as Field) * v;\n v = v * 256;\n }\n // Abuse that a % p + b % p = (a + b) % p and that low < p\n low + high * v\n}\n\nfn lt_fallback(x: Field, y: Field) -> bool {\n if is_unconstrained() {\n // Safety: unconstrained context\n unsafe {\n field_less_than(x, y)\n }\n } else {\n let x_bytes: [u8; 32] = x.to_le_bytes();\n let y_bytes: [u8; 32] = y.to_le_bytes();\n let mut x_is_lt = false;\n let mut done = false;\n for i in 0..32 {\n if (!done) {\n let x_byte = x_bytes[32 - 1 - i] as u8;\n let y_byte = y_bytes[32 - 1 - i] as u8;\n let bytes_match = x_byte == y_byte;\n if !bytes_match {\n x_is_lt = x_byte < y_byte;\n done = true;\n }\n }\n }\n x_is_lt\n }\n}\n\nmod tests {\n use crate::{panic::panic, runtime};\n use super::field_less_than;\n\n #[test]\n // docs:start:to_be_bits_example\n fn test_to_be_bits() {\n let field = 2;\n let bits: [u1; 8] = field.to_be_bits();\n assert_eq(bits, [0, 0, 0, 0, 0, 0, 1, 0]);\n }\n // docs:end:to_be_bits_example\n\n #[test]\n // docs:start:to_le_bits_example\n fn test_to_le_bits() {\n let field = 2;\n let bits: [u1; 8] = field.to_le_bits();\n assert_eq(bits, [0, 1, 0, 0, 0, 0, 0, 0]);\n }\n // docs:end:to_le_bits_example\n\n #[test]\n // docs:start:to_be_bytes_example\n fn test_to_be_bytes() {\n let field = 2;\n let bytes: [u8; 8] = field.to_be_bytes();\n assert_eq(bytes, [0, 0, 0, 0, 0, 0, 0, 2]);\n assert_eq(Field::from_be_bytes::<8>(bytes), field);\n }\n // docs:end:to_be_bytes_example\n\n #[test]\n // docs:start:to_le_bytes_example\n fn test_to_le_bytes() {\n let field = 2;\n let bytes: [u8; 8] = field.to_le_bytes();\n assert_eq(bytes, [2, 0, 0, 0, 0, 0, 0, 0]);\n assert_eq(Field::from_le_bytes::<8>(bytes), field);\n }\n // docs:end:to_le_bytes_example\n\n #[test]\n // docs:start:to_be_radix_example\n fn test_to_be_radix() {\n // 259, in base 256, big endian, is [1, 3].\n // i.e. 3 * 256^0 + 1 * 256^1\n let field = 259;\n\n // The radix (in this example, 256) must be a power of 2.\n // The length of the returned byte array can be specified to be\n // >= the amount of space needed.\n let bytes: [u8; 8] = field.to_be_radix(256);\n assert_eq(bytes, [0, 0, 0, 0, 0, 0, 1, 3]);\n assert_eq(Field::from_be_bytes::<8>(bytes), field);\n }\n // docs:end:to_be_radix_example\n\n #[test]\n // docs:start:to_le_radix_example\n fn test_to_le_radix() {\n // 259, in base 256, little endian, is [3, 1].\n // i.e. 3 * 256^0 + 1 * 256^1\n let field = 259;\n\n // The radix (in this example, 256) must be a power of 2.\n // The length of the returned byte array can be specified to be\n // >= the amount of space needed.\n let bytes: [u8; 8] = field.to_le_radix(256);\n assert_eq(bytes, [3, 1, 0, 0, 0, 0, 0, 0]);\n assert_eq(Field::from_le_bytes::<8>(bytes), field);\n }\n // docs:end:to_le_radix_example\n\n #[test(should_fail_with = \"radix must be greater than 1\")]\n fn test_to_le_radix_1() {\n // this test should only fail in constrained mode\n if !runtime::is_unconstrained() {\n let field = 2;\n let _: [u8; 8] = field.to_le_radix(1);\n } else {\n panic(f\"radix must be greater than 1\");\n }\n }\n\n // TODO: Update this test to account for the Brillig restriction that the radix must be greater than 2\n //#[test]\n //fn test_to_le_radix_brillig_1() {\n // // this test should only fail in constrained mode\n // if runtime::is_unconstrained() {\n // let field = 1;\n // let out: [u8; 8] = field.to_le_radix(1);\n // crate::println(out);\n // let expected = [0; 8];\n // assert(out == expected, \"unexpected result\");\n // }\n //}\n\n #[test(should_fail_with = \"radix must be a power of 2\")]\n fn test_to_le_radix_3() {\n // this test should only fail in constrained mode\n if !runtime::is_unconstrained() {\n let field = 2;\n let _: [u8; 8] = field.to_le_radix(3);\n } else {\n panic(f\"radix must be a power of 2\");\n }\n }\n\n #[test]\n fn test_to_le_radix_brillig_3() {\n // this test should only fail in constrained mode\n if runtime::is_unconstrained() {\n let field = 1;\n let out: [u8; 8] = field.to_le_radix(3);\n let mut expected = [0; 8];\n expected[0] = 1;\n assert(out == expected, \"unexpected result\");\n }\n }\n\n #[test(should_fail_with = \"radix must be less than or equal to 256\")]\n fn test_to_le_radix_512() {\n // this test should only fail in constrained mode\n if !runtime::is_unconstrained() {\n let field = 2;\n let _: [u8; 8] = field.to_le_radix(512);\n } else {\n panic(f\"radix must be less than or equal to 256\")\n }\n }\n\n // TODO: Update this test to account for the Brillig restriction that the radix must be less than 512\n //#[test]\n //fn test_to_le_radix_brillig_512() {\n // // this test should only fail in constrained mode\n // if runtime::is_unconstrained() {\n // let field = 1;\n // let out: [u8; 8] = field.to_le_radix(512);\n // let mut expected = [0; 8];\n // expected[0] = 1;\n // assert(out == expected, \"unexpected result\");\n // }\n //}\n\n #[test]\n unconstrained fn test_field_less_than() {\n assert(field_less_than(0, 1));\n assert(field_less_than(0, 0x100));\n assert(field_less_than(0x100, 0 - 1));\n assert(!field_less_than(0 - 1, 0));\n }\n}\n" - }, - "181": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/utils/array/subarray.nr", - "source": "/// Returns `DST_LEN` elements from a source array, starting at `offset`. `DST_LEN` must not be larger than the number\n/// of elements past `offset`.\n///\n/// Examples:\n/// ```\n/// let foo: [Field; 2] = subarray([1, 2, 3, 4, 5], 2);\n/// assert_eq(foo, [3, 4]);\n///\n/// let bar: [Field; 5] = subarray([1, 2, 3, 4, 5], 2); // fails - we can't return 5 elements since only 3 remain\n/// ```\npub fn subarray(\n src: [T; SRC_LEN],\n offset: u32,\n) -> [T; DST_LEN] {\n assert(offset + DST_LEN <= SRC_LEN, \"DST_LEN too large for offset\");\n\n let mut dst: [T; DST_LEN] = std::mem::zeroed();\n for i in 0..DST_LEN {\n dst[i] = src[i + offset];\n }\n\n dst\n}\n\nmod test {\n use super::subarray;\n\n #[test]\n unconstrained fn subarray_into_empty() {\n // In all of these cases we're setting DST_LEN to be 0, so we always get back an emtpy array.\n assert_eq(subarray::([], 0), []);\n assert_eq(subarray([1, 2, 3, 4, 5], 0), []);\n assert_eq(subarray([1, 2, 3, 4, 5], 2), []);\n }\n\n #[test]\n unconstrained fn subarray_complete() {\n assert_eq(subarray::([], 0), []);\n assert_eq(subarray([1, 2, 3, 4, 5], 0), [1, 2, 3, 4, 5]);\n }\n\n #[test]\n unconstrained fn subarray_different_end_sizes() {\n // We implicitly select how many values to read in the size of the return array\n assert_eq(subarray([1, 2, 3, 4, 5], 1), [2, 3, 4, 5]);\n assert_eq(subarray([1, 2, 3, 4, 5], 1), [2, 3, 4]);\n assert_eq(subarray([1, 2, 3, 4, 5], 1), [2, 3]);\n assert_eq(subarray([1, 2, 3, 4, 5], 1), [2]);\n }\n\n #[test(should_fail_with = \"DST_LEN too large for offset\")]\n unconstrained fn subarray_offset_too_large() {\n // With an offset of 1 we can only request up to 4 elements\n let _: [_; 5] = subarray([1, 2, 3, 4, 5], 1);\n }\n\n #[test(should_fail)]\n unconstrained fn subarray_bad_return_value() {\n assert_eq(subarray([1, 2, 3, 4, 5], 1), [3, 3, 4, 5]);\n }\n}\n" - }, - "182": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/utils/array/subbvec.nr", - "source": "use crate::utils::array;\n\n/// Returns `DST_MAX_LEN` elements from a source BoundedVec, starting at `offset`. `offset` must not be larger than the\n/// original length, and `DST_LEN` must not be larger than the total number of elements past `offset` (including the\n/// zeroed elements past `len()`).\n///\n/// Only elements at the beginning of the vector can be removed: it is not possible to also remove elements at the end\n/// of the vector by passing a value for `DST_LEN` that is smaller than `len() - offset`.\n///\n/// Examples:\n/// ```\n/// let foo = BoundedVec::<_, 10>::from_array([1, 2, 3, 4, 5]);\n/// assert_eq(subbvec(foo, 2), BoundedVec::<_, 8>::from_array([3, 4, 5]));\n///\n/// let bar: BoundedVec<_, 1> = subbvec(foo, 2); // fails - we can't return just 1 element since 3 remain\n/// let baz: BoundedVec<_, 10> = subbvec(foo, 3); // fails - we can't return 10 elements since only 7 remain\n/// ```\npub fn subbvec(\n bvec: BoundedVec,\n offset: u32,\n) -> BoundedVec {\n // from_parts_unchecked does not verify that the elements past len are zeroed, but that is not an issue in our case\n // because we're constructing the new storage array as a subarray of the original one (which should have zeroed\n // storage past len), guaranteeing correctness. This is because `subarray` does not allow extending arrays past\n // their original length.\n BoundedVec::from_parts_unchecked(array::subarray(bvec.storage(), offset), bvec.len() - offset)\n}\n\nmod test {\n use super::subbvec;\n\n #[test]\n unconstrained fn subbvec_empty() {\n let bvec = BoundedVec::::from_array([]);\n assert_eq(subbvec(bvec, 0), bvec);\n }\n\n #[test]\n unconstrained fn subbvec_complete() {\n let bvec = BoundedVec::<_, 10>::from_array([1, 2, 3, 4, 5]);\n assert_eq(subbvec(bvec, 0), bvec);\n\n let smaller_capacity = BoundedVec::<_, 5>::from_array([1, 2, 3, 4, 5]);\n assert_eq(subbvec(bvec, 0), smaller_capacity);\n }\n\n #[test]\n unconstrained fn subbvec_partial() {\n let bvec = BoundedVec::<_, 10>::from_array([1, 2, 3, 4, 5]);\n\n assert_eq(subbvec(bvec, 2), BoundedVec::<_, 8>::from_array([3, 4, 5]));\n assert_eq(subbvec(bvec, 2), BoundedVec::<_, 3>::from_array([3, 4, 5]));\n }\n\n #[test]\n unconstrained fn subbvec_into_empty() {\n let bvec: BoundedVec<_, 10> = BoundedVec::from_array([1, 2, 3, 4, 5]);\n assert_eq(subbvec(bvec, 5), BoundedVec::<_, 5>::from_array([]));\n }\n\n #[test(should_fail)]\n unconstrained fn subbvec_offset_past_len() {\n let bvec = BoundedVec::<_, 10>::from_array([1, 2, 3, 4, 5]);\n let _: BoundedVec<_, 1> = subbvec(bvec, 6);\n }\n\n #[test(should_fail)]\n unconstrained fn subbvec_insufficient_dst_len() {\n let bvec = BoundedVec::<_, 10>::from_array([1, 2, 3, 4, 5]);\n\n // We're not providing enough space to hold all of the items inside the original BoundedVec. subbvec can cause\n // for the capacity to reduce, but not the length (other than by len - offset).\n let _: BoundedVec<_, 1> = subbvec(bvec, 2);\n }\n\n #[test(should_fail_with = \"DST_LEN too large for offset\")]\n unconstrained fn subbvec_dst_len_causes_enlarge() {\n let bvec = BoundedVec::<_, 10>::from_array([1, 2, 3, 4, 5]);\n\n // subbvec does not supprt capacity increases\n let _: BoundedVec<_, 11> = subbvec(bvec, 0);\n }\n\n #[test(should_fail_with = \"DST_LEN too large for offset\")]\n unconstrained fn subbvec_dst_len_too_large_for_offset() {\n let bvec = BoundedVec::<_, 10>::from_array([1, 2, 3, 4, 5]);\n\n // This effectively requests a capacity increase, since there'd be just one element plus the 5 empty slots,\n // which is less than 7.\n let _: BoundedVec<_, 7> = subbvec(bvec, 4);\n }\n}\n" - }, - "184": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/utils/conversion/bytes_to_fields.nr", - "source": "use std::static_assert;\n\n// These functions are used to facilitate the conversion of log ciphertext between byte and field representations.\n//\n// `bytes_to_fields` uses fixed-size arrays since encryption contexts have compile-time size information.\n// `bytes_from_fields` uses BoundedVec for flexibility in unconstrained contexts where sizes are dynamic.\n//\n// Together they provide bidirectional conversion between bytes and fields when processing encrypted logs.\n\n/// Converts the input bytes into an array of fields. A Field is ~254 bits meaning that each field can store 31 whole\n/// bytes. Use `bytes_from_fields` to obtain the original bytes array.\n///\n/// The input bytes are chunked into chunks of 31 bytes. Each 31-byte chunk is viewed as big-endian, and is converted\n/// into a Field.\n/// For example, [1, 10, 3, ..., 0] (31 bytes) is encoded as [1 * 256^30 + 10 * 256^29 + 3 * 256^28 + ... + 0]\n/// Note: N must be a multiple of 31 bytes\npub fn bytes_to_fields(bytes: [u8; N]) -> [Field; N / 31] {\n // Assert that N is a multiple of 31\n static_assert(N % 31 == 0, \"N must be a multiple of 31\");\n\n let mut fields = [0; N / 31];\n\n // Since N is a multiple of 31, we can simply process all chunks fully\n for i in 0..N / 31 {\n let mut field = 0;\n for j in 0..31 {\n // Shift the existing value left by 8 bits and add the new byte\n field = field * 256 + bytes[i * 31 + j] as Field;\n }\n fields[i] = field;\n }\n\n fields\n}\n\n/// Converts an input BoundedVec of fields into a BoundedVec of bytes in big-endian order. Arbitrary Field arrays\n/// are not allowed: this is assumed to be an array obtained via `bytes_to_fields`, i.e. one that actually represents\n/// bytes. To convert a Field array into bytes, use `fields_to_bytes`.\n///\n/// Each input field must contain at most 31 bytes (this is constrained to be so).\n/// Each field is converted into 31 big-endian bytes, and the resulting 31-byte chunks are concatenated\n/// back together in the order of the original fields.\npub fn bytes_from_fields(fields: BoundedVec) -> BoundedVec {\n let mut bytes = BoundedVec::new();\n\n for i in 0..fields.len() {\n let field = fields.get(i);\n\n // We expect that the field contains at most 31 bytes of information.\n field.assert_max_bit_size::<248>();\n\n // Now we can safely convert the field to 31 bytes.\n let field_as_bytes: [u8; 31] = field.to_be_bytes();\n\n for j in 0..31 {\n bytes.push(field_as_bytes[j]);\n }\n }\n\n bytes\n}\n\nmod tests {\n use crate::utils::array::subarray;\n use super::{bytes_from_fields, bytes_to_fields};\n\n #[test]\n unconstrained fn random_bytes_to_fields_and_back(input: [u8; 93]) {\n let fields = bytes_to_fields(input);\n\n // At this point in production, the log flies through the system and we get a BoundedVec on the other end.\n // So we need to convert the field array to a BoundedVec to be able to feed it to the `bytes_from_fields`\n // function.\n let fields_as_bounded_vec = BoundedVec::<_, 6>::from_array(fields);\n\n let bytes_back = bytes_from_fields(fields_as_bounded_vec);\n\n // Compare the original input with the round-tripped result\n assert_eq(bytes_back.len(), input.len());\n assert_eq(subarray(bytes_back.storage(), 0), input);\n }\n\n #[test(should_fail_with = \"N must be a multiple of 31\")]\n unconstrained fn bytes_to_fields_input_length_not_multiple_of_31() {\n // Try to convert 32 bytes (not a multiple of 31) to fields\n let _fields = bytes_to_fields([0; 32]);\n }\n\n}\n" - }, - "185": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/utils/conversion/fields_to_bytes.nr", - "source": "// These functions are used to facilitate the conversion of log plaintext represented as fields into bytes and back.\n//\n// `fields_to_bytes` uses fixed-size arrays since encryption contexts have compile-time size information.\n// `fields_from_bytes` uses BoundedVec for flexibility in unconstrained contexts where sizes are dynamic.\n//\n// Together they provide bidirectional conversion between fields and bytes.\n\n/// Converts an input array of fields into a single array of bytes. Use `fields_from_bytes` to obtain the original\n/// field array.\n/// Each field is converted to a 32-byte big-endian array.\n///\n/// For example, if you have a field array [123, 456], it will be converted to a 64-byte array:\n/// [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,123, // First field (32 bytes)\n/// 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,200] // Second field (32 bytes)\n///\n/// Since a field is ~254 bits, you'll end up with a subtle 2-bit \"gap\" at the big end, every 32 bytes. Be careful\n/// that such a gap doesn't leak information! This could happen if you for example expected the output to be\n/// indistinguishable from random bytes.\npub fn fields_to_bytes(fields: [Field; N]) -> [u8; 32 * N] {\n let mut bytes = [0; 32 * N];\n\n for i in 0..N {\n let field_as_bytes: [u8; 32] = fields[i].to_be_bytes();\n\n for j in 0..32 {\n bytes[i * 32 + j] = field_as_bytes[j];\n }\n }\n\n bytes\n}\n\n/// Converts an input BoundedVec of bytes into a BoundedVec of fields. Arbitrary byte arrays are not allowed: this\n/// is assumed to be an array obtained via `fields_to_bytes`, i.e. one that actually represents fields. To convert\n/// a byte array into Fields, use `bytes_to_fields`.\n///\n/// The input bytes are chunked into chunks of 32 bytes. Each 32-byte chunk is viewed as big-endian, and is converted\n/// into a Field.\n/// For example, [1, 10, 3, ..., 0] (32 bytes) is encoded as [1 * 256^31 + 10 * 256^30 + 3 * 256^29 + ... + 0]\n/// Note 1: N must be a multiple of 32 bytes\n/// Note 2: The max value check code was taken from std::field::to_be_bytes function.\npub fn fields_from_bytes(bytes: BoundedVec) -> BoundedVec {\n // Assert that input length is a multiple of 32\n assert(bytes.len() % 32 == 0, \"Input length must be a multiple of 32\");\n\n let mut fields = BoundedVec::new();\n\n let p = std::field::modulus_be_bytes();\n\n // Since input length is a multiple of 32, we can simply process all chunks fully\n for i in 0..bytes.len() / 32 {\n let mut field = 0;\n\n // Process each byte in the 32-byte chunk\n let mut ok = false;\n\n for j in 0..32 {\n let next_byte = bytes.get(i * 32 + j);\n field = field * 256 + next_byte as Field;\n\n if !ok {\n if next_byte != p[j] {\n assert(next_byte < p[j], \"Value does not fit in field\");\n ok = true;\n }\n }\n }\n assert(ok, \"Value does not fit in field\");\n\n fields.push(field);\n }\n\n fields\n}\n\nmod tests {\n use crate::utils::array::subarray;\n use super::{fields_from_bytes, fields_to_bytes};\n\n #[test]\n unconstrained fn random_fields_to_bytes_and_back(input: [Field; 3]) {\n // Convert to bytes\n let bytes = fields_to_bytes(input);\n\n // At this point in production, the log flies through the system and we get a BoundedVec on the other end.\n // So we need to convert the field array to a BoundedVec to be able to feed it to the `fields_from_bytes`\n // function.\n // 113 is an arbitrary max length that is larger than the input length of 96.\n let bytes_as_bounded_vec = BoundedVec::<_, 113>::from_array(bytes);\n\n // Convert back to fields\n let fields_back = fields_from_bytes(bytes_as_bounded_vec);\n\n // Compare the original input with the round-tripped result\n assert_eq(fields_back.len(), input.len());\n assert_eq(subarray(fields_back.storage(), 0), input);\n }\n\n #[test(should_fail_with = \"Input length must be a multiple of 32\")]\n unconstrained fn to_fields_assert() {\n // 143 is an arbitrary max length that is larger than 33\n let input = BoundedVec::<_, 143>::from_array([\n 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,\n 25, 26, 27, 28, 29, 30, 31, 32, 33,\n ]);\n\n // This should fail since 33 is not a multiple of 32\n let _fields = fields_from_bytes(input);\n }\n\n #[test]\n unconstrained fn fields_from_bytes_max_value() {\n let max_field_as_bytes: [u8; 32] = (-1).to_be_bytes();\n let input = BoundedVec::<_, 32>::from_array(max_field_as_bytes);\n\n let fields = fields_from_bytes(input);\n\n // The result should be a largest value storable in a field (-1 since we are modulo-ing)\n assert_eq(fields.get(0), -1);\n }\n\n // In this test we verify that overflow check works by taking the max allowed value, bumping a random byte\n // and then feeding it to `fields_from_bytes` as input.\n #[test(should_fail_with = \"Value does not fit in field\")]\n unconstrained fn fields_from_bytes_overflow(random_value: u8) {\n let index_of_byte_to_bump = random_value % 32;\n\n // Obtain the byte representation of the maximum field value\n let max_field_value_as_bytes: [u8; 32] = (-1).to_be_bytes();\n\n let byte_to_bump = max_field_value_as_bytes[index_of_byte_to_bump as u32];\n\n // Skip test execution if the selected byte is already at maximum value (255).\n // This is acceptable since we are using fuzz testing to generate many test cases.\n if byte_to_bump != 255 {\n let mut input = BoundedVec::<_, 32>::from_array(max_field_value_as_bytes);\n\n // Increment the selected byte to exceed the field's maximum value\n input.set(index_of_byte_to_bump as u32, byte_to_bump + 1);\n\n // Attempt the conversion, which should fail due to the value exceeding the field's capacity\n let _fields = fields_from_bytes(input);\n }\n }\n\n}\n" - }, - "187": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/utils/field.nr", - "source": "use std::option::Option;\n\nglobal KNOWN_NON_RESIDUE: Field = 5; // This is a non-residue in Noir's native Field.\n\nglobal C1: u32 = 28;\nglobal C3: Field = 40770029410420498293352137776570907027550720424234931066070132305055;\nglobal C5: Field = 19103219067921713944291392827692070036145651957329286315305642004821462161904;\n\n// Power function of two Field arguments of arbitrary size.\n// Adapted from std::field::pow_32.\npub fn pow(x: Field, y: Field) -> Field {\n let mut r = 1 as Field;\n let b: [u1; 254] = y.to_le_bits();\n\n for i in 0..254 {\n r *= r;\n r *= (b[254 - 1 - i] as Field) * x + (1 - b[254 - 1 - i] as Field);\n }\n\n r\n}\n\n// Boolean indicating whether Field element is a square, i.e. whether there exists a y in Field s.t. x = y*y.\nunconstrained fn is_square(x: Field) -> bool {\n let v = pow(x, -1 / 2);\n v * (v - 1) == 0\n}\n\n// Tonelli-Shanks algorithm for computing the square root of a Field element.\n// Requires C1 = max{c: 2^c divides (p-1)}, where p is the order of Field\n// as well as C3 = (C2 - 1)/2, where C2 = (p-1)/(2^c1),\n// and C5 = ZETA^C2, where ZETA is a non-square element of Field.\n// These are pre-computed above as globals.\nunconstrained fn tonelli_shanks_sqrt(x: Field) -> Field {\n let mut z = pow(x, C3);\n let mut t = z * z * x;\n z *= x;\n let mut b = t;\n let mut c = C5;\n\n for i in 0..(C1 - 1) {\n for _j in 1..(C1 - i - 1) {\n b *= b;\n }\n\n z *= if b == 1 { 1 } else { c };\n\n c *= c;\n\n t *= if b == 1 { 1 } else { c };\n\n b = t;\n }\n\n z\n}\n\n// NB: this doesn't return an option, because in the case of there _not_ being a square root, we still want to return a field element that allows us to then assert in the _constrained_ sqrt function that there is no sqrt.\npub unconstrained fn __sqrt(x: Field) -> (bool, Field) {\n let is_sq = is_square(x);\n if is_sq {\n let sqrt = tonelli_shanks_sqrt(x);\n (true, sqrt)\n } else {\n // Demonstrate that x is not a square (a.k.a. a \"quadratic non-residue\").\n // Facts:\n // The Legendre symbol (\"LS\") of x, is x^((p-1)/2) (mod p).\n // - If x is a square, LS(x) = 1\n // - If x is not a square, LS(x) = -1\n // - If x = 0, LS(x) = 0.\n //\n // Hence:\n // sq * sq = sq // 1 * 1 = 1\n // non-sq * non-sq = sq // -1 * -1 = 1\n // sq * non-sq = non-sq // -1 * 1 = -1\n //\n // See: https://en.wikipedia.org/wiki/Legendre_symbol\n let demo_x_not_square = x * KNOWN_NON_RESIDUE;\n let not_sqrt = tonelli_shanks_sqrt(demo_x_not_square);\n (false, not_sqrt)\n }\n}\n\n// Returns (false, 0) if there is no square root.\n// Returns (true, sqrt) if there is a square root.\npub fn sqrt(x: Field) -> Option {\n // Safety: if the hint returns the square root of x, then we simply square it\n // check the result equals x. If x is not square, we return a value that\n // enables us to prove that fact (see the `else` clause below).\n let (is_sq, maybe_sqrt) = unsafe { __sqrt(x) };\n\n if is_sq {\n let sqrt = maybe_sqrt;\n validate_sqrt_hint(x, sqrt);\n Option::some(sqrt)\n } else {\n let not_sqrt_hint = maybe_sqrt;\n validate_not_sqrt_hint(x, not_sqrt_hint);\n Option::none()\n }\n}\n\nfn validate_sqrt_hint(x: Field, hint: Field) {\n assert(hint * hint == x, f\"The claimed_sqrt {hint} is not the sqrt of x {x}\");\n}\n\nfn validate_not_sqrt_hint(x: Field, hint: Field) {\n // We need this assertion, because x = 0 would pass the other assertions in this\n // function, and we don't want people to be able to prove that 0 is not square!\n assert(x != 0, \"0 has a square root; you cannot claim it is not square\");\n // Demonstrate that x is not a square (a.k.a. a \"quadratic non-residue\").\n //\n // Facts:\n // The Legendre symbol (\"LS\") of x, is x^((p-1)/2) (mod p).\n // - If x is a square, LS(x) = 1\n // - If x is not a square, LS(x) = -1\n // - If x = 0, LS(x) = 0.\n //\n // Hence:\n // 1. sq * sq = sq // 1 * 1 = 1\n // 2. non-sq * non-sq = sq // -1 * -1 = 1\n // 3. sq * non-sq = non-sq // -1 * 1 = -1\n //\n // See: https://en.wikipedia.org/wiki/Legendre_symbol\n //\n // We want to demonstrate that this below multiplication falls under bullet-point (2):\n let demo_x_not_square = x * KNOWN_NON_RESIDUE;\n // I.e. we want to demonstrate that `demo_x_not_square` has Legendre symbol 1\n // (i.e. that it is a square), so we prove that it is square below.\n // Why do we want to prove that it has LS 1?\n // Well, since it was computed with a known-non-residue, its squareness implies we're\n // in case 2 (something multiplied by a known-non-residue yielding a result which\n // has a LS of 1), which implies that x must be a non-square. The unconstrained\n // function gave us the sqrt of demo_x_not_square, so all we need to do is\n // assert its squareness:\n assert(\n hint * hint == demo_x_not_square,\n f\"The hint {hint} does not demonstrate that {x} is not a square\",\n );\n}\n\n#[test]\nfn test_sqrt() {\n let x = 9;\n let maybe_sqrt = sqrt(x);\n assert(maybe_sqrt.is_some());\n let sqrt = maybe_sqrt.unwrap_unchecked();\n assert((sqrt == 3) | (sqrt == -3));\n}\n\n#[test]\nfn test_non_square() {\n let x = 5;\n let maybe_sqrt = sqrt(x);\n assert(maybe_sqrt.is_none());\n}\n\n#[test]\nunconstrained fn test_known_non_residue_is_actually_a_non_residue_in_the_field() {\n assert(!is_square(KNOWN_NON_RESIDUE));\n}\n\n#[test]\nfn test_sqrt_0() {\n let x = 0;\n let sqrt = sqrt(x).unwrap();\n assert(sqrt == 0);\n}\n\n#[test]\nfn test_sqrt_1() {\n let x = 1;\n let sqrt = sqrt(x).unwrap();\n assert((sqrt == 1) | (sqrt == -1));\n}\n\n#[test(should_fail_with = \"The claimed_sqrt 0x04 is not the sqrt of x 0x09\")]\nfn test_bad_sqrt_hint_fails() {\n validate_sqrt_hint(9, 4);\n}\n\n#[test(should_fail_with = \"The hint 0x04 does not demonstrate that 0x0a is not a square\")]\nfn test_bad_not_sqrt_hint_fails() {\n validate_not_sqrt_hint(10, 4);\n}\n\n#[test(should_fail_with = \"0 has a square root; you cannot claim it is not square\")]\nfn test_0_not_sqrt_hint_fails() {\n validate_not_sqrt_hint(0, 0);\n}\n\n#[test]\nunconstrained fn test_is_square() {\n assert(is_square(25));\n}\n\n#[test]\nunconstrained fn test_is_not_square() {\n assert(!is_square(10));\n}\n" - }, - "189": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/utils/point.nr", - "source": "use crate::utils::field::sqrt;\nuse dep::protocol_types::point::Point;\n\n// I am storing the modulus minus 1 divided by 2 here because full modulus would throw \"String literal too large\" error\n// Full modulus is 21888242871839275222246405745257275088548364400416034343698204186575808495617\nglobal BN254_FR_MODULUS_DIV_2: Field =\n 10944121435919637611123202872628637544274182200208017171849102093287904247808;\n\n/// Converts a point to a byte array.\n///\n/// We don't serialize the point at infinity flag because this function is used in situations where we do not want\n/// to waste the extra byte (encrypted log).\npub fn point_to_bytes(p: Point) -> [u8; 32] {\n // Note that there is 1 more free bit in the 32 bytes (254 bits currently occupied by the x coordinate, 1 bit for\n // the \"sign\") so it's possible to use that last bit as an \"is_infinite\" flag if desired in the future.\n assert(!p.is_infinite, \"Cannot serialize point at infinity as bytes.\");\n\n let mut result: [u8; 32] = p.x.to_be_bytes();\n\n if get_sign_of_point(p) {\n // y is <= (modulus - 1) / 2 so we set the sign bit to 1\n // Here we leverage that field fits into 254 bits (log2(Fr.MODULUS) < 254) and given that we serialize Fr to 32\n // bytes and we use big-endian the 2 most significant bits are never populated. Hence we can use one of\n // the bits as a sign bit.\n result[0] += 128;\n }\n\n result\n}\n\n/**\n * Returns: true if p.y <= MOD_DIV_2, else false.\n */\npub fn get_sign_of_point(p: Point) -> bool {\n // We store only a \"sign\" of the y coordinate because the rest can be derived from the x coordinate. To get\n // the sign we check if the y coordinate is less or equal than the curve's order minus 1 divided by 2.\n // Ideally we'd do `y <= MOD_DIV_2`, but there's no `lte` function, so instead we do `!(y > MOD_DIV_2)`, which is\n // equivalent, and then rewrite that as `!(MOD_DIV_2 < y)`, since we also have no `gt` function.\n !BN254_FR_MODULUS_DIV_2.lt(p.y)\n}\n\npub fn point_from_x_coord(x: Field) -> Point {\n // y ^ 2 = x ^ 3 - 17\n let rhs = x * x * x - 17;\n let y = sqrt(rhs).unwrap();\n Point { x, y, is_infinite: false }\n}\n\n/// Uses the x coordinate and sign flag (+/-) to reconstruct the point.\n/// The y coordinate can be derived from the x coordinate and the \"sign\" flag by solving the grumpkin curve\n/// equation for y.\n/// @param x - The x coordinate of the point\n/// @param sign - The \"sign\" of the y coordinate - determines whether y <= (Fr.MODULUS - 1) / 2\npub fn point_from_x_coord_and_sign(x: Field, sign: bool) -> Point {\n // y ^ 2 = x ^ 3 - 17\n let rhs = x * x * x - 17;\n let y = sqrt(rhs).unwrap();\n\n // If y > MOD_DIV_2 and we want positive sign (or vice versa), negate y\n let y_is_positive = !BN254_FR_MODULUS_DIV_2.lt(y);\n let final_y = if y_is_positive == sign { y } else { -y };\n\n Point { x, y: final_y, is_infinite: false }\n}\n\nmod test {\n use crate::utils::point::{point_from_x_coord_and_sign, point_to_bytes};\n use dep::protocol_types::point::Point;\n\n #[test]\n unconstrained fn test_point_to_bytes_positive_sign() {\n let p = Point {\n x: 0x1af41f5de96446dc3776a1eb2d98bb956b7acd9979a67854bec6fa7c2973bd73,\n y: 0x07fc22c7f2c7057571f137fe46ea9c95114282bc95d37d71ec4bfb88de457d4a,\n is_infinite: false,\n };\n\n let compressed_point = point_to_bytes(p);\n\n let expected_compressed_point_positive_sign = [\n 154, 244, 31, 93, 233, 100, 70, 220, 55, 118, 161, 235, 45, 152, 187, 149, 107, 122,\n 205, 153, 121, 166, 120, 84, 190, 198, 250, 124, 41, 115, 189, 115,\n ];\n assert_eq(expected_compressed_point_positive_sign, compressed_point);\n }\n\n #[test]\n unconstrained fn test_point_to_bytes_negative_sign() {\n let p = Point {\n x: 0x247371652e55dd74c9af8dbe9fb44931ba29a9229994384bd7077796c14ee2b5,\n y: 0x26441aec112e1ae4cee374f42556932001507ad46e255ffb27369c7e3766e5c0,\n is_infinite: false,\n };\n\n let compressed_point = point_to_bytes(p);\n\n let expected_compressed_point_negative_sign = [\n 36, 115, 113, 101, 46, 85, 221, 116, 201, 175, 141, 190, 159, 180, 73, 49, 186, 41, 169,\n 34, 153, 148, 56, 75, 215, 7, 119, 150, 193, 78, 226, 181,\n ];\n\n assert_eq(expected_compressed_point_negative_sign, compressed_point);\n }\n\n #[test]\n unconstrained fn test_point_from_x_coord_and_sign() {\n // Test positive y coordinate\n let x = 0x1af41f5de96446dc3776a1eb2d98bb956b7acd9979a67854bec6fa7c2973bd73;\n let sign = true;\n let p = point_from_x_coord_and_sign(x, sign);\n\n assert_eq(p.x, x);\n assert_eq(p.y, 0x07fc22c7f2c7057571f137fe46ea9c95114282bc95d37d71ec4bfb88de457d4a);\n assert_eq(p.is_infinite, false);\n\n // Test negative y coordinate\n let x2 = 0x247371652e55dd74c9af8dbe9fb44931ba29a9229994384bd7077796c14ee2b5;\n let sign2 = false;\n let p2 = point_from_x_coord_and_sign(x2, sign2);\n\n assert_eq(p2.x, x2);\n assert_eq(p2.y, 0x26441aec112e1ae4cee374f42556932001507ad46e255ffb27369c7e3766e5c0);\n assert_eq(p2.is_infinite, false);\n }\n}\n" - }, - "19": { - "path": "std/hash/mod.nr", - "source": "// Exposed only for usage in `std::meta`\npub(crate) mod poseidon2;\n\nuse crate::default::Default;\nuse crate::embedded_curve_ops::{\n EmbeddedCurvePoint, EmbeddedCurveScalar, multi_scalar_mul, multi_scalar_mul_array_return,\n};\nuse crate::meta::derive_via;\n\n#[foreign(sha256_compression)]\n// docs:start:sha256_compression\npub fn sha256_compression(input: [u32; 16], state: [u32; 8]) -> [u32; 8] {}\n// docs:end:sha256_compression\n\n#[foreign(keccakf1600)]\n// docs:start:keccakf1600\npub fn keccakf1600(input: [u64; 25]) -> [u64; 25] {}\n// docs:end:keccakf1600\n\npub mod keccak {\n #[deprecated(\"This function has been moved to std::hash::keccakf1600\")]\n pub fn keccakf1600(input: [u64; 25]) -> [u64; 25] {\n super::keccakf1600(input)\n }\n}\n\n#[foreign(blake2s)]\n// docs:start:blake2s\npub fn blake2s(input: [u8; N]) -> [u8; 32]\n// docs:end:blake2s\n{}\n\n#[foreign(blake3)]\n// docs:start:blake3\npub fn blake3(input: [u8; N]) -> [u8; 32]\n// docs:end:blake3\n{}\n\n// docs:start:pedersen_commitment\npub fn pedersen_commitment(input: [Field; N]) -> EmbeddedCurvePoint {\n // docs:end:pedersen_commitment\n pedersen_commitment_with_separator(input, 0)\n}\n\n#[inline_always]\npub fn pedersen_commitment_with_separator(\n input: [Field; N],\n separator: u32,\n) -> EmbeddedCurvePoint {\n let mut points = [EmbeddedCurveScalar { lo: 0, hi: 0 }; N];\n for i in 0..N {\n // we use the unsafe version because the multi_scalar_mul will constrain the scalars.\n points[i] = from_field_unsafe(input[i]);\n }\n let generators = derive_generators(\"DEFAULT_DOMAIN_SEPARATOR\".as_bytes(), separator);\n multi_scalar_mul(generators, points)\n}\n\n// docs:start:pedersen_hash\npub fn pedersen_hash(input: [Field; N]) -> Field\n// docs:end:pedersen_hash\n{\n pedersen_hash_with_separator(input, 0)\n}\n\n#[no_predicates]\npub fn pedersen_hash_with_separator(input: [Field; N], separator: u32) -> Field {\n let mut scalars: [EmbeddedCurveScalar; N + 1] = [EmbeddedCurveScalar { lo: 0, hi: 0 }; N + 1];\n let mut generators: [EmbeddedCurvePoint; N + 1] =\n [EmbeddedCurvePoint::point_at_infinity(); N + 1];\n let domain_generators: [EmbeddedCurvePoint; N] =\n derive_generators(\"DEFAULT_DOMAIN_SEPARATOR\".as_bytes(), separator);\n\n for i in 0..N {\n scalars[i] = from_field_unsafe(input[i]);\n generators[i] = domain_generators[i];\n }\n scalars[N] = EmbeddedCurveScalar { lo: N as Field, hi: 0 as Field };\n\n let length_generator: [EmbeddedCurvePoint; 1] =\n derive_generators(\"pedersen_hash_length\".as_bytes(), 0);\n generators[N] = length_generator[0];\n multi_scalar_mul_array_return(generators, scalars)[0].x\n}\n\n#[field(bn254)]\n#[inline_always]\npub fn derive_generators(\n domain_separator_bytes: [u8; M],\n starting_index: u32,\n) -> [EmbeddedCurvePoint; N] {\n crate::assert_constant(domain_separator_bytes);\n // TODO(https://github.com/noir-lang/noir/issues/5672): Add back assert_constant on starting_index\n __derive_generators(domain_separator_bytes, starting_index)\n}\n\n#[builtin(derive_pedersen_generators)]\n#[field(bn254)]\nfn __derive_generators(\n domain_separator_bytes: [u8; M],\n starting_index: u32,\n) -> [EmbeddedCurvePoint; N] {}\n\n#[field(bn254)]\n// Same as from_field but:\n// does not assert the limbs are 128 bits\n// does not assert the decomposition does not overflow the EmbeddedCurveScalar\nfn from_field_unsafe(scalar: Field) -> EmbeddedCurveScalar {\n // Safety: xlo and xhi decomposition is checked below\n let (xlo, xhi) = unsafe { crate::field::bn254::decompose_hint(scalar) };\n // Check that the decomposition is correct\n assert_eq(scalar, xlo + crate::field::bn254::TWO_POW_128 * xhi);\n EmbeddedCurveScalar { lo: xlo, hi: xhi }\n}\n\npub fn hash_to_field(inputs: [Field]) -> Field {\n let mut sum = 0;\n\n for input in inputs {\n let input_bytes: [u8; 32] = input.to_le_bytes();\n sum += crate::field::bytes32_to_field(blake2s(input_bytes));\n }\n\n sum\n}\n\n#[foreign(poseidon2_permutation)]\npub fn poseidon2_permutation(_input: [Field; N], _state_length: u32) -> [Field; N] {}\n\n// Generic hashing support.\n// Partially ported and impacted by rust.\n\n// Hash trait shall be implemented per type.\n#[derive_via(derive_hash)]\npub trait Hash {\n fn hash(self, state: &mut H)\n where\n H: Hasher;\n}\n\n// docs:start:derive_hash\ncomptime fn derive_hash(s: TypeDefinition) -> Quoted {\n let name = quote { Hash };\n let signature = quote { fn hash(_self: Self, _state: &mut H) where H: std::hash::Hasher };\n let for_each_field = |name| quote { _self.$name.hash(_state); };\n crate::meta::make_trait_impl(\n s,\n name,\n signature,\n for_each_field,\n quote {},\n |fields| fields,\n )\n}\n// docs:end:derive_hash\n\n// Hasher trait shall be implemented by algorithms to provide hash-agnostic means.\n// TODO: consider making the types generic here ([u8], [Field], etc.)\npub trait Hasher {\n fn finish(self) -> Field;\n\n fn write(&mut self, input: Field);\n}\n\n// BuildHasher is a factory trait, responsible for production of specific Hasher.\npub trait BuildHasher\nwhere\n H: Hasher,\n{\n fn build_hasher(self) -> H;\n}\n\npub struct BuildHasherDefault;\n\nimpl BuildHasher for BuildHasherDefault\nwhere\n H: Hasher + Default,\n{\n fn build_hasher(_self: Self) -> H {\n H::default()\n }\n}\n\nimpl Default for BuildHasherDefault\nwhere\n H: Hasher + Default,\n{\n fn default() -> Self {\n BuildHasherDefault {}\n }\n}\n\nimpl Hash for Field {\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n H::write(state, self);\n }\n}\n\nimpl Hash for u1 {\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n H::write(state, self as Field);\n }\n}\n\nimpl Hash for u8 {\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n H::write(state, self as Field);\n }\n}\n\nimpl Hash for u16 {\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n H::write(state, self as Field);\n }\n}\n\nimpl Hash for u32 {\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n H::write(state, self as Field);\n }\n}\n\nimpl Hash for u64 {\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n H::write(state, self as Field);\n }\n}\n\nimpl Hash for u128 {\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n H::write(state, self as Field);\n }\n}\n\nimpl Hash for i8 {\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n H::write(state, self as Field);\n }\n}\n\nimpl Hash for i16 {\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n H::write(state, self as Field);\n }\n}\n\nimpl Hash for i32 {\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n H::write(state, self as Field);\n }\n}\n\nimpl Hash for i64 {\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n H::write(state, self as Field);\n }\n}\n\nimpl Hash for bool {\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n H::write(state, self as Field);\n }\n}\n\nimpl Hash for () {\n fn hash(_self: Self, _state: &mut H)\n where\n H: Hasher,\n {}\n}\n\nimpl Hash for [T; N]\nwhere\n T: Hash,\n{\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n for elem in self {\n elem.hash(state);\n }\n }\n}\n\nimpl Hash for [T]\nwhere\n T: Hash,\n{\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n self.len().hash(state);\n for elem in self {\n elem.hash(state);\n }\n }\n}\n\nimpl Hash for (A, B)\nwhere\n A: Hash,\n B: Hash,\n{\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n self.0.hash(state);\n self.1.hash(state);\n }\n}\n\nimpl Hash for (A, B, C)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n{\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n self.0.hash(state);\n self.1.hash(state);\n self.2.hash(state);\n }\n}\n\nimpl Hash for (A, B, C, D)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n{\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n self.0.hash(state);\n self.1.hash(state);\n self.2.hash(state);\n self.3.hash(state);\n }\n}\n\nimpl Hash for (A, B, C, D, E)\nwhere\n A: Hash,\n B: Hash,\n C: Hash,\n D: Hash,\n E: Hash,\n{\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n self.0.hash(state);\n self.1.hash(state);\n self.2.hash(state);\n self.3.hash(state);\n self.4.hash(state);\n }\n}\n\n// Some test vectors for Pedersen hash and Pedersen Commitment.\n// They have been generated using the same functions so the tests are for now useless\n// but they will be useful when we switch to Noir implementation.\n#[test]\nfn assert_pedersen() {\n assert_eq(\n pedersen_hash_with_separator([1], 1),\n 0x1b3f4b1a83092a13d8d1a59f7acb62aba15e7002f4440f2275edb99ebbc2305f,\n );\n assert_eq(\n pedersen_commitment_with_separator([1], 1),\n EmbeddedCurvePoint {\n x: 0x054aa86a73cb8a34525e5bbed6e43ba1198e860f5f3950268f71df4591bde402,\n y: 0x209dcfbf2cfb57f9f6046f44d71ac6faf87254afc7407c04eb621a6287cac126,\n is_infinite: false,\n },\n );\n\n assert_eq(\n pedersen_hash_with_separator([1, 2], 2),\n 0x26691c129448e9ace0c66d11f0a16d9014a9e8498ee78f4d69f0083168188255,\n );\n assert_eq(\n pedersen_commitment_with_separator([1, 2], 2),\n EmbeddedCurvePoint {\n x: 0x2e2b3b191e49541fe468ec6877721d445dcaffe41728df0a0eafeb15e87b0753,\n y: 0x2ff4482400ad3a6228be17a2af33e2bcdf41be04795f9782bd96efe7e24f8778,\n is_infinite: false,\n },\n );\n assert_eq(\n pedersen_hash_with_separator([1, 2, 3], 3),\n 0x0bc694b7a1f8d10d2d8987d07433f26bd616a2d351bc79a3c540d85b6206dbe4,\n );\n assert_eq(\n pedersen_commitment_with_separator([1, 2, 3], 3),\n EmbeddedCurvePoint {\n x: 0x1fee4e8cf8d2f527caa2684236b07c4b1bad7342c01b0f75e9a877a71827dc85,\n y: 0x2f9fedb9a090697ab69bf04c8bc15f7385b3e4b68c849c1536e5ae15ff138fd1,\n is_infinite: false,\n },\n );\n assert_eq(\n pedersen_hash_with_separator([1, 2, 3, 4], 4),\n 0xdae10fb32a8408521803905981a2b300d6a35e40e798743e9322b223a5eddc,\n );\n assert_eq(\n pedersen_commitment_with_separator([1, 2, 3, 4], 4),\n EmbeddedCurvePoint {\n x: 0x07ae3e202811e1fca39c2d81eabe6f79183978e6f12be0d3b8eda095b79bdbc9,\n y: 0x0afc6f892593db6fbba60f2da558517e279e0ae04f95758587760ba193145014,\n is_infinite: false,\n },\n );\n assert_eq(\n pedersen_hash_with_separator([1, 2, 3, 4, 5], 5),\n 0xfc375b062c4f4f0150f7100dfb8d9b72a6d28582dd9512390b0497cdad9c22,\n );\n assert_eq(\n pedersen_commitment_with_separator([1, 2, 3, 4, 5], 5),\n EmbeddedCurvePoint {\n x: 0x1754b12bd475a6984a1094b5109eeca9838f4f81ac89c5f0a41dbce53189bb29,\n y: 0x2da030e3cfcdc7ddad80eaf2599df6692cae0717d4e9f7bfbee8d073d5d278f7,\n is_infinite: false,\n },\n );\n assert_eq(\n pedersen_hash_with_separator([1, 2, 3, 4, 5, 6], 6),\n 0x1696ed13dc2730062a98ac9d8f9de0661bb98829c7582f699d0273b18c86a572,\n );\n assert_eq(\n pedersen_commitment_with_separator([1, 2, 3, 4, 5, 6], 6),\n EmbeddedCurvePoint {\n x: 0x190f6c0e97ad83e1e28da22a98aae156da083c5a4100e929b77e750d3106a697,\n y: 0x1f4b60f34ef91221a0b49756fa0705da93311a61af73d37a0c458877706616fb,\n is_infinite: false,\n },\n );\n assert_eq(\n pedersen_hash_with_separator([1, 2, 3, 4, 5, 6, 7], 7),\n 0x128c0ff144fc66b6cb60eeac8a38e23da52992fc427b92397a7dffd71c45ede3,\n );\n assert_eq(\n pedersen_commitment_with_separator([1, 2, 3, 4, 5, 6, 7], 7),\n EmbeddedCurvePoint {\n x: 0x015441e9d29491b06563fac16fc76abf7a9534c715421d0de85d20dbe2965939,\n y: 0x1d2575b0276f4e9087e6e07c2cb75aa1baafad127af4be5918ef8a2ef2fea8fc,\n is_infinite: false,\n },\n );\n assert_eq(\n pedersen_hash_with_separator([1, 2, 3, 4, 5, 6, 7, 8], 8),\n 0x2f960e117482044dfc99d12fece2ef6862fba9242be4846c7c9a3e854325a55c,\n );\n assert_eq(\n pedersen_commitment_with_separator([1, 2, 3, 4, 5, 6, 7, 8], 8),\n EmbeddedCurvePoint {\n x: 0x1657737676968887fceb6dd516382ea13b3a2c557f509811cd86d5d1199bc443,\n y: 0x1f39f0cb569040105fa1e2f156521e8b8e08261e635a2b210bdc94e8d6d65f77,\n is_infinite: false,\n },\n );\n assert_eq(\n pedersen_hash_with_separator([1, 2, 3, 4, 5, 6, 7, 8, 9], 9),\n 0x0c96db0790602dcb166cc4699e2d306c479a76926b81c2cb2aaa92d249ec7be7,\n );\n assert_eq(\n pedersen_commitment_with_separator([1, 2, 3, 4, 5, 6, 7, 8, 9], 9),\n EmbeddedCurvePoint {\n x: 0x0a3ceae42d14914a432aa60ec7fded4af7dad7dd4acdbf2908452675ec67e06d,\n y: 0xfc19761eaaf621ad4aec9a8b2e84a4eceffdba78f60f8b9391b0bd9345a2f2,\n is_infinite: false,\n },\n );\n assert_eq(\n pedersen_hash_with_separator([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 10),\n 0x2cd37505871bc460a62ea1e63c7fe51149df5d0801302cf1cbc48beb8dff7e94,\n );\n assert_eq(\n pedersen_commitment_with_separator([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 10),\n EmbeddedCurvePoint {\n x: 0x2fb3f8b3d41ddde007c8c3c62550f9a9380ee546fcc639ffbb3fd30c8d8de30c,\n y: 0x300783be23c446b11a4c0fabf6c91af148937cea15fcf5fb054abf7f752ee245,\n is_infinite: false,\n },\n );\n}\n" - }, - "193": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/utils/with_hash.nr", - "source": "use crate::{\n context::{PublicContext, UtilityContext},\n history::public_storage::PublicStorageHistoricalRead,\n oracle,\n};\nuse dep::protocol_types::{\n address::AztecAddress, block_header::BlockHeader, hash::poseidon2_hash, traits::Packable,\n};\n\n/// A struct that allows for efficient reading of value `T` from public storage in private.\n///\n/// The efficient reads are achieved by verifying large values through a single hash check\n/// and then proving inclusion only of the hash in public storage. This reduces the number\n/// of required tree inclusion proofs from `N` to 1.\n///\n/// # Type Parameters\n/// - `T`: The underlying type being wrapped, must implement `Packable`\n/// - `N`: The number of field elements required to pack values of type `T`\npub struct WithHash {\n value: T,\n packed: [Field; N],\n hash: Field,\n}\n\nimpl WithHash\nwhere\n T: Packable + Eq,\n{\n pub fn new(value: T) -> Self {\n let packed = value.pack();\n Self { value, packed, hash: poseidon2_hash(packed) }\n }\n\n pub fn get_value(self) -> T {\n self.value\n }\n\n pub fn get_hash(self) -> Field {\n self.hash\n }\n\n pub fn public_storage_read(context: PublicContext, storage_slot: Field) -> T {\n context.storage_read(storage_slot)\n }\n\n pub unconstrained fn utility_public_storage_read(\n context: UtilityContext,\n storage_slot: Field,\n ) -> T {\n context.storage_read(storage_slot)\n }\n\n pub fn historical_public_storage_read(\n header: BlockHeader,\n address: AztecAddress,\n storage_slot: Field,\n ) -> T {\n let historical_block_number = header.global_variables.block_number as u32;\n\n // We could simply produce historical inclusion proofs for each field in `packed`, but that would require one\n // full sibling path per storage slot (since due to kernel siloing the storage is not contiguous). Instead, we\n // get an oracle to provide us the values, and instead we prove inclusion of their hash, which is both a much\n // smaller proof (a single slot), and also independent of the size of T (except in that we need to pack and hash T).\n let hint = WithHash::new(\n // Safety: We verify that a hash of the hint/packed data matches the stored hash.\n unsafe {\n oracle::storage::storage_read(address, storage_slot, historical_block_number)\n },\n );\n\n let hash = header.public_storage_historical_read(storage_slot + N as Field, address);\n\n if hash != 0 {\n assert_eq(hash, hint.get_hash(), \"Hint values do not match hash\");\n } else {\n // The hash slot can only hold a zero if it is uninitialized. Therefore, the hints must then be zero\n // (i.e. the default value for public storage) as well.\n assert_eq(\n hint.get_value(),\n T::unpack(std::mem::zeroed()),\n \"Non-zero hint for zero hash\",\n );\n };\n\n hint.get_value()\n }\n}\n\nimpl Packable for WithHash\nwhere\n T: Packable,\n{\n fn pack(self) -> [Field; N + 1] {\n let mut result: [Field; N + 1] = std::mem::zeroed();\n for i in 0..N {\n result[i] = self.packed[i];\n }\n result[N] = self.hash;\n\n result\n }\n\n fn unpack(packed: [Field; N + 1]) -> Self {\n let mut value_packed: [Field; N] = std::mem::zeroed();\n for i in 0..N {\n value_packed[i] = packed[i];\n }\n let hash = packed[N];\n\n Self { value: T::unpack(value_packed), packed: value_packed, hash }\n }\n}\n\nmod test {\n use crate::{\n oracle::random::random,\n test::{\n helpers::{cheatcodes, test_environment::TestEnvironment},\n mocks::mock_struct::MockStruct,\n },\n utils::with_hash::WithHash,\n };\n use dep::protocol_types::hash::poseidon2_hash;\n use dep::protocol_types::traits::{Packable, ToField};\n use dep::std::{mem, test::OracleMock};\n\n global storage_slot: Field = 47;\n\n #[test]\n unconstrained fn create_and_recover() {\n let value = MockStruct { a: 5, b: 3 };\n let value_with_hash = WithHash::new(value);\n let recovered = WithHash::unpack(value_with_hash.pack());\n\n assert_eq(recovered.value, value);\n assert_eq(recovered.packed, value.pack());\n assert_eq(recovered.hash, poseidon2_hash(value.pack()));\n }\n\n #[test]\n unconstrained fn read_uninitialized_value() {\n let mut env = TestEnvironment::new();\n\n let block_header = env.private().historical_header;\n let address = env.contract_address();\n\n let result = WithHash::::historical_public_storage_read(\n block_header,\n address,\n storage_slot,\n );\n\n // We should get zeroed value\n let expected: MockStruct = mem::zeroed();\n assert_eq(result, expected);\n }\n\n #[test]\n unconstrained fn read_initialized_value() {\n let mut env = TestEnvironment::new();\n\n let value = MockStruct { a: 5, b: 3 };\n let value_with_hash = WithHash::new(value);\n\n // We write the value with hash to storage\n cheatcodes::direct_storage_write(\n env.contract_address(),\n storage_slot,\n value_with_hash.pack(),\n );\n\n // We advance block by 1 because env.private() currently returns context at latest_block - 1\n env.advance_block_by(1);\n\n let result = WithHash::::historical_public_storage_read(\n env.private().historical_header,\n env.contract_address(),\n storage_slot,\n );\n\n assert_eq(result, value);\n }\n\n #[test(should_fail_with = \"Non-zero hint for zero hash\")]\n unconstrained fn test_bad_hint_uninitialized_value() {\n let mut env = TestEnvironment::new();\n\n env.advance_block_to(6);\n\n let value_packed = MockStruct { a: 1, b: 1 }.pack();\n\n let block_header = env.private().historical_header;\n let address = env.contract_address();\n\n // Mock the oracle to return a non-zero hint/packed value\n let _ = OracleMock::mock(\"storageRead\")\n .with_params((\n address.to_field(), storage_slot, block_header.global_variables.block_number as u32,\n value_packed.len(),\n ))\n .returns(value_packed)\n .times(1);\n\n // This should revert because the hint value is non-zero and the hash is zero (default value of storage)\n let _ = WithHash::::historical_public_storage_read(\n block_header,\n address,\n storage_slot,\n );\n }\n\n #[test(should_fail_with = \"Hint values do not match hash\")]\n unconstrained fn test_bad_hint_initialized_value() {\n let mut env = TestEnvironment::new();\n\n let value_packed = MockStruct { a: 5, b: 3 }.pack();\n\n // We write the value to storage\n cheatcodes::direct_storage_write(env.contract_address(), storage_slot, value_packed);\n\n // Now we write incorrect hash to the hash storage slot\n let incorrect_hash = random();\n let hash_storage_slot = storage_slot + (value_packed.len() as Field);\n cheatcodes::direct_storage_write(\n env.contract_address(),\n hash_storage_slot,\n [incorrect_hash],\n );\n\n // We advance block by 1 because env.private() currently returns context at latest_block - 1\n env.advance_block_by(1);\n\n let _ = WithHash::::historical_public_storage_read(\n env.private().historical_header,\n env.contract_address(),\n storage_slot,\n );\n }\n}\n" - }, - "200": { - "path": "/mnt/user-data/saleel/nargo/github.com/noir-lang/poseidon/v0.1.0/src/poseidon2.nr", - "source": "use std::default::Default;\nuse std::hash::Hasher;\n\ncomptime global RATE: u32 = 3;\n\npub struct Poseidon2 {\n cache: [Field; 3],\n state: [Field; 4],\n cache_size: u32,\n squeeze_mode: bool, // 0 => absorb, 1 => squeeze\n}\n\nimpl Poseidon2 {\n #[no_predicates]\n pub fn hash(input: [Field; N], message_size: u32) -> Field {\n Poseidon2::hash_internal(input, message_size, message_size != N)\n }\n\n pub(crate) fn new(iv: Field) -> Poseidon2 {\n let mut result =\n Poseidon2 { cache: [0; 3], state: [0; 4], cache_size: 0, squeeze_mode: false };\n result.state[RATE] = iv;\n result\n }\n\n fn perform_duplex(&mut self) {\n // add the cache into sponge state\n for i in 0..RATE {\n // We effectively zero-pad the cache by only adding to the state\n // cache that is less than the specified `cache_size`\n if i < self.cache_size {\n self.state[i] += self.cache[i];\n }\n }\n self.state = crate::poseidon2_permutation(self.state, 4);\n }\n\n fn absorb(&mut self, input: Field) {\n assert(!self.squeeze_mode);\n if self.cache_size == RATE {\n // If we're absorbing, and the cache is full, apply the sponge permutation to compress the cache\n self.perform_duplex();\n self.cache[0] = input;\n self.cache_size = 1;\n } else {\n // If we're absorbing, and the cache is not full, add the input into the cache\n self.cache[self.cache_size] = input;\n self.cache_size += 1;\n }\n }\n\n fn squeeze(&mut self) -> Field {\n assert(!self.squeeze_mode);\n // If we're in absorb mode, apply sponge permutation to compress the cache.\n self.perform_duplex();\n self.squeeze_mode = true;\n\n // Pop one item off the top of the permutation and return it.\n self.state[0]\n }\n\n fn hash_internal(\n input: [Field; N],\n in_len: u32,\n is_variable_length: bool,\n ) -> Field {\n let two_pow_64 = 18446744073709551616;\n let iv: Field = (in_len as Field) * two_pow_64;\n let mut sponge = Poseidon2::new(iv);\n for i in 0..input.len() {\n if i < in_len {\n sponge.absorb(input[i]);\n }\n }\n\n // In the case where the hash preimage is variable-length, we append `1` to the end of the input, to distinguish\n // from fixed-length hashes. (the combination of this additional field element + the hash IV ensures\n // fixed-length and variable-length hashes do not collide)\n if is_variable_length {\n sponge.absorb(1);\n }\n sponge.squeeze()\n }\n}\n\npub struct Poseidon2Hasher {\n _state: [Field],\n}\n\nimpl Hasher for Poseidon2Hasher {\n fn finish(self) -> Field {\n let iv: Field = (self._state.len() as Field) * 18446744073709551616; // iv = (self._state.len() << 64)\n let mut sponge = Poseidon2::new(iv);\n for i in 0..self._state.len() {\n sponge.absorb(self._state[i]);\n }\n sponge.squeeze()\n }\n\n fn write(&mut self, input: Field) {\n self._state = self._state.push_back(input);\n }\n}\n\nimpl Default for Poseidon2Hasher {\n fn default() -> Self {\n Poseidon2Hasher { _state: &[] }\n }\n}\n" - }, - "217": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/noir-protocol-circuits/crates/types/src/abis/event_selector.nr", - "source": "use crate::traits::{Deserialize, Empty, FromField, Serialize, ToField};\n\npub struct EventSelector {\n // 1st 4-bytes (big-endian leftmost) of abi-encoding of an event.\n inner: u32,\n}\n\nimpl Eq for EventSelector {\n fn eq(self, other: EventSelector) -> bool {\n other.inner == self.inner\n }\n}\n\nimpl Serialize<1> for EventSelector {\n fn serialize(self: Self) -> [Field; 1] {\n [self.inner as Field]\n }\n}\n\nimpl Deserialize<1> for EventSelector {\n fn deserialize(fields: [Field; 1]) -> Self {\n Self { inner: fields[0] as u32 }\n }\n}\n\nimpl FromField for EventSelector {\n fn from_field(field: Field) -> Self {\n Self { inner: field as u32 }\n }\n}\n\nimpl ToField for EventSelector {\n fn to_field(self) -> Field {\n self.inner as Field\n }\n}\n\nimpl Empty for EventSelector {\n fn empty() -> Self {\n Self { inner: 0 as u32 }\n }\n}\n\nimpl EventSelector {\n pub fn from_u32(value: u32) -> Self {\n Self { inner: value }\n }\n\n pub fn from_signature(signature: str) -> Self {\n let bytes = signature.as_bytes();\n let hash = crate::hash::poseidon2_hash_bytes(bytes);\n\n // `hash` is automatically truncated to fit within 32 bits.\n EventSelector::from_field(hash)\n }\n\n pub fn zero() -> Self {\n Self { inner: 0 }\n }\n}\n" - }, - "219": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/noir-protocol-circuits/crates/types/src/abis/function_selector.nr", - "source": "use crate::traits::{Deserialize, Empty, FromField, Serialize, ToField};\n\npub struct FunctionSelector {\n // 1st 4-bytes of abi-encoding of function.\n pub inner: u32,\n}\n\nimpl Eq for FunctionSelector {\n fn eq(self, function_selector: FunctionSelector) -> bool {\n function_selector.inner == self.inner\n }\n}\n\nimpl Serialize<1> for FunctionSelector {\n fn serialize(self: Self) -> [Field; 1] {\n [self.inner as Field]\n }\n}\n\nimpl Deserialize<1> for FunctionSelector {\n fn deserialize(fields: [Field; 1]) -> Self {\n Self { inner: fields[0] as u32 }\n }\n}\n\nimpl FromField for FunctionSelector {\n fn from_field(field: Field) -> Self {\n Self { inner: field as u32 }\n }\n}\n\nimpl ToField for FunctionSelector {\n fn to_field(self) -> Field {\n self.inner as Field\n }\n}\n\nimpl Empty for FunctionSelector {\n fn empty() -> Self {\n Self { inner: 0 as u32 }\n }\n}\n\nimpl FunctionSelector {\n pub fn from_u32(value: u32) -> Self {\n Self { inner: value }\n }\n\n pub fn from_signature(signature: str) -> Self {\n let bytes = signature.as_bytes();\n let hash = crate::hash::poseidon2_hash_bytes(bytes);\n\n // `hash` is automatically truncated to fit within 32 bits.\n FunctionSelector::from_field(hash)\n }\n\n pub fn zero() -> Self {\n Self { inner: 0 }\n }\n}\n\n#[test]\nfn test_is_valid_selector() {\n let selector = FunctionSelector::from_signature(\"IS_VALID()\");\n assert_eq(selector.to_field(), 0x73cdda47);\n}\n\n#[test]\nfn test_long_selector() {\n let selector =\n FunctionSelector::from_signature(\"foo_and_bar_and_baz_and_foo_bar_baz_and_bar_foo\");\n assert_eq(selector.to_field(), 0x7590a997);\n}\n" - }, - "262": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/noir-protocol-circuits/crates/types/src/address/aztec_address.nr", - "source": "use crate::{\n address::{\n partial_address::PartialAddress, salted_initialization_hash::SaltedInitializationHash,\n },\n constants::{\n AZTEC_ADDRESS_LENGTH, GENERATOR_INDEX__CONTRACT_ADDRESS_V1, MAX_FIELD_VALUE,\n MAX_PROTOCOL_CONTRACTS,\n },\n contract_class_id::ContractClassId,\n hash::poseidon2_hash_with_separator,\n public_keys::{IvpkM, NpkM, OvpkM, PublicKeys, ToPoint, TpkM},\n traits::{Deserialize, Empty, FromField, Packable, Serialize, ToField},\n utils::field::{pow, sqrt},\n};\n\n// We do below because `use crate::point::Point;` does not work\nuse dep::std::embedded_curve_ops::EmbeddedCurvePoint as Point;\n\nuse crate::public_keys::AddressPoint;\nuse std::{\n embedded_curve_ops::{EmbeddedCurveScalar, fixed_base_scalar_mul as derive_public_key},\n ops::Add,\n};\n\n// Aztec address\npub struct AztecAddress {\n pub inner: Field,\n}\n\nimpl Eq for AztecAddress {\n fn eq(self, other: Self) -> bool {\n self.to_field() == other.to_field()\n }\n}\n\nimpl Empty for AztecAddress {\n fn empty() -> Self {\n Self { inner: 0 }\n }\n}\n\nimpl ToField for AztecAddress {\n fn to_field(self) -> Field {\n self.inner\n }\n}\n\nimpl FromField for AztecAddress {\n fn from_field(value: Field) -> AztecAddress {\n AztecAddress { inner: value }\n }\n}\n\nimpl Serialize for AztecAddress {\n fn serialize(self: Self) -> [Field; AZTEC_ADDRESS_LENGTH] {\n [self.to_field()]\n }\n}\n\nimpl Deserialize for AztecAddress {\n fn deserialize(fields: [Field; AZTEC_ADDRESS_LENGTH]) -> Self {\n FromField::from_field(fields[0])\n }\n}\n\n/// We implement the Packable trait for AztecAddress because it can be stored in contract's storage (and there\n/// the implementation of Packable is required).\nimpl Packable for AztecAddress {\n fn pack(self) -> [Field; AZTEC_ADDRESS_LENGTH] {\n self.serialize()\n }\n\n fn unpack(fields: [Field; AZTEC_ADDRESS_LENGTH]) -> Self {\n Self::deserialize(fields)\n }\n}\n\nimpl AztecAddress {\n pub fn zero() -> Self {\n Self { inner: 0 }\n }\n\n pub fn to_address_point(self) -> AddressPoint {\n // We compute the address point by taking our address, setting it to x, and then solving for y in the\n // equation which defines our bn curve:\n // y^2 = x^3 - 17; x = address\n let x = self.inner;\n let y_squared = pow(x, 3) - 17;\n\n // TODO (#8970): Handle cases where we cannot recover a point from an address\n let mut y = sqrt(y_squared);\n\n // If we get a negative y coordinate (any y where y > MAX_FIELD_VALUE / 2), we pin it to the\n // positive one (any value where y <= MAX_FIELD_VALUE / 2) by subtracting it from the Field modulus\n // note: The field modulus is MAX_FIELD_VALUE + 1\n if (!(y.lt(MAX_FIELD_VALUE / 2) | y.eq(MAX_FIELD_VALUE / 2))) {\n y = (MAX_FIELD_VALUE + 1) - y;\n }\n\n AddressPoint { inner: Point { x: self.inner, y, is_infinite: false } }\n }\n\n pub fn compute(public_keys: PublicKeys, partial_address: PartialAddress) -> AztecAddress {\n let public_keys_hash = public_keys.hash();\n\n let pre_address = poseidon2_hash_with_separator(\n [public_keys_hash.to_field(), partial_address.to_field()],\n GENERATOR_INDEX__CONTRACT_ADDRESS_V1,\n );\n\n let address_point = derive_public_key(EmbeddedCurveScalar::from_field(pre_address)).add(\n public_keys.ivpk_m.to_point(),\n );\n\n // Note that our address is only the x-coordinate of the full address_point. This is okay because when people want to encrypt something and send it to us\n // they can recover our full point using the x-coordinate (our address itself). To do this, they recompute the y-coordinate according to the equation y^2 = x^3 - 17.\n // When they do this, they may get a positive y-coordinate (a value that is less than or equal to MAX_FIELD_VALUE / 2) or\n // a negative y-coordinate (a value that is more than MAX_FIELD_VALUE), and we cannot dictate which one they get and hence the recovered point may sometimes be different than the one\n // our secret can decrypt. Regardless though, they should and will always encrypt using point with the positive y-coordinate by convention.\n // This ensures that everyone encrypts to the same point given an arbitrary x-coordinate (address). This is allowed because even though our original point may not have a positive y-coordinate,\n // with our original secret, we will be able to derive the secret to the point with the flipped (and now positive) y-coordinate that everyone encrypts to.\n AztecAddress::from_field(address_point.x)\n }\n\n pub fn compute_from_class_id(\n contract_class_id: ContractClassId,\n salted_initialization_hash: SaltedInitializationHash,\n public_keys: PublicKeys,\n ) -> Self {\n let partial_address = PartialAddress::compute_from_salted_initialization_hash(\n contract_class_id,\n salted_initialization_hash,\n );\n\n AztecAddress::compute(public_keys, partial_address)\n }\n\n pub fn is_protocol_contract(self) -> bool {\n self.inner.lt(MAX_PROTOCOL_CONTRACTS as Field)\n }\n\n pub fn is_zero(self) -> bool {\n self.inner == 0\n }\n\n pub fn assert_is_zero(self) {\n assert(self.to_field() == 0);\n }\n}\n\n#[test]\nfn compute_address_from_partial_and_pub_keys() {\n let public_keys = PublicKeys {\n npk_m: NpkM {\n inner: Point {\n x: 0x22f7fcddfa3ce3e8f0cc8e82d7b94cdd740afa3e77f8e4a63ea78a239432dcab,\n y: 0x0471657de2b6216ade6c506d28fbc22ba8b8ed95c871ad9f3e3984e90d9723a7,\n is_infinite: false,\n },\n },\n ivpk_m: IvpkM {\n inner: Point {\n x: 0x111223493147f6785514b1c195bb37a2589f22a6596d30bb2bb145fdc9ca8f1e,\n y: 0x273bbffd678edce8fe30e0deafc4f66d58357c06fd4a820285294b9746c3be95,\n is_infinite: false,\n },\n },\n ovpk_m: OvpkM {\n inner: Point {\n x: 0x09115c96e962322ffed6522f57194627136b8d03ac7469109707f5e44190c484,\n y: 0x0c49773308a13d740a7f0d4f0e6163b02c5a408b6f965856b6a491002d073d5b,\n is_infinite: false,\n },\n },\n tpk_m: TpkM {\n inner: Point {\n x: 0x00d3d81beb009873eb7116327cf47c612d5758ef083d4fda78e9b63980b2a762,\n y: 0x2f567d22d2b02fe1f4ad42db9d58a36afd1983e7e2909d1cab61cafedad6193a,\n is_infinite: false,\n },\n },\n };\n\n let partial_address = PartialAddress::from_field(\n 0x0a7c585381b10f4666044266a02405bf6e01fa564c8517d4ad5823493abd31de,\n );\n\n let address = AztecAddress::compute(public_keys, partial_address);\n\n // The following value was generated by `derivation.test.ts`.\n // --> Run the test with AZTEC_GENERATE_TEST_DATA=1 flag to update test data.\n let expected_computed_address_from_partial_and_pubkeys =\n 0x24e4646f58b9fbe7d38e317db8d5636c423fbbdfbe119fc190fe9c64747e0c62;\n assert(address.to_field() == expected_computed_address_from_partial_and_pubkeys);\n}\n\n#[test]\nfn compute_preaddress_from_partial_and_pub_keys() {\n let pre_address = poseidon2_hash_with_separator([1, 2], GENERATOR_INDEX__CONTRACT_ADDRESS_V1);\n let expected_computed_preaddress_from_partial_and_pubkey =\n 0x23ce9be3fa3c846b0f9245cc796902e731d04f086e8a42473bb29e405fc98075;\n assert(pre_address == expected_computed_preaddress_from_partial_and_pubkey);\n}\n\n#[test]\nfn from_field_to_field() {\n let address = AztecAddress { inner: 37 };\n assert_eq(FromField::from_field(address.to_field()), address);\n}\n\n#[test]\nfn serde() {\n let address = AztecAddress { inner: 37 };\n assert_eq(Deserialize::deserialize(address.serialize()), address);\n}\n" - }, - "279": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/noir-protocol-circuits/crates/types/src/debug_log.nr", - "source": "/// Utility function to console.log data in the acir simulator.\n/// Example:\n/// debug_log(\"blah blah this is a debug string\");\npub fn debug_log(msg: str) {\n debug_log_format(msg, []);\n}\n\n/// Utility function to console.log data in the acir simulator. This variant receives a format string in which the\n/// `${k}` tokens will be replaced with the k-eth value in the `args` array.\n/// Examples:\n/// debug_log_format(\"get_2(slot:{0}) =>\\n\\t0:{1}\\n\\t1:{2}\", [storage_slot, note0_hash, note1_hash]);\n/// debug_log_format(\"whole array: {}\", [e1, e2, e3, e4]);\npub fn debug_log_format(msg: str, args: [Field; N]) {\n // Safety: This oracle call returns nothing: we only call it for its side effects. It is therefore always safe\n // to call.\n unsafe { debug_log_oracle_wrapper(msg, args) };\n}\n\npub unconstrained fn debug_log_oracle_wrapper(\n msg: str,\n args: [Field; N],\n) {\n debug_log_oracle(msg, args.as_slice());\n}\n\n// WARNING: sometimes when using debug logs the ACVM errors with: `thrown: \"solver opcode resolution error: cannot solve opcode: expression has too many unknowns x155\"`\n#[oracle(debugLog)]\nunconstrained fn debug_log_oracle(_msg: str, args: [Field]) {}\n" - }, - "280": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/noir-protocol-circuits/crates/types/src/hash.nr", - "source": "use crate::{\n abis::{\n contract_class_function_leaf_preimage::ContractClassFunctionLeafPreimage,\n contract_class_log::ContractClassLog,\n function_selector::FunctionSelector,\n note_hash::ScopedNoteHash,\n nullifier::ScopedNullifier,\n private_log::{PrivateLog, PrivateLogData},\n side_effect::{OrderedValue, scoped::Scoped},\n },\n address::{AztecAddress, EthAddress},\n constants::{\n CONTRACT_CLASS_LOG_SIZE_IN_FIELDS, FUNCTION_TREE_HEIGHT, GENERATOR_INDEX__NOTE_HASH_NONCE,\n GENERATOR_INDEX__OUTER_NULLIFIER, GENERATOR_INDEX__SILOED_NOTE_HASH,\n GENERATOR_INDEX__UNIQUE_NOTE_HASH, TWO_POW_64,\n },\n merkle_tree::root::root_from_sibling_path,\n messaging::l2_to_l1_message::{L2ToL1Message, ScopedL2ToL1Message},\n poseidon2::Poseidon2Sponge,\n traits::{FromField, Hash, ToField},\n utils::{arrays::array_concat, field::{field_from_bytes, field_from_bytes_32_trunc}},\n};\n\npub fn sha256_to_field(bytes_to_hash: [u8; N]) -> Field {\n let sha256_hashed = sha256::digest(bytes_to_hash);\n let hash_in_a_field = field_from_bytes_32_trunc(sha256_hashed);\n\n hash_in_a_field\n}\n\npub fn private_functions_root_from_siblings(\n selector: FunctionSelector,\n vk_hash: Field,\n function_leaf_index: Field,\n function_leaf_sibling_path: [Field; FUNCTION_TREE_HEIGHT],\n) -> Field {\n let function_leaf_preimage = ContractClassFunctionLeafPreimage { selector, vk_hash };\n let function_leaf = function_leaf_preimage.hash();\n root_from_sibling_path(\n function_leaf,\n function_leaf_index,\n function_leaf_sibling_path,\n )\n}\n\npub fn compute_note_hash_nonce(first_nullifier_in_tx: Field, note_index_in_tx: u32) -> Field {\n // Hashing the first nullifier with note index in tx is guaranteed to be unique (because all nullifiers are also\n // unique).\n poseidon2_hash_with_separator(\n [first_nullifier_in_tx, note_index_in_tx as Field],\n GENERATOR_INDEX__NOTE_HASH_NONCE,\n )\n}\n\npub fn compute_unique_note_hash(nonce: Field, siloed_note_hash: Field) -> Field {\n let inputs = [nonce, siloed_note_hash];\n poseidon2_hash_with_separator(inputs, GENERATOR_INDEX__UNIQUE_NOTE_HASH)\n}\n\npub fn compute_siloed_note_hash(app: AztecAddress, note_hash: Field) -> Field {\n poseidon2_hash_with_separator(\n [app.to_field(), note_hash],\n GENERATOR_INDEX__SILOED_NOTE_HASH,\n )\n}\n\n/// Computes unique note hashes from siloed note hashes\npub fn compute_unique_siloed_note_hash(\n siloed_note_hash: Field,\n first_nullifier: Field,\n note_index_in_tx: u32,\n) -> Field {\n if siloed_note_hash == 0 {\n 0\n } else {\n let nonce = compute_note_hash_nonce(first_nullifier, note_index_in_tx);\n compute_unique_note_hash(nonce, siloed_note_hash)\n }\n}\n\n/// Siloing in the context of Aztec refers to the process of hashing a note hash with a contract address (this way\n/// the note hash is scoped to a specific contract). This is used to prevent intermingling of notes between contracts.\npub fn silo_note_hash(note_hash: ScopedNoteHash) -> Field {\n if note_hash.contract_address.is_zero() {\n 0\n } else {\n compute_siloed_note_hash(note_hash.contract_address, note_hash.value())\n }\n}\n\npub fn compute_siloed_nullifier(app: AztecAddress, nullifier: Field) -> Field {\n poseidon2_hash_with_separator(\n [app.to_field(), nullifier],\n GENERATOR_INDEX__OUTER_NULLIFIER,\n )\n}\n\npub fn silo_nullifier(nullifier: ScopedNullifier) -> Field {\n if nullifier.contract_address.is_zero() {\n nullifier.value() // Return value instead of 0 because the first nullifier's contract address is zero.\n } else {\n compute_siloed_nullifier(nullifier.contract_address, nullifier.value())\n }\n}\n\npub fn compute_siloed_private_log_field(contract_address: AztecAddress, field: Field) -> Field {\n poseidon2_hash([contract_address.to_field(), field])\n}\n\npub fn silo_private_log(private_log: Scoped) -> PrivateLog {\n if private_log.contract_address.is_zero() {\n private_log.inner.log\n } else {\n let mut fields = private_log.inner.log.fields;\n fields[0] = compute_siloed_private_log_field(private_log.contract_address, fields[0]);\n PrivateLog::new(fields, private_log.inner.log.length)\n }\n}\n\npub fn compute_siloed_contract_class_log_field(\n contract_address: AztecAddress,\n first_field: Field,\n) -> Field {\n poseidon2_hash([contract_address.to_field(), first_field])\n}\n\npub fn silo_contract_class_log(contract_class_log: ContractClassLog) -> ContractClassLog {\n if contract_class_log.contract_address.is_zero() {\n contract_class_log\n } else {\n let mut log = contract_class_log;\n log.log.fields[0] = compute_siloed_contract_class_log_field(\n contract_class_log.contract_address,\n log.log.fields[0],\n );\n log\n }\n}\n\npub fn compute_contract_class_log_hash(log: [Field; CONTRACT_CLASS_LOG_SIZE_IN_FIELDS]) -> Field {\n poseidon2_hash(log)\n}\n\npub fn merkle_hash(left: Field, right: Field) -> Field {\n poseidon2_hash([left, right])\n}\n\npub fn compute_l2_to_l1_hash(\n contract_address: AztecAddress,\n recipient: EthAddress,\n content: Field,\n rollup_version_id: Field,\n chain_id: Field,\n) -> Field {\n let mut bytes: [u8; 160] = std::mem::zeroed();\n\n let inputs =\n [contract_address.to_field(), rollup_version_id, recipient.to_field(), chain_id, content];\n for i in 0..5 {\n // TODO are bytes be in fr.to_buffer() ?\n let item_bytes: [u8; 32] = inputs[i].to_be_bytes();\n for j in 0..32 {\n bytes[32 * i + j] = item_bytes[j];\n }\n }\n\n sha256_to_field(bytes)\n}\n\npub fn silo_l2_to_l1_message(\n msg: ScopedL2ToL1Message,\n rollup_version_id: Field,\n chain_id: Field,\n) -> Field {\n if msg.contract_address.is_zero() {\n 0\n } else {\n compute_l2_to_l1_hash(\n msg.contract_address,\n msg.message.recipient,\n msg.message.content,\n rollup_version_id,\n chain_id,\n )\n }\n}\n\n// Computes sha256 hash of 2 input hashes.\n//\n// NB: This method now takes in two 31 byte fields - it assumes that any input\n// is the result of a sha_to_field hash and => is truncated\n//\n// TODO(Jan and David): This is used for the encrypted_log hashes.\n// Can we check to see if we can just use hash_to_field or pedersen_compress here?\n//\npub fn accumulate_sha256(input: [Field; 2]) -> Field {\n // This is a note about the cpp code, since it takes an array of Fields\n // instead of a u128.\n // 4 Field elements when converted to bytes will usually\n // occupy 4 * 32 = 128 bytes.\n // However, this function is making the assumption that each Field\n // only occupies 128 bits.\n //\n // TODO(David): This does not seem to be getting guaranteed anywhere in the code?\n // Concatentate two fields into 32x2 = 64 bytes\n // accumulate_sha256 assumes that the inputs are pre-truncated 31 byte numbers\n let mut hash_input_flattened = [0; 64];\n for offset in 0..input.len() {\n let input_as_bytes: [u8; 32] = input[offset].to_be_bytes();\n for byte_index in 0..32 {\n hash_input_flattened[offset * 32 + byte_index] = input_as_bytes[byte_index];\n }\n }\n\n sha256_to_field(hash_input_flattened)\n}\n\npub fn verification_key_hash(key: [Field; N]) -> Field {\n crate::hash::poseidon2_hash(key)\n}\n\n#[inline_always]\npub fn pedersen_hash(inputs: [Field; N], hash_index: u32) -> Field {\n std::hash::pedersen_hash_with_separator(inputs, hash_index)\n}\n\npub fn poseidon2_hash(inputs: [Field; N]) -> Field {\n poseidon::poseidon2::Poseidon2::hash(inputs, N)\n}\n\n#[no_predicates]\npub fn poseidon2_hash_with_separator(inputs: [Field; N], separator: T) -> Field\nwhere\n T: ToField,\n{\n let inputs_with_separator = array_concat([separator.to_field()], inputs);\n poseidon2_hash(inputs_with_separator)\n}\n\n// Performs a fixed length hash with a subarray of the given input.\n// Useful for SpongeBlob in which we aborb M things and want to check it vs a hash of M elts of an N-len array.\n// Using stdlib poseidon, this will always absorb an extra 1 as a 'variable' hash, and not match spongeblob.squeeze()\n// or any ts implementation. Also checks that any remaining elts not hashed are empty.\n#[no_predicates]\npub fn poseidon2_hash_subarray(input: [Field; N], in_len: u32) -> Field {\n let mut sponge = poseidon2_absorb_chunks(input, in_len, false);\n sponge.squeeze()\n}\n\n// NB the below is the same as poseidon::poseidon2::Poseidon2::hash(), but replacing a range check with a bit check,\n// and absorbing in chunks of 3 below.\n#[no_predicates]\npub fn poseidon2_cheaper_variable_hash(input: [Field; N], in_len: u32) -> Field {\n let mut sponge = poseidon2_absorb_chunks(input, in_len, true);\n // In the case where the hash preimage is variable-length, we append `1` to the end of the input, to distinguish\n // from fixed-length hashes. (the combination of this additional field element + the hash IV ensures\n // fixed-length and variable-length hashes do not collide)\n if in_len != N {\n sponge.absorb(1);\n }\n sponge.squeeze()\n}\n\n// The below fn reduces gates of a conditional poseidon2 hash by approx 3x (thank you ~* Giant Brain Dev @IlyasRidhuan *~ for the idea)\n// Why? Because when we call stdlib poseidon, we call absorb for each item. When absorbing is conditional, it seems the compiler does not know\n// what cache_size will be when calling absorb, so it assigns the permutation gates for /each i/ rather than /every 3rd i/, which is actually required.\n// The below code forces the compiler to:\n// - absorb normally up to 2 times to set cache_size to 1\n// - absorb in chunks of 3 to ensure perm. only happens every 3rd absorb\n// - absorb normally up to 2 times to add any remaining values to the hash\n// In fixed len hashes, the compiler is able to tell that it will only need to perform the permutation every 3 absorbs.\n// NB: it also replaces unnecessary range checks (i < thing) with a bit check (&= i != thing), which alone reduces the gates of a var. hash by half.\n\n#[no_predicates]\nfn poseidon2_absorb_chunks(\n input: [Field; N],\n in_len: u32,\n variable: bool,\n) -> Poseidon2Sponge {\n let iv: Field = (in_len as Field) * TWO_POW_64;\n let mut sponge = Poseidon2Sponge::new(iv);\n // Even though shift is always 1 here, if we input in_len = 0 we get an underflow\n // since we cannot isolate computation branches. The below is just to avoid that.\n let shift = if in_len == 0 { 0 } else { 1 };\n if in_len != 0 {\n // cache_size = 0, init absorb\n sponge.cache[0] = input[0];\n sponge.cache_size = 1;\n // shift = num elts already added to make cache_size 1 = 1 for a fresh sponge\n // M = max_chunks = (N - 1 - (N - 1) % 3) / 3: (must be written as a fn of N to compile)\n // max_remainder = (N - 1) % 3;\n // max_chunks = (N - 1 - max_remainder) / 3;\n sponge = poseidon2_absorb_chunks_loop::(\n sponge,\n input,\n in_len,\n variable,\n shift,\n );\n }\n sponge\n}\n\n// NB: If it's not required to check that the non-absorbed elts of 'input' are 0s, set skip_0_check=true\n#[no_predicates]\npub fn poseidon2_absorb_chunks_existing_sponge(\n in_sponge: Poseidon2Sponge,\n input: [Field; N],\n in_len: u32,\n skip_0_check: bool,\n) -> Poseidon2Sponge {\n let mut sponge = in_sponge;\n // 'shift' is to account for already added inputs\n let mut shift = 0;\n // 'stop' is to avoid an underflow when inputting in_len = 0\n let mut stop = false;\n for i in 0..3 {\n if shift == in_len {\n stop = true;\n }\n if (sponge.cache_size != 1) & (!stop) {\n sponge.absorb(input[i]);\n shift += 1;\n }\n }\n sponge = if stop {\n sponge\n } else {\n // max_chunks = (N - (N % 3)) / 3;\n poseidon2_absorb_chunks_loop::(\n sponge,\n input,\n in_len,\n skip_0_check,\n shift,\n )\n };\n sponge\n}\n\n// The below is the loop to absorb elts into a poseidon sponge in chunks of 3\n// shift - the num of elts already absorbed to ensure the sponge's cache_size = 1\n// M - the max number of chunks required to absorb N things (must be comptime to compile)\n// NB: The 0 checks ('Found non-zero field...') are messy, but having a separate loop over N to check\n// for 0s costs 3N gates. Current approach is approx 2N gates.\n#[no_predicates]\nfn poseidon2_absorb_chunks_loop(\n in_sponge: Poseidon2Sponge,\n input: [Field; N],\n in_len: u32,\n variable: bool,\n shift: u32,\n) -> Poseidon2Sponge {\n assert(in_len <= N, \"Given in_len to absorb is larger than the input array len\");\n // When we have an existing sponge, we may have a shift of 0, and the final 'k+2' below = N\n // The below avoids an overflow\n let skip_last = 3 * M == N;\n // Writing in_sponge: &mut does not compile\n let mut sponge = in_sponge;\n let mut should_add = true;\n // The num of things left over after absorbing in 3s\n let remainder = (in_len - shift) % 3;\n // The num of chunks of 3 to absorb (maximum M)\n let chunks = (in_len - shift - remainder) / 3;\n for i in 0..M {\n // Now we loop through cache size = 1 -> 3\n should_add &= i != chunks;\n // This is the index at the start of the chunk (for readability)\n let k = 3 * i + shift;\n if should_add {\n // cache_size = 1, 2 => just assign\n sponge.cache[1] = input[k];\n sponge.cache[2] = input[k + 1];\n // cache_size = 3 => duplex + perm\n for j in 0..3 {\n sponge.state[j] += sponge.cache[j];\n }\n sponge.state = std::hash::poseidon2_permutation(sponge.state, 4);\n sponge.cache[0] = input[k + 2];\n // cache_size is now 1 again, repeat loop\n } else if (!variable) & (i != chunks) {\n // if we are hashing a fixed len array which is a subarray, we check the remaining elts are 0\n // NB: we don't check at i == chunks, because that chunk contains elts to be absorbed or checked below\n let last_0 = if (i == M - 1) & (skip_last) {\n 0\n } else {\n input[k + 2]\n };\n let all_0 = (input[k] == 0) & (input[k + 1] == 0) & (last_0 == 0);\n assert(all_0, \"Found non-zero field after breakpoint\");\n }\n }\n // we have 'remainder' num of items left to absorb\n should_add = true;\n // below is to avoid overflows (i.e. if inlen is close to N)\n let mut should_check = !variable;\n for i in 0..3 {\n should_add &= i != remainder;\n should_check &= in_len - remainder + i != N;\n if should_add {\n // we want to absorb the final 'remainder' items\n sponge.absorb(input[in_len - remainder + i]);\n } else if should_check {\n assert(input[in_len - remainder + i] == 0, \"Found non-zero field after breakpoint\");\n }\n }\n sponge\n}\n\npub fn poseidon2_hash_with_separator_slice(inputs: [Field], separator: T) -> Field\nwhere\n T: ToField,\n{\n let in_len = inputs.len() + 1;\n let iv: Field = (in_len as Field) * TWO_POW_64;\n let mut sponge = Poseidon2Sponge::new(iv);\n sponge.absorb(separator.to_field());\n\n for i in 0..inputs.len() {\n sponge.absorb(inputs[i]);\n }\n\n sponge.squeeze()\n}\n\n#[no_predicates]\npub fn poseidon2_hash_bytes(inputs: [u8; N]) -> Field {\n let mut fields = [0; (N + 30) / 31];\n let mut field_index = 0;\n let mut current_field = [0; 31];\n for i in 0..inputs.len() {\n let index = i % 31;\n current_field[index] = inputs[i];\n if index == 30 {\n fields[field_index] = field_from_bytes(current_field, false);\n current_field = [0; 31];\n field_index += 1;\n }\n }\n if field_index != fields.len() {\n fields[field_index] = field_from_bytes(current_field, false);\n }\n poseidon2_hash(fields)\n}\n\n#[test]\nfn poseidon_chunks_matches_fixed() {\n let in_len = 501;\n let mut input: [Field; 4096] = [0; 4096];\n let mut fixed_input = [3; 501];\n assert(in_len == fixed_input.len()); // sanity check\n for i in 0..in_len {\n input[i] = 3;\n }\n let sub_chunk_hash = poseidon2_hash_subarray(input, in_len);\n let fixed_len_hash = poseidon::poseidon2::Poseidon2::hash(fixed_input, fixed_input.len());\n assert(sub_chunk_hash == fixed_len_hash);\n}\n\n#[test]\nfn poseidon_chunks_matches_variable() {\n let in_len = 501;\n let mut input: [Field; 4096] = [0; 4096];\n for i in 0..in_len {\n input[i] = 3;\n }\n let variable_chunk_hash = poseidon2_cheaper_variable_hash(input, in_len);\n let variable_len_hash = poseidon::poseidon2::Poseidon2::hash(input, in_len);\n assert(variable_chunk_hash == variable_len_hash);\n}\n\n#[test]\nfn existing_sponge_poseidon_chunks_matches_fixed() {\n let in_len = 501;\n let mut input: [Field; 4096] = [0; 4096];\n let mut fixed_input = [3; 501];\n assert(in_len == fixed_input.len()); // sanity check\n for i in 0..in_len {\n input[i] = 3;\n }\n // absorb 250 of the 501 things\n let empty_sponge = Poseidon2Sponge::new((in_len as Field) * TWO_POW_64);\n let first_sponge = poseidon2_absorb_chunks_existing_sponge(empty_sponge, input, 250, true);\n // now absorb the final 251 (since they are all 3s, im being lazy and not making a new array)\n let mut final_sponge = poseidon2_absorb_chunks_existing_sponge(first_sponge, input, 251, true);\n let fixed_len_hash = Poseidon2Sponge::hash(fixed_input, fixed_input.len());\n assert(final_sponge.squeeze() == fixed_len_hash);\n}\n\n#[test]\nfn poseidon_chunks_empty_inputs() {\n let in_len = 0;\n let mut input: [Field; 4096] = [0; 4096];\n let mut constructed_empty_sponge = poseidon2_absorb_chunks(input, in_len, true);\n let mut first_sponge =\n poseidon2_absorb_chunks_existing_sponge(constructed_empty_sponge, input, in_len, true);\n assert(first_sponge.squeeze() == constructed_empty_sponge.squeeze());\n}\n\n#[test]\nfn smoke_sha256_to_field() {\n let full_buffer = [\n 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,\n 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,\n 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70,\n 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93,\n 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,\n 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130,\n 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148,\n 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,\n ];\n let result = sha256_to_field(full_buffer);\n\n assert(result == 0x448ebbc9e1a31220a2f3830c18eef61b9bd070e5084b7fa2a359fe729184c7);\n\n // to show correctness of the current ver (truncate one byte) vs old ver (mod full bytes):\n let result_bytes = sha256::digest(full_buffer);\n let truncated_field = crate::utils::field::field_from_bytes_32_trunc(result_bytes);\n assert(truncated_field == result);\n let mod_res = result + (result_bytes[31] as Field);\n assert(mod_res == 0x448ebbc9e1a31220a2f3830c18eef61b9bd070e5084b7fa2a359fe729184e0);\n}\n\n#[test]\nfn compute_l2_l1_hash() {\n // All zeroes\n let hash_result =\n compute_l2_to_l1_hash(AztecAddress::from_field(0), EthAddress::zero(), 0, 0, 0);\n assert(hash_result == 0xb393978842a0fa3d3e1470196f098f473f9678e72463cb65ec4ab5581856c2);\n\n // Non-zero case\n let hash_result = compute_l2_to_l1_hash(\n AztecAddress::from_field(1),\n EthAddress::from_field(3),\n 5,\n 2,\n 4,\n );\n assert(hash_result == 0x3f88c1044a05e5340ed20466276500f6d45ca5603913b9091e957161734e16);\n}\n\n#[test]\nfn silo_l2_to_l1_message_matches_typescript() {\n let version = 4;\n let chainId = 5;\n\n let hash = silo_l2_to_l1_message(\n ScopedL2ToL1Message {\n message: L2ToL1Message { recipient: EthAddress::from_field(1), content: 2, counter: 0 },\n contract_address: AztecAddress::from_field(3),\n },\n version,\n chainId,\n );\n\n // The following value was generated by `l2_to_l1_message.test.ts`\n let hash_from_typescript = 0x00c6155d69febb9d5039b374dd4f77bf57b7c881709aa524a18acaa0bd57476a;\n\n assert_eq(hash, hash_from_typescript);\n}\n" - }, - "294": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/noir-protocol-circuits/crates/types/src/meta/mod.nr", - "source": "use super::traits::{Deserialize, Packable, Serialize};\n\n/// Returns the typed expression of a trait method implementation.\n///\n/// This helper function is preferred over directly inlining with `$typ::target_method()` in a quote,\n/// as direct inlining would result in missing import warnings in the generated code (specifically,\n/// warnings that the trait implementation is not in scope).\n///\n/// # Note\n/// A copy of this function exists in `aztec-nr/aztec/src/macros/utils.nr`. We maintain separate copies\n/// because importing it there from here would cause the `target_trait` to be interpreted in the context\n/// of this crate, making it impossible to compile code for traits from that crate (e.g. NoteType).\ncomptime fn get_trait_impl_method(\n typ: Type,\n target_trait: Quoted,\n target_method: Quoted,\n) -> TypedExpr {\n let trait_constraint = target_trait.as_trait_constraint();\n typ\n .get_trait_impl(trait_constraint)\n .expect(f\"Could not find impl for {target_trait} for type {typ}\")\n .methods()\n .filter(|m| m.name() == target_method)[0]\n .as_typed_expr()\n}\n\n/// Generates code that deserializes a struct, primitive type, array or string from a field array.\n///\n/// # Parameters\n/// - `name`: The name of the current field being processed, used to identify fields for replacement.\n/// - `typ`: The type of the struct or field being deserialized (e.g., a custom struct, array, or primitive).\n/// - `field_array_name`: The name of the field array containing serialized field data (e.g., `\"values\"`).\n/// - `num_already_consumed`: The number of fields already processed in previous recursion calls.\n/// - `should_unpack`: A boolean indicating whether the type should be unpacked (see description of `Packable`\n/// and `Serialize` trait for more information about the difference between packing and serialization).\n///\n/// # Returns\n/// A tuple containing:\n/// - `Quoted`: A code that deserializes a given struct, primitive type, array, or string from the field array.\n/// - `u32`: The total number of fields consumed during deserialization (used for recursion).\n///\n/// # Nested Struct Example\n/// Given the following setup:\n/// ```\n/// struct UintNote {\n/// value: u128,\n/// owner: AztecAddress,\n/// randomness: Field,\n/// }\n///\n/// struct AztecAddress {\n/// inner: Field,\n/// }\n/// ```\n///\n/// If `UintNote` is the input type, the function will generate the following deserialization code:\n/// ```\n/// UintNote {\n/// value: fields[0] as u128,\n/// owner: AztecAddress {\n/// inner: fields[1],\n/// },\n/// randomness: fields[2],\n/// }\n/// ```\n/// # Nested Struct Example with Unpacking\n/// - given the same setup as above and given that u128, AztecAddress and Field implement the `Packable` trait\n/// the result we get is:\n/// ```\n/// UintNote {\n/// value: aztec::protocol_types::traits::Packable::unpack([fields[0]]),\n/// owner: aztec::protocol_types::traits::Packable::unpack([fields[1]]),\n/// randomness: aztec::protocol_types::traits::Packable::unpack([fields[2]]),\n/// }\n/// ```\n///\n/// # Panics\n/// - If the deserialization logic encounters a type it does not support.\n/// - If an incorrect number of fields are consumed when deserializing a string.\npub comptime fn generate_deserialize_from_fields(\n name: Quoted,\n typ: Type,\n field_array_name: Quoted,\n num_already_consumed: u32,\n should_unpack: bool,\n) -> (Quoted, u32) {\n let mut result = quote {};\n // Counter for the number of fields consumed\n let mut consumed_counter: u32 = 0;\n\n // If the type implements `Packable`, its length will be assigned to the `maybe_packed_len_typ` variable.\n let maybe_packed_len_typ = std::meta::typ::fresh_type_variable();\n let packable_constraint = quote { Packable<$maybe_packed_len_typ> }.as_trait_constraint();\n\n if (should_unpack & typ.implements(packable_constraint)) {\n // Unpacking is enabled and the given type implements the `Packable` trait so we call the `unpack()`\n // method, add the resulting field array to `aux_vars` and each field to `fields`.\n let packed_len = maybe_packed_len_typ.as_constant().unwrap();\n\n // We copy the packed fields into a new array and pass that to the unpack function in a quote\n let mut packed_fields_quotes = &[];\n for i in 0..packed_len {\n let index_in_field_array = i + num_already_consumed;\n packed_fields_quotes =\n packed_fields_quotes.push_back(quote { $field_array_name[$index_in_field_array] });\n }\n let packed_fields = packed_fields_quotes.join(quote {,});\n\n // Now we call unpack on the type\n let unpack_method = get_trait_impl_method(typ, quote { Packable<_> }, quote { unpack });\n result = quote { $unpack_method([ $packed_fields ]) };\n\n consumed_counter = packed_len;\n } else if typ.is_field() | typ.as_integer().is_some() | typ.is_bool() {\n // The field is a primitive so we just reference it in the field array\n result = quote { $field_array_name[$num_already_consumed] as $typ };\n consumed_counter = 1;\n } else if typ.as_data_type().is_some() {\n // The field is a struct so we iterate over each struct field and recursively call\n // `generate_deserialize_from_fields`\n let (nested_def, generics) = typ.as_data_type().unwrap();\n let nested_name = nested_def.name();\n let mut deserialized_fields_list = &[];\n\n // Iterate over each field in the struct\n for field in nested_def.fields(generics) {\n let (field_name, field_type) = field;\n // Recursively call `generate_deserialize_from_fields` for each field in the struct\n let (deserialized_field, num_consumed_in_recursion) = generate_deserialize_from_fields(\n field_name,\n field_type,\n field_array_name,\n consumed_counter + num_already_consumed,\n should_unpack,\n );\n // We increment the consumed counter by the number of fields consumed in the recursion\n consumed_counter += num_consumed_in_recursion;\n // We add the deserialized field to the list of deserialized fields.\n // E.g. `value: u128 { lo: fields[0], hi: fields[1] }`\n deserialized_fields_list =\n deserialized_fields_list.push_back(quote { $field_name: $deserialized_field });\n }\n\n // We can construct the struct from the deserialized fields\n let deserialized_fields = deserialized_fields_list.join(quote {,});\n result = quote {\n $nested_name {\n $deserialized_fields\n }\n };\n } else if typ.as_array().is_some() {\n // The field is an array so we iterate over each element and recursively call\n // `generate_deserialize_from_fields`\n let (element_type, array_len) = typ.as_array().unwrap();\n let array_len = array_len.as_constant().unwrap();\n let mut array_fields_list = &[];\n\n // Iterate over each element in the array\n for _ in 0..array_len {\n // Recursively call `generate_deserialize_from_fields` for each element in the array\n let (deserialized_field, num_consumed_in_recursion) = generate_deserialize_from_fields(\n name,\n element_type,\n field_array_name,\n consumed_counter + num_already_consumed,\n should_unpack,\n );\n // We increment the consumed counter by the number of fields consumed in the recursion\n consumed_counter += num_consumed_in_recursion;\n // We add the deserialized field to the list of deserialized fields.\n array_fields_list = array_fields_list.push_back(deserialized_field);\n }\n\n // We can construct the array from the deserialized fields\n let array_fields = array_fields_list.join(quote {,});\n result = quote { [ $array_fields ] };\n } else if typ.as_str().is_some() {\n // The field is a string and we expect each byte of the string to be represented as 1 field in the field\n // array. So we iterate over the string length and deserialize each character as u8 in the recursive call\n // to `generate_deserialize_from_fields`.\n let length_type = typ.as_str().unwrap();\n let str_len = length_type.as_constant().unwrap();\n let mut byte_list = &[];\n\n // Iterate over each character in the string\n for _ in 0..str_len {\n // Recursively call `generate_deserialize_from_fields` for each character in the string\n let (deserialized_field, num_consumed_in_recursion) = generate_deserialize_from_fields(\n name,\n quote {u8}.as_type(),\n field_array_name,\n consumed_counter + num_already_consumed,\n should_unpack,\n );\n\n // We should consume just one field in the recursion so we sanity check that\n assert_eq(\n num_consumed_in_recursion,\n 1,\n \"Incorrect number of fields consumed in string deserialization\",\n );\n\n // We increment the consumed counter by 1 as we have consumed one field\n consumed_counter += 1;\n\n // We add the deserialized field to the list of deserialized fields.\n // E.g. `fields[6] as u8`\n byte_list = byte_list.push_back(deserialized_field);\n }\n\n // We construct the string from the deserialized fields\n let bytes = byte_list.join(quote {,});\n result = quote { [ $bytes ].as_str_unchecked() };\n } else {\n panic(\n f\"Unsupported type for serialization of argument {name} and type {typ}\",\n )\n }\n\n (result, consumed_counter)\n}\n\n/// Generates code that serializes a type into an array of fields. Also generates auxiliary variables if necessary\n/// for serialization. If `should_pack` is true, we check if the type implements the `Packable` trait and pack it\n/// if it does.\n///\n/// # Parameters\n/// - `name`: The base identifier (e.g., `self`, `some_var`).\n/// - `typ`: The type being serialized (e.g., a custom struct, array, or primitive type).\n/// - `should_pack`: A boolean indicating whether the type should be packed.\n///\n/// # Returns\n/// A tuple containing:\n/// - A flattened array of `Quoted` field references representing the serialized fields.\n/// - An array of `Quoted` auxiliary variables needed for serialization, such as byte arrays for strings.\n///\n/// # Examples\n///\n/// ## Struct\n/// Given the following struct:\n/// ```rust\n/// struct MockStruct {\n/// a: Field,\n/// b: Field,\n/// }\n/// ```\n///\n/// Serializing the struct:\n/// ```rust\n/// generate_serialize_to_fields(quote { my_mock_struct }, MockStruct, false)\n/// // Returns:\n/// // ([`my_mock_struct.a`, `my_mock_struct.b`], [])\n/// ```\n///\n/// ## Nested Struct\n/// For a more complex struct:\n/// ```rust\n/// struct NestedStruct {\n/// m1: MockStruct,\n/// m2: MockStruct,\n/// }\n/// ```\n///\n/// Serialization output:\n/// ```rust\n/// generate_serialize_to_fields(quote { self }, NestedStruct, false)\n/// // Returns:\n/// // ([`self.m1.a`, `self.m1.b`, `self.m2.a`, `self.m2.b`], [])\n/// ```\n///\n/// ## Array\n/// For an array type:\n/// ```rust\n/// generate_serialize_to_fields(quote { my_array }, [Field; 3], false)\n/// // Returns:\n/// // ([`my_array[0]`, `my_array[1]`, `my_array[2]`], [])\n/// ```\n///\n/// ## String\n/// For a string field, where each character is serialized as a `Field`:\n/// ```rust\n/// generate_serialize_to_fields(quote { my_string }, StringType, false)\n/// // Returns:\n/// // ([`my_string_as_bytes[0] as Field`, `my_string_as_bytes[1] as Field`, ...],\n/// // [`let my_string_as_bytes = my_string.as_bytes()`])\n/// ```\n///\n/// ## Nested Struct with packing enabled\n/// - u128 has a `Packable` implementation hence it will be packed.\n///\n/// For a more complex struct:\n/// ```rust\n/// struct MyStruct {\n/// value: u128,\n/// value2: Field,\n/// }\n/// ```\n///\n/// # Panics\n/// - If the type is unsupported for serialization.\n/// - If the provided `typ` contains invalid constants or incompatible structures.\npub comptime fn generate_serialize_to_fields(\n name: Quoted,\n typ: Type,\n should_pack: bool,\n) -> ([Quoted], [Quoted]) {\n let mut fields = &[];\n let mut aux_vars = &[];\n\n // If the type implements `Packable`, its length will be assigned to the `maybe_packed_len_typ` variable.\n let maybe_packed_len_typ = std::meta::typ::fresh_type_variable();\n let packable_constraint =\n quote { crate::traits::Packable<$maybe_packed_len_typ> }.as_trait_constraint();\n\n if (should_pack & typ.implements(packable_constraint)) {\n // Packing is enabled and the given type implements the `Packable` trait so we call the `pack()`\n // method, add the resulting field array to `aux_vars` and each field to `fields`.\n let packed_len = maybe_packed_len_typ.as_constant().unwrap();\n\n // We collapse the name to a one that gets tokenized as a single token (e.g. \"self.value\" -> \"self_value\").\n let name_at_one_token = collapse_to_one_token(name);\n let packed_struct_name = f\"{name_at_one_token}_aux_var\".quoted_contents();\n\n // We add the individual fields to the fields array\n let pack_method = get_trait_impl_method(\n typ,\n quote { crate::traits::Packable<$packed_len> },\n quote { pack },\n );\n let packed_struct = quote { let $packed_struct_name = $pack_method($name) };\n for i in 0..packed_len {\n fields = fields.push_back(quote { $packed_struct_name[$i] });\n }\n\n // We add the new auxiliary variable to the aux_vars array\n aux_vars = aux_vars.push_back(packed_struct);\n } else if typ.is_field() {\n // For field we just add the value to fields\n fields = fields.push_back(name);\n } else if typ.as_integer().is_some() | typ.is_bool() {\n // For integer and bool we just cast to Field and add the value to fields\n fields = fields.push_back(quote { $name as Field });\n } else if typ.as_data_type().is_some() {\n // For struct we pref\n let nested_struct = typ.as_data_type().unwrap();\n let params = nested_struct.0.fields(nested_struct.1);\n let struct_flattened = params.map(|(param_name, param_type): (Quoted, Type)| {\n let maybe_prefixed_name = if name == quote {} {\n // Triggered when the param name is of a value available in the current scope (e.g. a function\n // argument) --> then we don't prefix the name with anything.\n param_name\n } else {\n // Triggered when we want to prefix the param name with the `name` from function input. This\n // can typically be `self` when implementing a method on a struct.\n quote { $name.$param_name }\n };\n generate_serialize_to_fields(quote {$maybe_prefixed_name}, param_type, should_pack)\n });\n let struct_flattened_fields = struct_flattened.fold(\n &[],\n |acc: [Quoted], (fields, _): (_, [Quoted])| acc.append(fields),\n );\n let struct_flattened_aux_vars = struct_flattened.fold(\n &[],\n |acc: [Quoted], (_, aux_vars): ([Quoted], _)| acc.append(aux_vars),\n );\n fields = fields.append(struct_flattened_fields);\n aux_vars = aux_vars.append(struct_flattened_aux_vars);\n } else if typ.as_array().is_some() {\n // For array we recursively call `generate_serialize_to_fields(...)` for each element\n let (element_type, array_len) = typ.as_array().unwrap();\n let array_len = array_len.as_constant().unwrap();\n for i in 0..array_len {\n let (element_fields, element_aux_vars) =\n generate_serialize_to_fields(quote { $name[$i] }, element_type, should_pack);\n fields = fields.append(element_fields);\n aux_vars = aux_vars.append(element_aux_vars);\n }\n } else if typ.as_str().is_some() {\n // For string we convert the value to bytes, we store the `as_bytes` in an auxiliary variables and\n // then we add each byte to fields as a Field\n let length_type = typ.as_str().unwrap();\n let str_len = length_type.as_constant().unwrap();\n let as_member = name.as_expr().unwrap().as_member_access();\n let var_name = if as_member.is_some() {\n as_member.unwrap().1\n } else {\n name\n };\n let as_bytes_name = f\"{var_name}_as_bytes\".quoted_contents();\n let as_bytes = quote { let $as_bytes_name = $name.as_bytes() };\n for i in 0..str_len {\n fields = fields.push_back(quote { $as_bytes_name[$i] as Field });\n }\n aux_vars = aux_vars.push_back(as_bytes);\n } else {\n panic(\n f\"Unsupported type for serialization of argument {name} and type {typ}\",\n )\n }\n\n (fields, aux_vars)\n}\n\n/// From a quote that gets tokenized to a multiple tokens we collapse it to a single token by replacing all `.` with `_`.\n/// E.g. \"self.values[0]\" -> \"self_values_0_\"\ncomptime fn collapse_to_one_token(q: Quoted) -> Quoted {\n let tokens = q.tokens();\n\n let mut single_token = quote {};\n for token in tokens {\n let new_token = if ((token == quote {.}) | (token == quote {[}) | (token == quote {]})) {\n quote {_}\n } else {\n token\n };\n single_token = f\"{single_token}{new_token}\".quoted_contents();\n }\n single_token\n}\n\npub(crate) comptime fn derive_serialize(s: TypeDefinition) -> Quoted {\n let typ = s.as_type();\n let (fields, aux_vars) = generate_serialize_to_fields(quote { self }, typ, false);\n let aux_vars_for_serialization = if aux_vars.len() > 0 {\n let joint = aux_vars.join(quote {;});\n quote { $joint; }\n } else {\n quote {}\n };\n\n let field_serializations = fields.join(quote {,});\n let serialized_len = fields.len();\n quote {\n impl Serialize<$serialized_len> for $typ {\n #[inline_always]\n fn serialize(self) -> [Field; $serialized_len] {\n $aux_vars_for_serialization\n [ $field_serializations ]\n }\n }\n }\n}\n\npub(crate) comptime fn derive_deserialize(s: TypeDefinition) -> Quoted {\n let typ = s.as_type();\n let (fields, _) = generate_serialize_to_fields(quote { self }, typ, false);\n let serialized_len = fields.len();\n let (deserialized, _) =\n generate_deserialize_from_fields(quote { self }, typ, quote { serialized }, 0, false);\n quote {\n impl Deserialize<$serialized_len> for $typ {\n #[inline_always]\n fn deserialize(serialized: [Field; $serialized_len]) -> Self {\n $deserialized\n }\n }\n }\n}\n\n/// Generates `Packable` implementation for a given struct and returns the packed length.\n///\n/// Note: We are having this function separate from `derive_packable` because we use this in the note macros to get\n/// the packed length of a note as well as the `Packable` implementation. We need the length to be able to register\n/// the note in the global `NOTES` map. There the length is used to generate partial note helper functions.\npub comptime fn derive_packable_and_get_packed_len(s: TypeDefinition) -> (Quoted, u32) {\n let packing_enabled = true;\n\n let typ = s.as_type();\n let (fields, aux_vars) = generate_serialize_to_fields(quote { self }, typ, packing_enabled);\n let aux_vars_for_packing = if aux_vars.len() > 0 {\n let joint = aux_vars.join(quote {;});\n quote { $joint; }\n } else {\n quote {}\n };\n\n let (unpacked, _) =\n generate_deserialize_from_fields(quote { self }, typ, quote { packed }, 0, packing_enabled);\n\n let field_packings = fields.join(quote {,});\n let packed_len = fields.len();\n let packable_trait: TraitConstraint = quote { Packable<$packed_len> }.as_trait_constraint();\n (\n quote {\n impl $packable_trait for $typ {\n fn pack(self) -> [Field; $packed_len] {\n $aux_vars_for_packing\n [ $field_packings ]\n }\n\n fn unpack(packed: [Field; $packed_len]) -> Self {\n $unpacked\n }\n }\n },\n packed_len,\n )\n}\n\npub(crate) comptime fn derive_packable(s: TypeDefinition) -> Quoted {\n let (packable_impl, _) = derive_packable_and_get_packed_len(s);\n packable_impl\n}\n\n#[derive(Packable, Serialize, Deserialize, Eq)]\npub struct Smol {\n a: Field,\n b: Field,\n}\n\n#[derive(Serialize, Deserialize, Eq)]\npub struct HasArray {\n a: [Field; 2],\n b: bool,\n}\n\n#[derive(Serialize, Deserialize, Eq)]\npub struct Fancier {\n a: Smol,\n b: [Field; 2],\n c: [u8; 3],\n d: str<16>,\n}\n\nfn main() {\n assert(false);\n}\n\n#[test]\nfn smol_test() {\n let smol = Smol { a: 1, b: 2 };\n let serialized = smol.serialize();\n assert(serialized == [1, 2], serialized);\n let deserialized = Smol::deserialize(serialized);\n assert(deserialized == smol);\n\n // None of the struct members implements the `Packable` trait so the packed and serialized data should be the same\n let packed = smol.pack();\n assert_eq(packed, serialized, \"Packed does not match serialized\");\n}\n\n#[test]\nfn has_array_test() {\n let has_array = HasArray { a: [1, 2], b: true };\n let serialized = has_array.serialize();\n assert(serialized == [1, 2, 1], serialized);\n let deserialized = HasArray::deserialize(serialized);\n assert(deserialized == has_array);\n}\n\n#[test]\nfn fancier_test() {\n let fancier =\n Fancier { a: Smol { a: 1, b: 2 }, b: [0, 1], c: [1, 2, 3], d: \"metaprogramming!\" };\n let serialized = fancier.serialize();\n assert(\n serialized\n == [\n 1, 2, 0, 1, 1, 2, 3, 0x6d, 0x65, 0x74, 0x61, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61,\n 0x6d, 0x6d, 0x69, 0x6e, 0x67, 0x21,\n ],\n serialized,\n );\n let deserialized = Fancier::deserialize(serialized);\n assert(deserialized == fancier);\n}\n" - }, - "296": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/noir-protocol-circuits/crates/types/src/point.nr", - "source": "pub use dep::std::embedded_curve_ops::EmbeddedCurvePoint as Point;\nuse crate::{hash::poseidon2_hash, traits::{Deserialize, Empty, Hash, Packable, Serialize}};\n\npub global POINT_LENGTH: u32 = 3;\n\nimpl Serialize for Point {\n fn serialize(self: Self) -> [Field; POINT_LENGTH] {\n [self.x, self.y, self.is_infinite as Field]\n }\n}\n\nimpl Hash for Point {\n fn hash(self) -> Field {\n poseidon2_hash(self.serialize())\n }\n}\n\nimpl Empty for Point {\n /// Note: Does not return a valid point on curve - instead represents an empty/\"unpopulated\" point struct (e.g.\n /// empty/unpopulated value in an array of points).\n fn empty() -> Self {\n Point { x: 0, y: 0, is_infinite: false }\n }\n}\n\nimpl Deserialize for Point {\n fn deserialize(serialized: [Field; POINT_LENGTH]) -> Point {\n Point { x: serialized[0], y: serialized[1], is_infinite: serialized[2] as bool }\n }\n}\n// TODO(#11356): use compact representation here.\nimpl Packable for Point {\n fn pack(self) -> [Field; POINT_LENGTH] {\n self.serialize()\n }\n\n fn unpack(packed: [Field; POINT_LENGTH]) -> Self {\n Self::deserialize(packed)\n }\n}\n" - }, - "297": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/noir-protocol-circuits/crates/types/src/poseidon2.nr", - "source": "use crate::constants::TWO_POW_64;\n\n// NB: This is a clone of noir/noir-repo/noir_stdlib/src/hash/poseidon2.nr\n// It exists as we sometimes need to perform custom absorption, but the stdlib version\n// has a private absorb() method (it's also designed to just be a hasher)\n// Can be removed when standalone noir poseidon lib exists: See noir#6679\n\ncomptime global RATE: u32 = 3;\n\npub struct Poseidon2Sponge {\n pub cache: [Field; 3],\n pub state: [Field; 4],\n pub cache_size: u32,\n pub squeeze_mode: bool, // 0 => absorb, 1 => squeeze\n}\n\nimpl Poseidon2Sponge {\n #[no_predicates]\n pub fn hash(input: [Field; N], message_size: u32) -> Field {\n Poseidon2Sponge::hash_internal(input, message_size, message_size != N)\n }\n\n pub(crate) fn new(iv: Field) -> Poseidon2Sponge {\n let mut result =\n Poseidon2Sponge { cache: [0; 3], state: [0; 4], cache_size: 0, squeeze_mode: false };\n result.state[RATE] = iv;\n result\n }\n\n fn perform_duplex(&mut self) {\n // add the cache into sponge state\n for i in 0..RATE {\n // We effectively zero-pad the cache by only adding to the state\n // cache that is less than the specified `cache_size`\n if i < self.cache_size {\n self.state[i] += self.cache[i];\n }\n }\n self.state = std::hash::poseidon2_permutation(self.state, 4);\n }\n\n pub fn absorb(&mut self, input: Field) {\n assert(!self.squeeze_mode);\n if self.cache_size == RATE {\n // If we're absorbing, and the cache is full, apply the sponge permutation to compress the cache\n self.perform_duplex();\n self.cache[0] = input;\n self.cache_size = 1;\n } else {\n // If we're absorbing, and the cache is not full, add the input into the cache\n self.cache[self.cache_size] = input;\n self.cache_size += 1;\n }\n }\n\n pub fn squeeze(&mut self) -> Field {\n assert(!self.squeeze_mode);\n // If we're in absorb mode, apply sponge permutation to compress the cache.\n self.perform_duplex();\n self.squeeze_mode = true;\n\n // Pop one item off the top of the permutation and return it.\n self.state[0]\n }\n\n fn hash_internal(\n input: [Field; N],\n in_len: u32,\n is_variable_length: bool,\n ) -> Field {\n let iv: Field = (in_len as Field) * TWO_POW_64;\n let mut sponge = Poseidon2Sponge::new(iv);\n for i in 0..input.len() {\n if i < in_len {\n sponge.absorb(input[i]);\n }\n }\n\n // In the case where the hash preimage is variable-length, we append `1` to the end of the input, to distinguish\n // from fixed-length hashes. (the combination of this additional field element + the hash IV ensures\n // fixed-length and variable-length hashes do not collide)\n if is_variable_length {\n sponge.absorb(1);\n }\n sponge.squeeze()\n }\n}\n" - }, - "307": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/noir-protocol-circuits/crates/types/src/public_keys.nr", - "source": "use crate::{\n address::public_keys_hash::PublicKeysHash,\n constants::{\n DEFAULT_IVPK_M_X, DEFAULT_IVPK_M_Y, DEFAULT_NPK_M_X, DEFAULT_NPK_M_Y, DEFAULT_OVPK_M_X,\n DEFAULT_OVPK_M_Y, DEFAULT_TPK_M_X, DEFAULT_TPK_M_Y, GENERATOR_INDEX__PUBLIC_KEYS_HASH,\n },\n hash::poseidon2_hash_with_separator,\n point::POINT_LENGTH,\n traits::{Deserialize, Hash, Serialize},\n};\n\nuse dep::std::embedded_curve_ops::EmbeddedCurvePoint as Point;\nuse std::default::Default;\n\npub global PUBLIC_KEYS_LENGTH: u32 = 12;\n\npub struct PublicKeys {\n pub npk_m: NpkM,\n pub ivpk_m: IvpkM,\n pub ovpk_m: OvpkM,\n pub tpk_m: TpkM,\n}\n\npub trait ToPoint {\n fn to_point(self) -> Point;\n}\n\npub struct NpkM {\n pub inner: Point,\n}\n\nimpl ToPoint for NpkM {\n fn to_point(self) -> Point {\n self.inner\n }\n}\n\nimpl Serialize for NpkM {\n fn serialize(self) -> [Field; POINT_LENGTH] {\n self.inner.serialize()\n }\n}\n\n// Note: If we store npk_m_hash directly we can remove this trait implementation. See #8091\nimpl Hash for NpkM {\n fn hash(self) -> Field {\n self.inner.hash()\n }\n}\n\npub struct IvpkM {\n pub inner: Point,\n}\n\nimpl ToPoint for IvpkM {\n fn to_point(self) -> Point {\n self.inner\n }\n}\n\nimpl Serialize for IvpkM {\n fn serialize(self) -> [Field; POINT_LENGTH] {\n self.inner.serialize()\n }\n}\n\npub struct OvpkM {\n pub inner: Point,\n}\n\nimpl Hash for OvpkM {\n fn hash(self) -> Field {\n self.inner.hash()\n }\n}\n\nimpl ToPoint for OvpkM {\n fn to_point(self) -> Point {\n self.inner\n }\n}\n\nimpl Serialize for OvpkM {\n fn serialize(self) -> [Field; POINT_LENGTH] {\n self.inner.serialize()\n }\n}\n\npub struct TpkM {\n pub inner: Point,\n}\n\nimpl ToPoint for TpkM {\n fn to_point(self) -> Point {\n self.inner\n }\n}\n\nimpl Serialize for TpkM {\n fn serialize(self) -> [Field; POINT_LENGTH] {\n self.inner.serialize()\n }\n}\n\nimpl Default for PublicKeys {\n fn default() -> Self {\n PublicKeys {\n npk_m: NpkM {\n inner: Point { x: DEFAULT_NPK_M_X, y: DEFAULT_NPK_M_Y, is_infinite: false },\n },\n ivpk_m: IvpkM {\n inner: Point { x: DEFAULT_IVPK_M_X, y: DEFAULT_IVPK_M_Y, is_infinite: false },\n },\n ovpk_m: OvpkM {\n inner: Point { x: DEFAULT_OVPK_M_X, y: DEFAULT_OVPK_M_Y, is_infinite: false },\n },\n tpk_m: TpkM {\n inner: Point { x: DEFAULT_TPK_M_X, y: DEFAULT_TPK_M_Y, is_infinite: false },\n },\n }\n }\n}\n\nimpl Eq for PublicKeys {\n fn eq(self, other: PublicKeys) -> bool {\n (self.npk_m.inner == other.npk_m.inner)\n & (self.ivpk_m.inner == other.ivpk_m.inner)\n & (self.ovpk_m.inner == other.ovpk_m.inner)\n & (self.tpk_m.inner == other.tpk_m.inner)\n }\n}\n\nimpl PublicKeys {\n pub fn hash(self) -> PublicKeysHash {\n PublicKeysHash::from_field(poseidon2_hash_with_separator(\n self.serialize(),\n GENERATOR_INDEX__PUBLIC_KEYS_HASH as Field,\n ))\n }\n}\n\nimpl Serialize for PublicKeys {\n fn serialize(self) -> [Field; PUBLIC_KEYS_LENGTH] {\n [\n self.npk_m.inner.x,\n self.npk_m.inner.y,\n self.npk_m.inner.is_infinite as Field,\n self.ivpk_m.inner.x,\n self.ivpk_m.inner.y,\n self.ivpk_m.inner.is_infinite as Field,\n self.ovpk_m.inner.x,\n self.ovpk_m.inner.y,\n self.ovpk_m.inner.is_infinite as Field,\n self.tpk_m.inner.x,\n self.tpk_m.inner.y,\n self.tpk_m.inner.is_infinite as Field,\n ]\n }\n}\n\nimpl Deserialize for PublicKeys {\n fn deserialize(serialized: [Field; PUBLIC_KEYS_LENGTH]) -> PublicKeys {\n PublicKeys {\n npk_m: NpkM {\n inner: Point {\n x: serialized[0],\n y: serialized[1],\n is_infinite: serialized[2] as bool,\n },\n },\n ivpk_m: IvpkM {\n inner: Point {\n x: serialized[3],\n y: serialized[4],\n is_infinite: serialized[5] as bool,\n },\n },\n ovpk_m: OvpkM {\n inner: Point {\n x: serialized[6],\n y: serialized[7],\n is_infinite: serialized[8] as bool,\n },\n },\n tpk_m: TpkM {\n inner: Point {\n x: serialized[9],\n y: serialized[10],\n is_infinite: serialized[11] as bool,\n },\n },\n }\n }\n}\n\npub struct AddressPoint {\n pub inner: Point,\n}\n\nimpl ToPoint for AddressPoint {\n fn to_point(self) -> Point {\n self.inner\n }\n}\n\n#[test]\nunconstrained fn compute_public_keys_hash() {\n let keys = PublicKeys {\n npk_m: NpkM { inner: Point { x: 1, y: 2, is_infinite: false } },\n ivpk_m: IvpkM { inner: Point { x: 3, y: 4, is_infinite: false } },\n ovpk_m: OvpkM { inner: Point { x: 5, y: 6, is_infinite: false } },\n tpk_m: TpkM { inner: Point { x: 7, y: 8, is_infinite: false } },\n };\n\n let actual = keys.hash();\n let expected_public_keys_hash =\n 0x0fecd9a32db731fec1fded1b9ff957a1625c069245a3613a2538bd527068b0ad;\n\n assert(actual.to_field() == expected_public_keys_hash);\n}\n\n#[test]\nunconstrained fn compute_default_hash() {\n let keys = PublicKeys::default();\n\n let actual = keys.hash();\n let test_data_default_hash = 0x1d3bf1fb93ae0e9cda83b203dd91c3bfb492a9aecf30ec90e1057eced0f0e62d;\n\n assert(actual.to_field() == test_data_default_hash);\n}\n\n#[test]\nunconstrained fn test_public_keys_serialization() {\n let keys = PublicKeys {\n npk_m: NpkM { inner: Point { x: 1, y: 2, is_infinite: false } },\n ivpk_m: IvpkM { inner: Point { x: 3, y: 4, is_infinite: false } },\n ovpk_m: OvpkM { inner: Point { x: 5, y: 6, is_infinite: false } },\n tpk_m: TpkM { inner: Point { x: 7, y: 8, is_infinite: false } },\n };\n\n let serialized = keys.serialize();\n let deserialized = PublicKeys::deserialize(serialized);\n\n assert_eq(keys.npk_m.inner.x, deserialized.npk_m.inner.x);\n assert_eq(keys.npk_m.inner.y, deserialized.npk_m.inner.y);\n assert_eq(keys.ivpk_m.inner.x, deserialized.ivpk_m.inner.x);\n assert_eq(keys.ivpk_m.inner.y, deserialized.ivpk_m.inner.y);\n assert_eq(keys.ovpk_m.inner.x, deserialized.ovpk_m.inner.x);\n assert_eq(keys.ovpk_m.inner.y, deserialized.ovpk_m.inner.y);\n assert_eq(keys.tpk_m.inner.x, deserialized.tpk_m.inner.x);\n assert_eq(keys.tpk_m.inner.y, deserialized.tpk_m.inner.y);\n}\n" - }, - "318": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/noir-protocol-circuits/crates/types/src/storage/map.nr", - "source": "use crate::{hash::poseidon2_hash, traits::ToField};\n\npub fn derive_storage_slot_in_map(storage_slot: Field, key: K) -> Field\nwhere\n K: ToField,\n{\n poseidon2_hash([storage_slot, key.to_field()])\n}\n\nmod test {\n use crate::{address::AztecAddress, storage::map::derive_storage_slot_in_map, traits::FromField};\n\n #[test]\n fn test_derive_storage_slot_in_map_matches_typescript() {\n let map_slot = 0x132258fb6962c4387ba659d9556521102d227549a386d39f0b22d1890d59c2b5;\n let key = AztecAddress::from_field(\n 0x302dbc2f9b50a73283d5fb2f35bc01eae8935615817a0b4219a057b2ba8a5a3f,\n );\n\n let slot = derive_storage_slot_in_map(map_slot, key);\n\n // The following value was generated by `map_slot.test.ts`\n let slot_from_typescript =\n 0x15b9fe39449affd8b377461263e9d2b610b9ad40580553500b4e41d9cbd887ac;\n\n assert_eq(slot, slot_from_typescript);\n }\n}\n" - }, - "336": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/noir-protocol-circuits/crates/types/src/type_packing.nr", - "source": "use crate::traits::Packable;\n\nglobal BOOL_PACKED_LEN: u32 = 1;\nglobal U8_PACKED_LEN: u32 = 1;\nglobal U16_PACKED_LEN: u32 = 1;\nglobal U32_PACKED_LEN: u32 = 1;\nglobal U64_PACKED_LEN: u32 = 1;\nglobal U128_PACKED_LEN: u32 = 1;\nglobal FIELD_PACKED_LEN: u32 = 1;\nglobal I8_PACKED_LEN: u32 = 1;\nglobal I16_PACKED_LEN: u32 = 1;\nglobal I32_PACKED_LEN: u32 = 1;\nglobal I64_PACKED_LEN: u32 = 1;\n\nimpl Packable for bool {\n fn pack(self) -> [Field; BOOL_PACKED_LEN] {\n [self as Field]\n }\n\n fn unpack(fields: [Field; BOOL_PACKED_LEN]) -> bool {\n fields[0] as bool\n }\n}\n\nimpl Packable for u8 {\n fn pack(self) -> [Field; U8_PACKED_LEN] {\n [self as Field]\n }\n\n fn unpack(fields: [Field; U8_PACKED_LEN]) -> Self {\n fields[0] as u8\n }\n}\n\nimpl Packable for u16 {\n fn pack(self) -> [Field; U16_PACKED_LEN] {\n [self as Field]\n }\n\n fn unpack(fields: [Field; U16_PACKED_LEN]) -> Self {\n fields[0] as u16\n }\n}\n\nimpl Packable for u32 {\n fn pack(self) -> [Field; U32_PACKED_LEN] {\n [self as Field]\n }\n\n fn unpack(fields: [Field; U32_PACKED_LEN]) -> Self {\n fields[0] as u32\n }\n}\n\nimpl Packable for u64 {\n fn pack(self) -> [Field; U64_PACKED_LEN] {\n [self as Field]\n }\n\n fn unpack(fields: [Field; U64_PACKED_LEN]) -> Self {\n fields[0] as u64\n }\n}\n\nimpl Packable for u128 {\n fn pack(self) -> [Field; U128_PACKED_LEN] {\n [self as Field]\n }\n\n fn unpack(fields: [Field; U128_PACKED_LEN]) -> Self {\n fields[0] as u128\n }\n}\n\nimpl Packable for Field {\n fn pack(self) -> [Field; FIELD_PACKED_LEN] {\n [self]\n }\n\n fn unpack(fields: [Field; FIELD_PACKED_LEN]) -> Self {\n fields[0]\n }\n}\n\nimpl Packable for i8 {\n fn pack(self) -> [Field; I8_PACKED_LEN] {\n [self as Field]\n }\n\n fn unpack(fields: [Field; I8_PACKED_LEN]) -> Self {\n fields[0] as i8\n }\n}\n\nimpl Packable for i16 {\n fn pack(self) -> [Field; I16_PACKED_LEN] {\n [self as Field]\n }\n\n fn unpack(fields: [Field; I16_PACKED_LEN]) -> Self {\n fields[0] as i16\n }\n}\n\nimpl Packable for i32 {\n fn pack(self) -> [Field; I32_PACKED_LEN] {\n [self as Field]\n }\n\n fn unpack(fields: [Field; I32_PACKED_LEN]) -> Self {\n fields[0] as i32\n }\n}\n\nimpl Packable for i64 {\n fn pack(self) -> [Field; I64_PACKED_LEN] {\n [self as Field]\n }\n\n fn unpack(fields: [Field; I64_PACKED_LEN]) -> Self {\n fields[0] as i64\n }\n}\n\nimpl Packable for [T; N]\nwhere\n T: Packable,\n{\n fn pack(self) -> [Field; N * M] {\n let mut result: [Field; N * M] = std::mem::zeroed();\n let mut serialized: [Field; M] = std::mem::zeroed();\n for i in 0..N {\n serialized = self[i].pack();\n for j in 0..M {\n result[i * M + j] = serialized[j];\n }\n }\n result\n }\n\n fn unpack(fields: [Field; N * M]) -> Self {\n let mut reader = crate::utils::reader::Reader::new(fields);\n let mut result: [T; N] = std::mem::zeroed();\n reader.read_struct_array::(Packable::unpack, result)\n }\n}\n\n#[test]\nfn test_u16_packing() {\n let a: u16 = 10;\n assert_eq(a, u16::unpack(a.pack()));\n}\n\n#[test]\nfn test_i8_packing() {\n let a: i8 = -10;\n assert_eq(a, i8::unpack(a.pack()));\n}\n\n#[test]\nfn test_i16_packing() {\n let a: i16 = -10;\n assert_eq(a, i16::unpack(a.pack()));\n}\n\n#[test]\nfn test_i32_packing() {\n let a: i32 = -10;\n assert_eq(a, i32::unpack(a.pack()));\n}\n\n#[test]\nfn test_i64_packing() {\n let a: i64 = -10;\n assert_eq(a, i64::unpack(a.pack()));\n}\n" - }, - "337": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/noir-protocol-circuits/crates/types/src/type_serialization.nr", - "source": "use crate::traits::{Deserialize, Serialize};\n\nglobal BOOL_SERIALIZED_LEN: u32 = 1;\nglobal U8_SERIALIZED_LEN: u32 = 1;\nglobal U16_SERIALIZED_LEN: u32 = 1;\nglobal U32_SERIALIZED_LEN: u32 = 1;\nglobal U64_SERIALIZED_LEN: u32 = 1;\nglobal U128_SERIALIZED_LEN: u32 = 1;\nglobal FIELD_SERIALIZED_LEN: u32 = 1;\nglobal I8_SERIALIZED_LEN: u32 = 1;\nglobal I16_SERIALIZED_LEN: u32 = 1;\nglobal I32_SERIALIZED_LEN: u32 = 1;\nglobal I64_SERIALIZED_LEN: u32 = 1;\n\nimpl Serialize for bool {\n fn serialize(self) -> [Field; BOOL_SERIALIZED_LEN] {\n [self as Field]\n }\n}\n\nimpl Deserialize for bool {\n fn deserialize(fields: [Field; BOOL_SERIALIZED_LEN]) -> bool {\n fields[0] as bool\n }\n}\n\nimpl Serialize for u8 {\n fn serialize(self) -> [Field; U8_SERIALIZED_LEN] {\n [self as Field]\n }\n}\n\nimpl Deserialize for u8 {\n fn deserialize(fields: [Field; U8_SERIALIZED_LEN]) -> Self {\n fields[0] as u8\n }\n}\n\nimpl Serialize for u16 {\n fn serialize(self) -> [Field; U16_SERIALIZED_LEN] {\n [self as Field]\n }\n}\n\nimpl Deserialize for u16 {\n fn deserialize(fields: [Field; U16_SERIALIZED_LEN]) -> Self {\n fields[0] as u16\n }\n}\n\nimpl Serialize for u32 {\n fn serialize(self) -> [Field; U32_SERIALIZED_LEN] {\n [self as Field]\n }\n}\n\nimpl Deserialize for u32 {\n fn deserialize(fields: [Field; U32_SERIALIZED_LEN]) -> Self {\n fields[0] as u32\n }\n}\n\nimpl Serialize for u64 {\n fn serialize(self) -> [Field; U64_SERIALIZED_LEN] {\n [self as Field]\n }\n}\n\nimpl Deserialize for u64 {\n fn deserialize(fields: [Field; U64_SERIALIZED_LEN]) -> Self {\n fields[0] as u64\n }\n}\n\nimpl Serialize for u128 {\n fn serialize(self) -> [Field; U128_SERIALIZED_LEN] {\n [self as Field]\n }\n}\n\nimpl Deserialize for u128 {\n fn deserialize(fields: [Field; U128_SERIALIZED_LEN]) -> Self {\n fields[0] as u128\n }\n}\n\nimpl Serialize for Field {\n fn serialize(self) -> [Field; FIELD_SERIALIZED_LEN] {\n [self]\n }\n}\n\nimpl Deserialize for Field {\n fn deserialize(fields: [Field; FIELD_SERIALIZED_LEN]) -> Self {\n fields[0]\n }\n}\n\nimpl Serialize for i8 {\n fn serialize(self) -> [Field; I8_SERIALIZED_LEN] {\n [self as Field]\n }\n}\n\nimpl Deserialize for i8 {\n fn deserialize(fields: [Field; I8_SERIALIZED_LEN]) -> Self {\n fields[0] as i8\n }\n}\n\nimpl Serialize for i16 {\n fn serialize(self) -> [Field; I16_SERIALIZED_LEN] {\n [self as Field]\n }\n}\n\nimpl Deserialize for i16 {\n fn deserialize(fields: [Field; I16_SERIALIZED_LEN]) -> Self {\n fields[0] as i16\n }\n}\n\nimpl Serialize for i32 {\n fn serialize(self) -> [Field; I32_SERIALIZED_LEN] {\n [self as Field]\n }\n}\n\nimpl Deserialize for i32 {\n fn deserialize(fields: [Field; I32_SERIALIZED_LEN]) -> Self {\n fields[0] as i32\n }\n}\n\nimpl Serialize for i64 {\n fn serialize(self) -> [Field; I64_SERIALIZED_LEN] {\n [self as Field]\n }\n}\n\nimpl Deserialize for i64 {\n fn deserialize(fields: [Field; I64_SERIALIZED_LEN]) -> Self {\n fields[0] as i64\n }\n}\n\nimpl Serialize for [T; N]\nwhere\n T: Serialize,\n{\n fn serialize(self) -> [Field; N * M] {\n let mut result: [Field; N * M] = std::mem::zeroed();\n let mut serialized: [Field; M] = std::mem::zeroed();\n for i in 0..N {\n serialized = self[i].serialize();\n for j in 0..M {\n result[i * M + j] = serialized[j];\n }\n }\n result\n }\n}\n\nimpl Deserialize for [T; N]\nwhere\n T: Deserialize,\n{\n fn deserialize(fields: [Field; N * M]) -> Self {\n let mut reader = crate::utils::reader::Reader::new(fields);\n let mut result: [T; N] = std::mem::zeroed();\n reader.read_struct_array::(Deserialize::deserialize, result)\n }\n}\n\n#[test]\nfn test_u16_serialization() {\n let a: u16 = 10;\n assert_eq(a, u16::deserialize(a.serialize()));\n}\n\n#[test]\nfn test_i8_serialization() {\n let a: i8 = -10;\n assert_eq(a, i8::deserialize(a.serialize()));\n}\n\n#[test]\nfn test_i16_serialization() {\n let a: i16 = -10;\n assert_eq(a, i16::deserialize(a.serialize()));\n}\n\n#[test]\nfn test_i32_serialization() {\n let a: i32 = -10;\n assert_eq(a, i32::deserialize(a.serialize()));\n}\n\n#[test]\nfn test_i64_serialization() {\n let a: i64 = -10;\n assert_eq(a, i64::deserialize(a.serialize()));\n}\n" - }, - "353": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr", - "source": "pub mod assert_array_appended;\npub mod assert_array_prepended;\npub mod assert_combined_array;\npub mod assert_combined_transformed_array;\npub mod assert_exposed_sorted_transformed_value_array;\npub mod assert_sorted_array;\npub mod assert_sorted_transformed_value_array;\npub mod assert_split_sorted_transformed_value_arrays;\npub mod assert_split_transformed_value_arrays;\npub mod get_sorted_result;\npub mod get_sorted_tuple;\npub mod sort_by;\npub mod sort_by_counter;\n\n// Re-exports.\npub use assert_array_appended::{\n assert_array_appended, assert_array_appended_and_scoped, assert_array_appended_reversed,\n assert_array_appended_scoped,\n};\npub use assert_array_prepended::assert_array_prepended;\npub use assert_combined_array::{assert_combined_array, combine_arrays};\npub use assert_combined_transformed_array::{\n assert_combined_transformed_array, combine_and_transform_arrays,\n};\npub use assert_exposed_sorted_transformed_value_array::{\n assert_exposed_sorted_transformed_value_array,\n get_order_hints::{get_order_hints_asc, OrderHint},\n};\npub use assert_sorted_array::assert_sorted_array;\npub use assert_sorted_transformed_value_array::{\n assert_sorted_transformed_value_array, assert_sorted_transformed_value_array_capped_size,\n};\npub use assert_split_sorted_transformed_value_arrays::{\n assert_split_sorted_transformed_value_arrays_asc,\n get_split_order_hints::{get_split_order_hints_asc, SplitOrderHints},\n};\npub use assert_split_transformed_value_arrays::assert_split_transformed_value_arrays;\npub use get_sorted_result::{get_sorted_result, SortedResult};\npub use sort_by_counter::sort_by_counter_asc;\n\nuse crate::traits::{Empty, is_empty};\n\npub fn subarray(\n src: [Field; SRC_LEN],\n offset: u32,\n) -> [Field; DST_LEN] {\n assert(offset + DST_LEN <= SRC_LEN, \"offset too large\");\n\n let mut dst: [Field; DST_LEN] = std::mem::zeroed();\n for i in 0..DST_LEN {\n dst[i] = src[i + offset];\n }\n\n dst\n}\n\n// Helper function to convert a validated array to BoundedVec.\n// Important: Only use it for validated arrays: validate_array(array) should be true.\npub unconstrained fn array_to_bounded_vec(array: [T; N]) -> BoundedVec\nwhere\n T: Empty + Eq,\n{\n let len = array_length(array);\n BoundedVec::from_parts_unchecked(array, len)\n}\n\n// Helper function to find the index of the first element in an array that satisfies a given predicate. If the element\n// is not found, the function returns N as the index.\npub unconstrained fn find_index_hint(\n array: [T; N],\n find: fn[Env](T) -> bool,\n) -> u32 {\n let mut index = N;\n for i in 0..N {\n // We check `index == N` to ensure that we only update the index if we haven't found a match yet.\n if (index == N) & find(array[i]) {\n index = i;\n }\n }\n index\n}\n\n// Routine which validates that all zero values of an array form a contiguous region at the end, i.e.,\n// of the form: [*,*,*...,0,0,0,0] where any * is non-zero. Note that a full array of non-zero values is\n// valid.\npub fn validate_array(array: [T; N]) -> u32\nwhere\n T: Empty + Eq,\n{\n let mut seen_empty = false;\n let mut length = 0;\n for i in 0..N {\n if is_empty(array[i]) {\n seen_empty = true;\n } else {\n assert(seen_empty == false, \"invalid array\");\n length += 1;\n }\n }\n length\n}\n\n// Helper function to count the number of non-empty elements in a validated array.\n// Important: Only use it for validated arrays where validate_array(array) returns true,\n// which ensures that:\n// 1. All elements before the first empty element are non-empty\n// 2. All elements after and including the first empty element are empty\n// 3. The array forms a contiguous sequence of non-empty elements followed by empty elements\npub fn array_length(array: [T; N]) -> u32\nwhere\n T: Empty + Eq,\n{\n // We get the length by checking the index of the first empty element.\n\n // Safety: This is safe because we have validated the array (see function doc above) and the emptiness\n // of the element and non-emptiness of the previous element is checked below.\n let length = unsafe { find_index_hint(array, |elem: T| is_empty(elem)) };\n if length != 0 {\n assert(!is_empty(array[length - 1]));\n }\n if length != N {\n assert(is_empty(array[length]));\n }\n length\n}\n\n// Returns the number of consecutive elements at the start of the array for which the predicate returns false.\n// This function ensures that any element after the first matching element (predicate returns true) also matches the predicate.\npub fn array_length_until(array: [T; N], predicate: fn[Env](T) -> bool) -> u32 {\n let mut length = 0;\n let mut stop = false;\n for i in 0..N {\n if predicate(array[i]) {\n stop = true;\n } else {\n assert(\n stop == false,\n \"matching element found after already encountering a non-matching element\",\n );\n length += 1;\n }\n }\n length\n}\n\npub fn array_concat(array1: [T; N], array2: [T; M]) -> [T; N + M] {\n let mut result = [array1[0]; N + M];\n for i in 1..N {\n result[i] = array1[i];\n }\n for i in 0..M {\n result[i + N] = array2[i];\n }\n result\n}\n\n/// This function assumes that `array1` and `array2` contain no more than N non-empty elements between them,\n/// if this is not the case then elements from the end of `array2` will be dropped.\npub fn array_merge(array1: [T; N], array2: [T; N]) -> [T; N]\nwhere\n T: Empty + Eq,\n{\n // Safety: we constrain this array below\n let result = unsafe { array_merge_helper(array1, array2) };\n // We assume arrays have been validated. The only use cases so far are with previously validated arrays.\n let array1_len = array_length(array1);\n let mut add_from_left = true;\n for i in 0..N {\n add_from_left &= i != array1_len;\n if add_from_left {\n assert_eq(result[i], array1[i]);\n } else {\n assert_eq(result[i], array2[i - array1_len]);\n }\n }\n result\n}\n\nunconstrained fn array_merge_helper(array1: [T; N], array2: [T; N]) -> [T; N]\nwhere\n T: Empty + Eq,\n{\n let mut result: [T; N] = [T::empty(); N];\n let mut i = 0;\n for elem in array1 {\n if !is_empty(elem) {\n result[i] = elem;\n i += 1;\n }\n }\n for elem in array2 {\n if !is_empty(elem) {\n result[i] = elem;\n i += 1;\n }\n }\n result\n}\n\n// Helper fn to create a subarray from a given array\npub fn array_splice(array: [T; N], offset: u32) -> [T; M]\nwhere\n T: Empty,\n{\n assert(M + offset <= N, \"Subarray length larger than array length\");\n let mut result: [T; M] = [T::empty(); M];\n for i in 0..M {\n result[i] = array[offset + i];\n }\n result\n}\n\npub fn check_permutation(\n original_array: [T; N],\n permuted_array: [T; N],\n original_indexes: [u32; N],\n)\nwhere\n T: Eq + Empty,\n{\n let mut seen_value = [false; N];\n for i in 0..N {\n let index = original_indexes[i];\n let original_value = original_array[index];\n assert(permuted_array[i].eq(original_value), \"Invalid index\");\n assert(!seen_value[index], \"Duplicated index\");\n seen_value[index] = true;\n }\n}\n\n// Helper function to find the index of the last element in an array, allowing empty elements.\n// e.g. useful for removing trailing 0s from [1, 0, 2, 0, 0, 0] -> [1, 0, 2]\n// Nothing to do with validated arrays. Correctness constrained by padded_array_length.\npub unconstrained fn find_last_value_index(array: [T; N]) -> u32\nwhere\n T: Empty + Eq,\n{\n let mut index = N;\n for i in 0..N {\n let j = N - i - 1;\n // We check `index == N` to ensure that we only update the index if we haven't found a match yet.\n if (index == N) & !is_empty(array[j]) {\n index = j;\n }\n }\n index\n}\n\n// Routine which returns the length of an array right padded by empty elements\n// of the form: [*,*,*...,0,0,0,0] where * is any value (zeroes allowed).\n// See smoke_validate_array_trailing for examples.\n// Nothing to do with validated arrays. Correctness constrained by padded_array_length.\npub unconstrained fn unsafe_padded_array_length(array: [T; N]) -> u32\nwhere\n T: Empty + Eq,\n{\n let index = find_last_value_index(array);\n if index == N {\n 0\n } else {\n index + 1\n }\n}\n\n// Routine which validates that zero values of an array form a contiguous region at the end, i.e.,\n// of the form: [*,*,*...,0,0,0,0] where * is any value (zeroes allowed).\npub fn padded_array_length(array: [T; N]) -> u32\nwhere\n T: Empty + Eq,\n{\n // Safety: this value is constrained in the below loop.\n let length = unsafe { unsafe_padded_array_length(array) };\n // Check the elt just before length is non-zero:\n if length != 0 {\n assert(!is_empty(array[length - 1]), \"invalid right padded array\");\n }\n // Check all beyond length are zero:\n let mut check_zero = false;\n for i in 0..N {\n check_zero |= i == length;\n if check_zero {\n assert(is_empty(array[i]), \"invalid right padded array\");\n }\n }\n length\n}\n\n// Helper function to check if an array is padded with a given value from a given index.\n// Different to padded_array_length in that it allows the elements before the given index to be the same as the padded value.\npub fn array_padded_with(array: [T; N], from_index: u32, padded_with: T) -> bool\nwhere\n T: Eq,\n{\n let mut is_valid = true;\n let mut should_check = false;\n for i in 0..N {\n should_check |= i == from_index;\n is_valid &= !should_check | (array[i] == padded_with);\n }\n is_valid\n}\n\n#[test]\nfn smoke_validate_array() {\n let valid_array: [Field; 0] = [];\n assert(validate_array(valid_array) == 0);\n\n let valid_array = [0];\n assert(validate_array(valid_array) == 0);\n\n let valid_array = [3];\n assert(validate_array(valid_array) == 1);\n\n let valid_array = [1, 2, 3];\n assert(validate_array(valid_array) == 3);\n\n let valid_array = [1, 2, 3, 0];\n assert(validate_array(valid_array) == 3);\n\n let valid_array = [1, 2, 3, 0, 0];\n assert(validate_array(valid_array) == 3);\n}\n\n#[test]\nfn smoke_validate_array_trailing() {\n let valid_array: [Field; 0] = [];\n assert(padded_array_length(valid_array) == 0);\n\n let valid_array = [0];\n assert(padded_array_length(valid_array) == 0);\n\n let valid_array = [3];\n assert(padded_array_length(valid_array) == 1);\n\n let valid_array = [1, 0, 3];\n assert(padded_array_length(valid_array) == 3);\n\n let valid_array = [1, 0, 3, 0];\n assert(padded_array_length(valid_array) == 3);\n\n let valid_array = [1, 2, 3, 0, 0];\n assert(padded_array_length(valid_array) == 3);\n\n let valid_array = [0, 0, 3, 0, 0];\n assert(padded_array_length(valid_array) == 3);\n}\n\n#[test(should_fail_with = \"invalid array\")]\nfn smoke_validate_array_invalid_case0() {\n let invalid_array = [0, 1];\n let _ = validate_array(invalid_array);\n}\n\n#[test(should_fail_with = \"invalid array\")]\nfn smoke_validate_array_invalid_case1() {\n let invalid_array = [1, 0, 0, 1, 0];\n let _ = validate_array(invalid_array);\n}\n\n#[test(should_fail_with = \"invalid array\")]\nfn smoke_validate_array_invalid_case2() {\n let invalid_array = [0, 0, 0, 0, 1];\n let _ = validate_array(invalid_array);\n}\n\n#[test]\nfn test_empty_array_length() {\n assert_eq(array_length([0]), 0);\n assert_eq(array_length([0, 0, 0]), 0);\n}\n\n#[test]\nfn test_array_length() {\n assert_eq(array_length([123]), 1);\n assert_eq(array_length([123, 0, 0]), 1);\n assert_eq(array_length([123, 456]), 2);\n assert_eq(array_length([123, 456, 0]), 2);\n}\n\n#[test]\nfn test_array_length_invalid_arrays() {\n // Result can be misleading (but correct) for invalid arrays.\n assert_eq(array_length([0, 0, 123]), 0);\n assert_eq(array_length([0, 123, 0]), 0);\n assert_eq(array_length([0, 123, 456]), 0);\n assert_eq(array_length([123, 0, 456]), 1);\n}\n\n#[test]\nfn test_array_length_until() {\n let array = [11, 22, 33, 44, 55];\n assert_eq(array_length_until(array, |x| x == 55), 4);\n assert_eq(array_length_until(array, |x| x == 56), 5);\n assert_eq(array_length_until(array, |x| x > 40), 3);\n assert_eq(array_length_until(array, |x| x > 10), 0);\n}\n\n#[test(should_fail_with = \"matching element found after already encountering a non-matching element\")]\nfn test_array_length_until_non_consecutive_fails() {\n let array = [1, 1, 0, 1, 0];\n let _ = array_length_until(array, |x| x == 0);\n}\n\n#[test(should_fail_with = \"matching element found after already encountering a non-matching element\")]\nfn test_array_length_until_first_non_matching_fails() {\n let array = [1, 0, 0, 0, 0];\n let _ = array_length_until(array, |x| x == 1);\n}\n\n#[test]\nunconstrained fn find_index_greater_than_min() {\n let values = [10, 20, 30, 40];\n let min = 22;\n let index = find_index_hint(values, |v: Field| min.lt(v));\n assert_eq(index, 2);\n}\n\n#[test]\nunconstrained fn find_index_not_found() {\n let values = [10, 20, 30, 40];\n let min = 100;\n let index = find_index_hint(values, |v: Field| min.lt(v));\n assert_eq(index, 4);\n}\n\n#[test]\nfn test_array_concat() {\n let array0 = [1, 2, 3];\n let array1 = [4, 5];\n let concatenated = array_concat(array0, array1);\n assert_eq(concatenated, [1, 2, 3, 4, 5]);\n}\n\n#[test]\nfn check_permutation_basic_test() {\n let original_array = [1, 2, 3];\n let permuted_array = [3, 1, 2];\n let indexes = [2, 0, 1];\n check_permutation(original_array, permuted_array, indexes);\n}\n\n#[test(should_fail_with = \"Duplicated index\")]\nfn check_permutation_duplicated_index() {\n let original_array = [0, 1, 0];\n let permuted_array = [1, 0, 0];\n let indexes = [1, 0, 0];\n check_permutation(original_array, permuted_array, indexes);\n}\n\n#[test(should_fail_with = \"Invalid index\")]\nfn check_permutation_invalid_index() {\n let original_array = [0, 1, 2];\n let permuted_array = [1, 0, 0];\n let indexes = [1, 0, 2];\n check_permutation(original_array, permuted_array, indexes);\n}\n\n#[test]\nfn test_array_padded_with() {\n let array = [11, 22, 33, 44, 44];\n assert_eq(array_padded_with(array, 0, 44), false);\n assert_eq(array_padded_with(array, 1, 44), false);\n assert_eq(array_padded_with(array, 2, 44), false);\n assert_eq(array_padded_with(array, 3, 44), true);\n assert_eq(array_padded_with(array, 4, 44), true);\n assert_eq(array_padded_with(array, 4, 33), false);\n assert_eq(array_padded_with(array, 5, 44), true); // Index out of bounds.\n assert_eq(array_padded_with(array, 0, 11), false);\n}\n" - }, - "356": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/noir-protocol-circuits/crates/types/src/utils/reader.nr", - "source": "pub struct Reader {\n data: [Field; N],\n offset: u32,\n}\n\nimpl Reader {\n pub fn new(data: [Field; N]) -> Self {\n Self { data, offset: 0 }\n }\n\n pub fn read(&mut self) -> Field {\n let result = self.data[self.offset];\n self.offset += 1;\n result\n }\n\n pub fn read_u32(&mut self) -> u32 {\n self.read() as u32\n }\n\n pub fn read_bool(&mut self) -> bool {\n self.read() as bool\n }\n\n pub fn read_array(&mut self) -> [Field; K] {\n let mut result = [0; K];\n for i in 0..K {\n result[i] = self.data[self.offset + i];\n }\n self.offset += K;\n result\n }\n\n pub fn read_struct(&mut self, deserialise: fn([Field; K]) -> T) -> T {\n let result = deserialise(self.read_array());\n result\n }\n\n pub fn read_struct_array(\n &mut self,\n deserialise: fn([Field; K]) -> T,\n mut result: [T; C],\n ) -> [T; C] {\n for i in 0..C {\n result[i] = self.read_struct(deserialise);\n }\n result\n }\n\n pub fn finish(self) {\n assert(self.offset == self.data.len(), \"Reader did not read all data\");\n }\n}\n" - }, - "366": { - "path": "/mnt/user-data/saleel/nargo/github.com/noir-lang/sha256/v0.1.2/src/sha256.nr", - "source": "use std::hash::sha256_compression;\nuse std::runtime::is_unconstrained;\n\nuse constants::{\n BLOCK_BYTE_PTR, BLOCK_SIZE, HASH, INITIAL_STATE, INT_BLOCK, INT_BLOCK_SIZE, INT_SIZE,\n INT_SIZE_PTR, MSG_BLOCK, MSG_SIZE_PTR, STATE, TWO_POW_16, TWO_POW_24, TWO_POW_32, TWO_POW_8,\n};\n\nmod constants;\nmod tests;\n\n// Implementation of SHA-256 mapping a byte array of variable length to\n// 32 bytes.\n\n// Deprecated in favour of `sha256_var`\n// docs:start:sha256\npub fn sha256(input: [u8; N]) -> HASH\n// docs:end:sha256\n{\n digest(input)\n}\n\n// SHA-256 hash function\n#[no_predicates]\npub fn digest(msg: [u8; N]) -> HASH {\n sha256_var(msg, N as u64)\n}\n\n// Variable size SHA-256 hash\npub fn sha256_var(msg: [u8; N], message_size: u64) -> HASH {\n let message_size = message_size as u32;\n assert(message_size <= N);\n\n if std::runtime::is_unconstrained() {\n // Safety: SHA256 is running as an unconstrained function.\n unsafe {\n __sha256_var(msg, message_size)\n }\n } else {\n let mut msg_block: MSG_BLOCK = [0; INT_BLOCK_SIZE];\n // Intermediate hash, starting with the canonical initial value\n let mut h: STATE = INITIAL_STATE;\n // Pointer into msg_block on a 64 byte scale\n let mut msg_byte_ptr = 0;\n let num_blocks = N / BLOCK_SIZE;\n for i in 0..num_blocks {\n let msg_start = BLOCK_SIZE * i;\n let (new_msg_block, new_msg_byte_ptr) =\n unsafe { build_msg_block(msg, message_size, msg_start) };\n\n if msg_start < message_size {\n msg_block = new_msg_block;\n }\n\n // Verify the block we are compressing was appropriately constructed\n let new_msg_byte_ptr = verify_msg_block(msg, message_size, msg_block, msg_start);\n if msg_start < message_size {\n msg_byte_ptr = new_msg_byte_ptr;\n }\n\n // If the block is filled, compress it.\n // An un-filled block is handled after this loop.\n if (msg_start < message_size) & (msg_byte_ptr == BLOCK_SIZE) {\n h = sha256_compression(msg_block, h);\n }\n }\n\n let modulo = N % BLOCK_SIZE;\n // Handle setup of the final msg block.\n // This case is only hit if the msg is less than the block size,\n // or our message cannot be evenly split into blocks.\n if modulo != 0 {\n let msg_start = BLOCK_SIZE * num_blocks;\n let (new_msg_block, new_msg_byte_ptr) =\n unsafe { build_msg_block(msg, message_size, msg_start) };\n\n if msg_start < message_size {\n msg_block = new_msg_block;\n }\n\n let new_msg_byte_ptr = verify_msg_block(msg, message_size, msg_block, msg_start);\n if msg_start < message_size {\n msg_byte_ptr = new_msg_byte_ptr;\n verify_msg_block_padding(msg_block, msg_byte_ptr);\n }\n }\n\n // If we had modulo == 0 then it means the last block was full,\n // and we can reset the pointer to zero to overwrite it.\n if msg_byte_ptr == BLOCK_SIZE {\n msg_byte_ptr = 0;\n }\n\n // Pad the rest such that we have a [u32; 2] block at the end representing the length\n // of the message, and a block of 1 0 ... 0 following the message (i.e. [1 << 7, 0, ..., 0]).\n // Here we rely on the fact that everything beyond the available input is set to 0.\n let index = msg_byte_ptr / INT_SIZE;\n msg_block[index] = set_item_byte_then_zeros(msg_block[index], msg_byte_ptr, 1 << 7);\n\n msg_byte_ptr = msg_byte_ptr + 1;\n let last_block = msg_block;\n\n // If we don't have room to write the size, compress the block and reset it.\n if msg_byte_ptr > MSG_SIZE_PTR {\n h = sha256_compression(msg_block, h);\n // `attach_len_to_msg_block` will zero out everything after the `msg_byte_ptr`.\n msg_byte_ptr = 0;\n }\n\n msg_block = unsafe { attach_len_to_msg_block(msg_block, msg_byte_ptr, message_size) };\n\n verify_msg_len(msg_block, last_block, msg_byte_ptr, message_size);\n\n hash_final_block(msg_block, h)\n }\n}\n\n// Variable size SHA-256 hash\nunconstrained fn __sha256_var(msg: [u8; N], message_size: u32) -> HASH {\n let num_full_blocks = message_size / BLOCK_SIZE;\n // Intermediate hash, starting with the canonical initial value\n let mut h: STATE = INITIAL_STATE;\n // Pointer into msg_block on a 64 byte scale\n for i in 0..num_full_blocks {\n let (msg_block, _) = build_msg_block(msg, message_size, BLOCK_SIZE * i);\n h = sha256_compression(msg_block, h);\n }\n\n // Handle setup of the final msg block.\n // This case is only hit if the msg is less than the block size,\n // or our message cannot be evenly split into blocks.\n let modulo = message_size % BLOCK_SIZE;\n let (mut msg_block, mut msg_byte_ptr): (INT_BLOCK, u32) = if modulo != 0 {\n let msg_start = BLOCK_SIZE * num_full_blocks;\n let (new_msg_block, new_msg_byte_ptr) = build_msg_block(msg, message_size, msg_start);\n\n (new_msg_block, new_msg_byte_ptr)\n } else {\n // If we had modulo == 0 then it means the last block was full,\n // and we can reset the pointer to zero to overwrite it.\n ([0; INT_BLOCK_SIZE], 0)\n };\n\n // Pad the rest such that we have a [u32; 2] block at the end representing the length\n // of the message, and a block of 1 0 ... 0 following the message (i.e. [1 << 7, 0, ..., 0]).\n // Here we rely on the fact that everything beyond the available input is set to 0.\n let index = msg_byte_ptr / INT_SIZE;\n msg_block[index] = set_item_byte_then_zeros(msg_block[index], msg_byte_ptr, 1 << 7);\n\n // If we don't have room to write the size, compress the block and reset it.\n let (h, mut msg_byte_ptr): (STATE, u32) = if msg_byte_ptr >= MSG_SIZE_PTR {\n // `attach_len_to_msg_block` will zero out everything after the `msg_byte_ptr`.\n (sha256_compression(msg_block, h), 0)\n } else {\n (h, msg_byte_ptr + 1)\n };\n msg_block = attach_len_to_msg_block(msg_block, msg_byte_ptr, message_size);\n\n hash_final_block(msg_block, h)\n}\n\n// Take `BLOCK_SIZE` number of bytes from `msg` starting at `msg_start`.\n// Returns the block and the length that has been copied rather than padded with zeros.\nunconstrained fn build_msg_block(\n msg: [u8; N],\n message_size: u32,\n msg_start: u32,\n) -> (MSG_BLOCK, BLOCK_BYTE_PTR) {\n let mut msg_block: MSG_BLOCK = [0; INT_BLOCK_SIZE];\n\n // We insert `BLOCK_SIZE` bytes (or up to the end of the message)\n let block_input = if message_size < msg_start {\n // This function is sometimes called with `msg_start` past the end of the message.\n // In this case we return an empty block and zero pointer to signal that the result should be ignored.\n 0\n } else if message_size < msg_start + BLOCK_SIZE {\n message_size - msg_start\n } else {\n BLOCK_SIZE\n };\n\n // Figure out the number of items in the int array that we have to pack.\n // e.g. if the input is [0,1,2,3,4,5] then we need to pack it as 2 items: [0123, 4500]\n let mut int_input = block_input / INT_SIZE;\n if block_input % INT_SIZE != 0 {\n int_input = int_input + 1;\n };\n\n for i in 0..int_input {\n let mut msg_item: u32 = 0;\n // Always construct the integer as 4 bytes, even if it means going beyond the input.\n for j in 0..INT_SIZE {\n let k = i * INT_SIZE + j;\n let msg_byte = if k < block_input {\n msg[msg_start + k]\n } else {\n 0\n };\n msg_item = lshift8(msg_item, 1) + msg_byte as u32;\n }\n msg_block[i] = msg_item;\n }\n\n // Returning the index as if it was a 64 byte array.\n // We have to project it down to 16 items and bit shifting to get a byte back if we need it.\n (msg_block, block_input)\n}\n\n// Verify the block we are compressing was appropriately constructed by `build_msg_block`\n// and matches the input data. Returns the index of the first unset item.\n// If `message_size` is less than `msg_start` then this is called with the old non-empty block;\n// in that case we can skip verification, ie. no need to check that everything is zero.\nfn verify_msg_block(\n msg: [u8; N],\n message_size: u32,\n msg_block: MSG_BLOCK,\n msg_start: u32,\n) -> BLOCK_BYTE_PTR {\n let mut msg_byte_ptr = 0;\n let mut msg_end = msg_start + BLOCK_SIZE;\n if msg_end > N {\n msg_end = N;\n }\n // We might have to go beyond the input to pad the fields.\n if msg_end % INT_SIZE != 0 {\n msg_end = msg_end + INT_SIZE - msg_end % INT_SIZE;\n }\n\n // Reconstructed packed item.\n let mut msg_item: u32 = 0;\n\n // Inclusive at the end so that we can compare the last item.\n let mut i: u32 = 0;\n for k in msg_start..=msg_end {\n if k % INT_SIZE == 0 {\n // If we consumed some input we can compare against the block.\n if (msg_start < message_size) & (k > msg_start) {\n assert_eq(msg_block[i], msg_item as u32);\n i = i + 1;\n msg_item = 0;\n }\n }\n // Shift the accumulator\n msg_item = lshift8(msg_item, 1);\n // If we have input to consume, add it at the rightmost position.\n if k < message_size & k < msg_end {\n msg_item = msg_item + msg[k] as u32;\n msg_byte_ptr = msg_byte_ptr + 1;\n }\n }\n\n msg_byte_ptr\n}\n\n// Verify the block we are compressing was appropriately padded with zeros by `build_msg_block`.\n// This is only relevant for the last, potentially partially filled block.\nfn verify_msg_block_padding(msg_block: MSG_BLOCK, msg_byte_ptr: BLOCK_BYTE_PTR) {\n // Check all the way to the end of the block.\n verify_msg_block_zeros(msg_block, msg_byte_ptr, INT_BLOCK_SIZE);\n}\n\n// Verify that a region of ints in the message block are (partially) zeroed,\n// up to an (exclusive) maximum which can either be the end of the block\n// or just where the size is to be written.\nfn verify_msg_block_zeros(\n msg_block: MSG_BLOCK,\n mut msg_byte_ptr: BLOCK_BYTE_PTR,\n max_int_byte_ptr: u32,\n) {\n // This variable is used to get around the compiler under-constrained check giving a warning.\n // We want to check against a constant zero, but if it does not come from the circuit inputs\n // or return values the compiler check will issue a warning.\n let zero = msg_block[0] - msg_block[0];\n\n // First integer which is supposed to be (partially) zero.\n let mut int_byte_ptr = msg_byte_ptr / INT_SIZE;\n\n // Check partial zeros.\n let modulo = msg_byte_ptr % INT_SIZE;\n if modulo != 0 {\n let zeros = INT_SIZE - modulo;\n let mask = if zeros == 3 {\n TWO_POW_24\n } else if zeros == 2 {\n TWO_POW_16\n } else {\n TWO_POW_8\n };\n assert_eq(msg_block[int_byte_ptr] % mask, zero);\n int_byte_ptr = int_byte_ptr + 1;\n }\n\n // Check the rest of the items.\n for i in 0..max_int_byte_ptr {\n if i >= int_byte_ptr {\n assert_eq(msg_block[i], zero);\n }\n }\n}\n\n// Verify that up to the byte pointer the two blocks are equal.\n// At the byte pointer the new block can be partially zeroed.\nfn verify_msg_block_equals_last(\n msg_block: MSG_BLOCK,\n last_block: MSG_BLOCK,\n mut msg_byte_ptr: BLOCK_BYTE_PTR,\n) {\n // msg_byte_ptr is the position at which they are no longer have to be the same.\n // First integer which is supposed to be (partially) zero contains that pointer.\n let mut int_byte_ptr = msg_byte_ptr / INT_SIZE;\n\n // Check partial zeros.\n let modulo = msg_byte_ptr % INT_SIZE;\n if modulo != 0 {\n // Reconstruct the partially zero item from the last block.\n let last_field = last_block[int_byte_ptr];\n let mut msg_item: u32 = 0;\n // Reset to where they are still equal.\n msg_byte_ptr = msg_byte_ptr - modulo;\n for i in 0..INT_SIZE {\n msg_item = lshift8(msg_item, 1);\n if i < modulo {\n msg_item = msg_item + get_item_byte(last_field, msg_byte_ptr) as u32;\n msg_byte_ptr = msg_byte_ptr + 1;\n }\n }\n assert_eq(msg_block[int_byte_ptr], msg_item);\n }\n\n for i in 0..INT_SIZE_PTR {\n if i < int_byte_ptr {\n assert_eq(msg_block[i], last_block[i]);\n }\n }\n}\n\n// Set the rightmost `zeros` number of bytes to 0.\n#[inline_always]\nfn set_item_zeros(item: u32, zeros: u8) -> u32 {\n lshift8(rshift8(item, zeros), zeros)\n}\n\n// Replace one byte in the item with a value, and set everything after it to zero.\nfn set_item_byte_then_zeros(msg_item: u32, msg_byte_ptr: BLOCK_BYTE_PTR, msg_byte: u8) -> u32 {\n let zeros = INT_SIZE - msg_byte_ptr % INT_SIZE;\n let zeroed_item = set_item_zeros(msg_item, zeros as u8);\n let new_item = byte_into_item(msg_byte, msg_byte_ptr);\n zeroed_item + new_item\n}\n\n// Get a byte of a message item according to its overall position in the `BLOCK_SIZE` space.\nfn get_item_byte(mut msg_item: u32, msg_byte_ptr: BLOCK_BYTE_PTR) -> u8 {\n // How many times do we have to shift to the right to get to the position we want?\n let max_shifts = INT_SIZE - 1;\n let shifts = max_shifts - msg_byte_ptr % INT_SIZE;\n msg_item = rshift8(msg_item, shifts as u8);\n // At this point the byte we want is in the rightmost position.\n msg_item as u8\n}\n\n// Project a byte into a position in a field based on the overall block pointer.\n// For example putting 1 into pointer 5 would be 100, because overall we would\n// have [____, 0100] with indexes [0123,4567].\n#[inline_always]\nfn byte_into_item(msg_byte: u8, msg_byte_ptr: BLOCK_BYTE_PTR) -> u32 {\n let mut msg_item = msg_byte as u32;\n // How many times do we have to shift to the left to get to the position we want?\n let max_shifts = INT_SIZE - 1;\n let shifts = max_shifts - msg_byte_ptr % INT_SIZE;\n lshift8(msg_item, shifts as u8)\n}\n\n// Construct a field out of 4 bytes.\n#[inline_always]\nfn make_item(b0: u8, b1: u8, b2: u8, b3: u8) -> u32 {\n let mut item = b0 as u32;\n item = lshift8(item, 1) + b1 as u32;\n item = lshift8(item, 1) + b2 as u32;\n item = lshift8(item, 1) + b3 as u32;\n item\n}\n\n// Shift by 8 bits to the left between 0 and 4 times.\n// Checks `is_unconstrained()` to just use a bitshift if we're running in an unconstrained context,\n// otherwise multiplies by 256.\n#[inline_always]\nfn lshift8(item: u32, shifts: u8) -> u32 {\n if is_unconstrained() {\n // Brillig wouldn't shift 0<<4 without overflow.\n if shifts >= 4 {\n 0\n } else {\n item << (8 * shifts)\n }\n } else {\n // We can do a for loop up to INT_SIZE or an if-else.\n if shifts == 0 {\n item\n } else if shifts == 1 {\n item * TWO_POW_8\n } else if shifts == 2 {\n item * TWO_POW_16\n } else if shifts == 3 {\n item * TWO_POW_24\n } else {\n // Doesn't make sense, but it's most likely called on 0 anyway.\n 0\n }\n }\n}\n\n// Shift by 8 bits to the right between 0 and 4 times.\n// Checks `is_unconstrained()` to just use a bitshift if we're running in an unconstrained context,\n// otherwise divides by 256.\nfn rshift8(item: u32, shifts: u8) -> u32 {\n if is_unconstrained() {\n item >> (8 * shifts)\n } else {\n // Division wouldn't work on `Field`.\n if shifts == 0 {\n item\n } else if shifts == 1 {\n item / TWO_POW_8\n } else if shifts == 2 {\n item / TWO_POW_16\n } else if shifts == 3 {\n item / TWO_POW_24\n } else {\n 0\n }\n }\n}\n\n// Zero out all bytes between the end of the message and where the length is appended,\n// then write the length into the last 8 bytes of the block.\nunconstrained fn attach_len_to_msg_block(\n mut msg_block: MSG_BLOCK,\n mut msg_byte_ptr: BLOCK_BYTE_PTR,\n message_size: u32,\n) -> MSG_BLOCK {\n // We assume that `msg_byte_ptr` is less than 57 because if not then it is reset to zero before calling this function.\n // In any case, fill blocks up with zeros until the last 64 bits (i.e. until msg_byte_ptr = 56).\n // There can be one item which has to be partially zeroed.\n let modulo = msg_byte_ptr % INT_SIZE;\n if modulo != 0 {\n // Index of the block in which we find the item we need to partially zero.\n let i = msg_byte_ptr / INT_SIZE;\n let zeros = INT_SIZE - modulo;\n msg_block[i] = set_item_zeros(msg_block[i], zeros as u8);\n msg_byte_ptr = msg_byte_ptr + zeros;\n }\n\n // The rest can be zeroed without bit shifting anything.\n for i in (msg_byte_ptr / INT_SIZE)..INT_SIZE_PTR {\n msg_block[i] = 0;\n }\n\n // Set the last two 4 byte ints as the first/second half of the 8 bytes of the length.\n let len = 8 * message_size;\n let len_bytes: [u8; 8] = (len as Field).to_be_bytes();\n for i in 0..=1 {\n let shift = i * 4;\n msg_block[INT_SIZE_PTR + i] = make_item(\n len_bytes[shift],\n len_bytes[shift + 1],\n len_bytes[shift + 2],\n len_bytes[shift + 3],\n );\n }\n msg_block\n}\n\n// Verify that the message length was correctly written by `attach_len_to_msg_block`,\n// and that everything between the byte pointer and the size pointer was zeroed,\n// and that everything before the byte pointer was untouched.\nfn verify_msg_len(\n msg_block: MSG_BLOCK,\n last_block: MSG_BLOCK,\n msg_byte_ptr: BLOCK_BYTE_PTR,\n message_size: u32,\n) {\n // Check zeros up to the size pointer.\n verify_msg_block_zeros(msg_block, msg_byte_ptr, INT_SIZE_PTR);\n\n // Check that up to the pointer we match the last block.\n verify_msg_block_equals_last(msg_block, last_block, msg_byte_ptr);\n\n // We verify the message length was inserted correctly by reversing the byte decomposition.\n let mut reconstructed_len: u64 = 0;\n for i in INT_SIZE_PTR..INT_BLOCK_SIZE {\n reconstructed_len = reconstructed_len * TWO_POW_32;\n reconstructed_len = reconstructed_len + msg_block[i] as u64;\n }\n let len = 8 * message_size as u64;\n assert_eq(reconstructed_len, len);\n}\n\n// Perform the final compression, then transform the `STATE` into `HASH`.\nfn hash_final_block(msg_block: MSG_BLOCK, mut state: STATE) -> HASH {\n let mut out_h: HASH = [0; 32]; // Digest as sequence of bytes\n // Hash final padded block\n state = sha256_compression(msg_block, state);\n\n // Return final hash as byte array\n for j in 0..8 {\n let h_bytes: [u8; 4] = (state[j] as Field).to_be_bytes();\n for k in 0..4 {\n out_h[4 * j + k] = h_bytes[k];\n }\n }\n\n out_h\n}\n\nmod equivalence_test {\n\n #[test]\n fn test_implementations_agree(msg: [u8; 100], message_size: u64) {\n let message_size = message_size % 100;\n let unconstrained_sha = unsafe { super::__sha256_var(msg, message_size as u32) };\n let sha = super::sha256_var(msg, message_size);\n assert_eq(sha, unconstrained_sha);\n }\n}\n" - }, - "42": { - "path": "std/option.nr", - "source": "use crate::cmp::{Eq, Ord, Ordering};\nuse crate::default::Default;\nuse crate::hash::{Hash, Hasher};\n\npub struct Option {\n _is_some: bool,\n _value: T,\n}\n\nimpl Option {\n /// Constructs a None value\n pub fn none() -> Self {\n Self { _is_some: false, _value: crate::mem::zeroed() }\n }\n\n /// Constructs a Some wrapper around the given value\n pub fn some(_value: T) -> Self {\n Self { _is_some: true, _value }\n }\n\n /// True if this Option is None\n pub fn is_none(self) -> bool {\n !self._is_some\n }\n\n /// True if this Option is Some\n pub fn is_some(self) -> bool {\n self._is_some\n }\n\n /// Asserts `self.is_some()` and returns the wrapped value.\n pub fn unwrap(self) -> T {\n assert(self._is_some);\n self._value\n }\n\n /// Returns the inner value without asserting `self.is_some()`\n /// Note that if `self` is `None`, there is no guarantee what value will be returned,\n /// only that it will be of type `T`.\n pub fn unwrap_unchecked(self) -> T {\n self._value\n }\n\n /// Returns the wrapped value if `self.is_some()`. Otherwise, returns the given default value.\n pub fn unwrap_or(self, default: T) -> T {\n if self._is_some {\n self._value\n } else {\n default\n }\n }\n\n /// Returns the wrapped value if `self.is_some()`. Otherwise, calls the given function to return\n /// a default value.\n pub fn unwrap_or_else(self, default: fn[Env]() -> T) -> T {\n if self._is_some {\n self._value\n } else {\n default()\n }\n }\n\n /// Asserts `self.is_some()` with a provided custom message and returns the contained `Some` value\n pub fn expect(self, message: fmtstr) -> T {\n assert(self.is_some(), message);\n self._value\n }\n\n /// If self is `Some(x)`, this returns `Some(f(x))`. Otherwise, this returns `None`.\n pub fn map(self, f: fn[Env](T) -> U) -> Option {\n if self._is_some {\n Option::some(f(self._value))\n } else {\n Option::none()\n }\n }\n\n /// If self is `Some(x)`, this returns `f(x)`. Otherwise, this returns the given default value.\n pub fn map_or(self, default: U, f: fn[Env](T) -> U) -> U {\n if self._is_some {\n f(self._value)\n } else {\n default\n }\n }\n\n /// If self is `Some(x)`, this returns `f(x)`. Otherwise, this returns `default()`.\n pub fn map_or_else(self, default: fn[Env1]() -> U, f: fn[Env2](T) -> U) -> U {\n if self._is_some {\n f(self._value)\n } else {\n default()\n }\n }\n\n /// Returns None if self is None. Otherwise, this returns `other`.\n pub fn and(self, other: Self) -> Self {\n if self.is_none() {\n Option::none()\n } else {\n other\n }\n }\n\n /// If self is None, this returns None. Otherwise, this calls the given function\n /// with the Some value contained within self, and returns the result of that call.\n ///\n /// In some languages this function is called `flat_map` or `bind`.\n pub fn and_then(self, f: fn[Env](T) -> Option) -> Option {\n if self._is_some {\n f(self._value)\n } else {\n Option::none()\n }\n }\n\n /// If self is Some, return self. Otherwise, return `other`.\n pub fn or(self, other: Self) -> Self {\n if self._is_some {\n self\n } else {\n other\n }\n }\n\n /// If self is Some, return self. Otherwise, return `default()`.\n pub fn or_else(self, default: fn[Env]() -> Self) -> Self {\n if self._is_some {\n self\n } else {\n default()\n }\n }\n\n // If only one of the two Options is Some, return that option.\n // Otherwise, if both options are Some or both are None, None is returned.\n pub fn xor(self, other: Self) -> Self {\n if self._is_some {\n if other._is_some {\n Option::none()\n } else {\n self\n }\n } else if other._is_some {\n other\n } else {\n Option::none()\n }\n }\n\n /// Returns `Some(x)` if self is `Some(x)` and `predicate(x)` is true.\n /// Otherwise, this returns `None`\n pub fn filter(self, predicate: fn[Env](T) -> bool) -> Self {\n if self._is_some {\n if predicate(self._value) {\n self\n } else {\n Option::none()\n }\n } else {\n Option::none()\n }\n }\n\n /// Flattens an Option> into a Option.\n /// This returns None if the outer Option is None. Otherwise, this returns the inner Option.\n pub fn flatten(option: Option>) -> Option {\n if option._is_some {\n option._value\n } else {\n Option::none()\n }\n }\n}\n\nimpl Default for Option {\n fn default() -> Self {\n Option::none()\n }\n}\n\nimpl Eq for Option\nwhere\n T: Eq,\n{\n fn eq(self, other: Self) -> bool {\n if self._is_some == other._is_some {\n if self._is_some {\n self._value == other._value\n } else {\n true\n }\n } else {\n false\n }\n }\n}\n\nimpl Hash for Option\nwhere\n T: Hash,\n{\n fn hash(self, state: &mut H)\n where\n H: Hasher,\n {\n self._is_some.hash(state);\n if self._is_some {\n self._value.hash(state);\n }\n }\n}\n\n// For this impl we're declaring Option::none < Option::some\nimpl Ord for Option\nwhere\n T: Ord,\n{\n fn cmp(self, other: Self) -> Ordering {\n if self._is_some {\n if other._is_some {\n self._value.cmp(other._value)\n } else {\n Ordering::greater()\n }\n } else if other._is_some {\n Ordering::less()\n } else {\n Ordering::equal()\n }\n }\n}\n" - }, - "43": { - "path": "std/panic.nr", - "source": "pub fn panic(message: fmtstr) -> U {\n assert(false, message);\n crate::mem::zeroed()\n}\n" - }, - "50": { - "path": "/mnt/user-data/saleel/aztec-web-starter/contracts/src/main.nr", - "source": "use dep::aztec::macros::aztec;\n\n#[aztec]\npub contract EasyPrivateVoting {\n use dep::aztec:: macros::{\n functions::{initializer, internal, private, public, utility}, \n storage::storage\n };\n use dep::aztec::keys::getters::get_public_keys;\n use dep::aztec::prelude::{AztecAddress, Map, PublicImmutable, PublicMutable};\n use dep::aztec::protocol_types::traits::{Hash, ToField};\n\n #[storage]\n struct Storage {\n admin: PublicMutable, // admin can end vote\n tally: Map, Context>, // we will store candidate as key and number of votes as value\n vote_ended: PublicMutable, // vote_ended is boolean\n active_at_block: PublicImmutable, // when people can start voting\n }\n\n #[public]\n #[initializer]\n fn constructor(admin: AztecAddress) {\n storage.admin.write(admin);\n storage.vote_ended.write(false);\n storage.active_at_block.initialize(context.block_number() as u32);\n }\n\n #[private]\n fn cast_vote(candidate: Field) {\n let msg_sender_npk_m_hash = get_public_keys(context.msg_sender()).npk_m.hash();\n\n let secret = context.request_nsk_app(msg_sender_npk_m_hash); // get secret key of caller of function\n let nullifier = std::hash::pedersen_hash([context.msg_sender().to_field(), secret]); // derive nullifier from sender and secret\n context.push_nullifier(nullifier);\n EasyPrivateVoting::at(context.this_address()).add_to_tally_public(candidate).enqueue(\n &mut context,\n );\n }\n\n #[public]\n #[internal]\n fn add_to_tally_public(candidate: Field) {\n assert(storage.vote_ended.read() == false, \"Vote has ended\"); // assert that vote has not ended\n let new_tally = storage.tally.at(candidate).read() + 1;\n storage.tally.at(candidate).write(new_tally);\n }\n\n #[public]\n fn end_vote() {\n assert(storage.admin.read().eq(context.msg_sender()), \"Only admin can end votes\"); // assert that caller is admin\n storage.vote_ended.write(true);\n }\n\n #[utility]\n unconstrained fn get_vote(candidate: Field) -> Field {\n storage.tally.at(candidate).read()\n }\n}" - }, - "51": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/capsules/mod.nr", - "source": "use crate::oracle::capsules;\nuse protocol_types::{address::AztecAddress, traits::{Deserialize, Serialize}};\n\n/// A dynamically sized array backed by PXE's non-volatile database (called capsules). Values are persisted until\n/// deleted, so they can be e.g. stored during simulation of a transaction and later retrieved during witness\n/// generation. All values are scoped per contract address, so external contracts cannot access them.\npub struct CapsuleArray {\n contract_address: AztecAddress,\n /// The base slot is where the array length is stored in capsules. Array elements are stored in consecutive slots\n /// after the base slot. For example, with base slot 5: the length is at slot 5, the first element (index 0) is at\n /// slot 6, the second element (index 1) is at slot 7, and so on.\n base_slot: Field,\n}\n\nimpl CapsuleArray {\n /// Returns a CapsuleArray connected to a contract's capsules at a base slot. Array elements are stored in\n /// contiguous slots following the base slot, so there should be sufficient space between array base slots to\n /// accommodate elements. A reasonable strategy is to make the base slot a hash of a unique value.\n pub unconstrained fn at(contract_address: AztecAddress, base_slot: Field) -> Self {\n Self { contract_address, base_slot }\n }\n\n /// Returns the number of elements stored in the array.\n pub unconstrained fn len(self) -> u32 {\n // An uninitialized array defaults to a length of 0.\n capsules::load(self.contract_address, self.base_slot).unwrap_or(0) as u32\n }\n\n /// Stores a value at the end of the array.\n pub unconstrained fn push(self, value: T)\n where\n T: Serialize,\n {\n let current_length = self.len();\n\n // The slot corresponding to the index `current_length` is the first slot immediately after the end of the\n // array, which is where we want to place the new value.\n capsules::store(self.contract_address, self.slot_at(current_length), value);\n\n // Then we simply update the length.\n let new_length = current_length + 1;\n capsules::store(self.contract_address, self.base_slot, new_length);\n }\n\n /// Retrieves the value stored in the array at `index`. Throws if the index is out of bounds.\n pub unconstrained fn get(self, index: u32) -> T\n where\n T: Deserialize,\n {\n assert(index < self.len(), \"Attempted to read past the length of a CapsuleArray\");\n\n capsules::load(self.contract_address, self.slot_at(index)).unwrap()\n }\n\n /// Deletes the value stored in the array at `index`. Throws if the index is out of bounds.\n pub unconstrained fn remove(self, index: u32) {\n let current_length = self.len();\n assert(index < current_length, \"Attempted to delete past the length of a CapsuleArray\");\n\n // In order to be able to remove elements at arbitrary indices, we need to shift the entire contents of the\n // array past the removed element one slot backward so that we don't end up with a gap and preserve the\n // contiguous slots. We can skip this when deleting the last element however.\n if index != current_length - 1 {\n // The source and destination regions overlap, but `copy` supports this.\n capsules::copy(\n self.contract_address,\n self.slot_at(index + 1),\n self.slot_at(index),\n current_length - index - 1,\n );\n }\n\n // We can now delete the last element (which has either been copied to the slot immediately before it, or was\n // the element we meant to delete in the first place) and update the length.\n capsules::delete(self.contract_address, self.slot_at(current_length - 1));\n capsules::store(self.contract_address, self.base_slot, current_length - 1);\n }\n\n /// Iterates over the entire array, calling the callback with all values and their array index. The order in which\n /// values are processed is arbitrary.\n ///\n /// It is safe to delete the current element (and only the current element) from inside the callback via `remove`:\n /// ```noir\n /// array.for_each(|index, value| {\n /// if some_condition(value) {\n /// array.remove(index); // safe only for this index\n /// }\n /// }\n /// ```\n ///\n /// If all elements in the array need to iterated over and then removed, then using `for_each` results in optimal\n /// efficiency.\n ///\n /// It is **not** safe to push new elements into the array from inside the callback.\n pub unconstrained fn for_each(self, f: unconstrained fn[Env](u32, T) -> ())\n where\n T: Deserialize,\n {\n // Iterating over all elements is simple, but we want to do it in such a way that a) deleting the current\n // element is safe to do, and b) deleting *all* elements is optimally efficient. This is because CapsuleArrays\n // are typically used to hold pending tasks, so iterating them while clearing completed tasks (sometimes\n // unconditionally, resulting in a full clear) is a very common access pattern.\n //\n // The way we achieve this is by iterating backwards: each element can always be deleted since it won't change\n // any preceding (lower) indices, and if every element is deleted then every element will (in turn) be the last\n // element. This results in an optimal full clear since `remove` will be able to skip the `capsules::copy` call\n // to shift any elements past the deleted one (because there will be none).\n let mut i = self.len();\n while i > 0 {\n i -= 1;\n f(i, self.get(i));\n }\n }\n\n unconstrained fn slot_at(self, index: u32) -> Field {\n // Elements are stored immediately after the base slot, so we add 1 to it to compute the slot for the first\n // element.\n self.base_slot + 1 + index as Field\n }\n}\n\nmod test {\n use crate::test::helpers::test_environment::TestEnvironment;\n use super::CapsuleArray;\n use protocol_types::address::AztecAddress;\n\n global SLOT: Field = 1230;\n\n unconstrained fn setup() -> AztecAddress {\n TestEnvironment::new().utility().this_address()\n }\n\n #[test]\n unconstrained fn empty_array() {\n let contract_address = setup();\n\n let array: CapsuleArray = CapsuleArray::at(contract_address, SLOT);\n assert_eq(array.len(), 0);\n }\n\n #[test(should_fail_with = \"Attempted to read past the length of a CapsuleArray\")]\n unconstrained fn empty_array_read() {\n let contract_address = setup();\n\n let array = CapsuleArray::at(contract_address, SLOT);\n let _: Field = array.get(0);\n }\n\n #[test]\n unconstrained fn array_push() {\n let contract_address = setup();\n\n let array = CapsuleArray::at(contract_address, SLOT);\n array.push(5);\n\n assert_eq(array.len(), 1);\n assert_eq(array.get(0), 5);\n }\n\n #[test(should_fail_with = \"Attempted to read past the length of a CapsuleArray\")]\n unconstrained fn read_past_len() {\n let contract_address = setup();\n\n let array = CapsuleArray::at(contract_address, SLOT);\n array.push(5);\n\n let _ = array.get(1);\n }\n\n #[test]\n unconstrained fn array_remove_last() {\n let contract_address = setup();\n\n let array = CapsuleArray::at(contract_address, SLOT);\n\n array.push(5);\n array.remove(0);\n\n assert_eq(array.len(), 0);\n }\n\n #[test]\n unconstrained fn array_remove_some() {\n let contract_address = setup();\n\n let array = CapsuleArray::at(contract_address, SLOT);\n\n array.push(7);\n array.push(8);\n array.push(9);\n\n assert_eq(array.len(), 3);\n assert_eq(array.get(0), 7);\n assert_eq(array.get(1), 8);\n assert_eq(array.get(2), 9);\n\n array.remove(1);\n\n assert_eq(array.len(), 2);\n assert_eq(array.get(0), 7);\n assert_eq(array.get(1), 9);\n }\n\n #[test]\n unconstrained fn array_remove_all() {\n let contract_address = setup();\n\n let array = CapsuleArray::at(contract_address, SLOT);\n\n array.push(7);\n array.push(8);\n array.push(9);\n\n array.remove(1);\n array.remove(1);\n array.remove(0);\n\n assert_eq(array.len(), 0);\n }\n\n #[test]\n unconstrained fn for_each_called_with_all_elements() {\n let contract_address = setup();\n let array = CapsuleArray::at(contract_address, SLOT);\n\n array.push(4);\n array.push(5);\n array.push(6);\n\n // We store all values that we were called with and check that all (value, index) tuples are present. Note that\n // we do not care about the order in which each tuple was passed to the closure.\n let called_with = &mut BoundedVec::<(u32, Field), 3>::new();\n array.for_each(|index, value| { called_with.push((index, value)); });\n\n assert_eq(called_with.len(), 3);\n assert(called_with.any(|(index, value)| (index == 0) & (value == 4)));\n assert(called_with.any(|(index, value)| (index == 1) & (value == 5)));\n assert(called_with.any(|(index, value)| (index == 2) & (value == 6)));\n }\n\n #[test]\n unconstrained fn for_each_remove_some() {\n let contract_address = setup();\n let array = CapsuleArray::at(contract_address, SLOT);\n\n array.push(4);\n array.push(5);\n array.push(6);\n\n array.for_each(|index, _| {\n if index == 1 {\n array.remove(index);\n }\n });\n\n assert_eq(array.len(), 2);\n assert_eq(array.get(0), 4);\n assert_eq(array.get(1), 6);\n }\n\n #[test]\n unconstrained fn for_each_remove_all() {\n let contract_address = setup();\n let array = CapsuleArray::at(contract_address, SLOT);\n\n array.push(4);\n array.push(5);\n array.push(6);\n\n array.for_each(|index, _| { array.remove(index); });\n\n assert_eq(array.len(), 0);\n }\n\n // TODO: uncomment this test once OracleMock::count is implemented in the stdlib.\n // #[test]\n // unconstrained fn for_each_remove_all_no_copy() {\n // let contract_address = setup();\n // let array = CapsuleArray::at(contract_address, SLOT);\n\n // array.push(4);\n // array.push(5);\n // array.push(6);\n\n // // We test that the copyCapsule was never called, which is the expensive operation we want to avoid.\n // let mock = OracleMock::mock(\"copyCapsule\");\n\n // array.for_each(|index, _| {\n // array.remove(index);\n // });\n\n // assert_eq(mock.count(), 0);\n // }\n}\n" - }, - "52": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/context/call_interfaces.nr", - "source": "use dep::protocol_types::{\n abis::function_selector::FunctionSelector,\n address::AztecAddress,\n traits::{Deserialize, ToField},\n};\n\nuse crate::context::{gas::GasOpts, private_context::PrivateContext, public_context::PublicContext};\n\nuse crate::hash::{hash_args, hash_calldata};\nuse crate::oracle::execution_cache;\n\npub trait CallInterface {\n fn get_args(self) -> [Field];\n fn get_selector(self) -> FunctionSelector;\n fn get_name(self) -> str;\n fn get_contract_address(self) -> AztecAddress;\n fn get_is_static(self) -> bool;\n}\n\n// PrivateCallInterface\n\npub struct PrivateCallInterface {\n target_contract: AztecAddress,\n selector: FunctionSelector,\n name: str,\n args_hash: Field,\n args: [Field],\n return_type: T,\n is_static: bool,\n}\n\nimpl PrivateCallInterface {\n pub fn new(\n target_contract: AztecAddress,\n selector: FunctionSelector,\n name: str,\n args: [Field],\n is_static: bool,\n ) -> Self {\n let args_hash = hash_args(args);\n Self {\n target_contract,\n selector,\n name,\n args_hash,\n args,\n return_type: std::mem::zeroed(),\n is_static,\n }\n }\n\n pub fn call(self, context: &mut PrivateContext) -> T\n where\n T: Deserialize,\n {\n execution_cache::store(self.args, self.args_hash);\n let returns_hash = context.call_private_function_with_args_hash(\n self.target_contract,\n self.selector,\n self.args_hash,\n false,\n );\n let returns: T = returns_hash.get_preimage();\n returns\n }\n\n pub fn view(self, context: &mut PrivateContext) -> T\n where\n T: Deserialize,\n {\n execution_cache::store(self.args, self.args_hash);\n let returns_hash = context.call_private_function_with_args_hash(\n self.target_contract,\n self.selector,\n self.args_hash,\n true,\n );\n returns_hash.get_preimage()\n }\n}\n\nimpl CallInterface for PrivateCallInterface {\n fn get_args(self) -> [Field] {\n self.args\n }\n\n fn get_selector(self) -> FunctionSelector {\n self.selector\n }\n\n fn get_name(self) -> str {\n self.name\n }\n\n fn get_contract_address(self) -> AztecAddress {\n self.target_contract\n }\n\n fn get_is_static(self) -> bool {\n self.is_static\n }\n}\n\n// PrivateVoidCallInterface\n\npub struct PrivateVoidCallInterface {\n target_contract: AztecAddress,\n selector: FunctionSelector,\n name: str,\n args_hash: Field,\n args: [Field],\n return_type: (), // Unit type () indicates this interface is for functions that return nothing (void)\n is_static: bool,\n}\n\nimpl PrivateVoidCallInterface {\n pub fn new(\n target_contract: AztecAddress,\n selector: FunctionSelector,\n name: str,\n args: [Field],\n is_static: bool,\n ) -> Self {\n let args_hash = hash_args(args);\n Self { target_contract, selector, name, args_hash, args, return_type: (), is_static }\n }\n\n pub fn call(self, context: &mut PrivateContext) {\n execution_cache::store(self.args, self.args_hash);\n context\n .call_private_function_with_args_hash(\n self.target_contract,\n self.selector,\n self.args_hash,\n false,\n )\n .assert_empty();\n }\n\n pub fn view(self, context: &mut PrivateContext) {\n execution_cache::store(self.args, self.args_hash);\n context\n .call_private_function_with_args_hash(\n self.target_contract,\n self.selector,\n self.args_hash,\n true,\n )\n .assert_empty();\n }\n}\n\nimpl CallInterface for PrivateVoidCallInterface {\n fn get_args(self) -> [Field] {\n self.args\n }\n\n fn get_selector(self) -> FunctionSelector {\n self.selector\n }\n\n fn get_name(self) -> str {\n self.name\n }\n\n fn get_contract_address(self) -> AztecAddress {\n self.target_contract\n }\n\n fn get_is_static(self) -> bool {\n self.is_static\n }\n}\n\n// PrivateStaticCallInterface\n\npub struct PrivateStaticCallInterface {\n target_contract: AztecAddress,\n selector: FunctionSelector,\n name: str,\n args_hash: Field,\n args: [Field],\n return_type: T,\n is_static: bool,\n}\n\nimpl PrivateStaticCallInterface {\n pub fn new(\n target_contract: AztecAddress,\n selector: FunctionSelector,\n name: str,\n args: [Field],\n ) -> Self {\n let args_hash = hash_args(args);\n Self {\n target_contract,\n selector,\n name,\n args_hash,\n args,\n return_type: std::mem::zeroed(),\n is_static: true,\n }\n }\n\n pub fn view(self, context: &mut PrivateContext) -> T\n where\n T: Deserialize,\n {\n execution_cache::store(self.args, self.args_hash);\n let returns = context.call_private_function_with_args_hash(\n self.target_contract,\n self.selector,\n self.args_hash,\n true,\n );\n returns.get_preimage()\n }\n}\n\nimpl CallInterface for PrivateStaticCallInterface {\n fn get_args(self) -> [Field] {\n self.args\n }\n\n fn get_selector(self) -> FunctionSelector {\n self.selector\n }\n\n fn get_name(self) -> str {\n self.name\n }\n\n fn get_contract_address(self) -> AztecAddress {\n self.target_contract\n }\n\n fn get_is_static(self) -> bool {\n self.is_static\n }\n}\n\n// PrivateStaticVoidCallInterface\n\npub struct PrivateStaticVoidCallInterface {\n target_contract: AztecAddress,\n selector: FunctionSelector,\n name: str,\n args_hash: Field,\n args: [Field],\n return_type: (), // Unit type () indicates this interface is for functions that return nothing (void)\n is_static: bool,\n}\n\nimpl PrivateStaticVoidCallInterface {\n pub fn new(\n target_contract: AztecAddress,\n selector: FunctionSelector,\n name: str,\n args: [Field],\n ) -> Self {\n let args_hash = hash_args(args);\n Self { target_contract, selector, name, args_hash, args, return_type: (), is_static: true }\n }\n\n pub fn view(self, context: &mut PrivateContext) {\n execution_cache::store(self.args, self.args_hash);\n context\n .call_private_function_with_args_hash(\n self.target_contract,\n self.selector,\n self.args_hash,\n true,\n )\n .assert_empty();\n }\n}\n\nimpl CallInterface for PrivateStaticVoidCallInterface {\n fn get_args(self) -> [Field] {\n self.args\n }\n\n fn get_selector(self) -> FunctionSelector {\n self.selector\n }\n\n fn get_name(self) -> str {\n self.name\n }\n\n fn get_contract_address(self) -> AztecAddress {\n self.target_contract\n }\n\n fn get_is_static(self) -> bool {\n self.is_static\n }\n}\n\n// PublicCallInterface\n\npub struct PublicCallInterface {\n target_contract: AztecAddress,\n selector: FunctionSelector,\n name: str,\n args: [Field],\n gas_opts: GasOpts,\n return_type: T,\n is_static: bool,\n}\n\nimpl PublicCallInterface {\n pub fn new(\n target_contract: AztecAddress,\n selector: FunctionSelector,\n name: str,\n args: [Field],\n is_static: bool,\n ) -> Self {\n Self {\n target_contract,\n selector,\n name,\n args,\n gas_opts: GasOpts::default(),\n return_type: std::mem::zeroed(),\n is_static,\n }\n }\n\n pub fn with_gas(self: &mut Self, gas_opts: GasOpts) -> &mut Self {\n self.gas_opts = gas_opts;\n self\n }\n\n pub unconstrained fn call(self, context: &mut PublicContext) -> T\n where\n T: Deserialize,\n {\n let returns = context.call_public_function(\n self.target_contract,\n self.selector,\n self.args,\n self.gas_opts,\n );\n Deserialize::deserialize(returns.as_array::())\n }\n\n pub unconstrained fn view(self, context: &mut PublicContext) -> T\n where\n T: Deserialize,\n {\n let returns = context.static_call_public_function(\n self.target_contract,\n self.selector,\n self.args,\n self.gas_opts,\n );\n Deserialize::deserialize(returns.as_array::())\n }\n\n pub fn enqueue(self, context: &mut PrivateContext) {\n let calldata = self.args.push_front(self.selector.to_field());\n let calldata_hash = hash_calldata(calldata);\n execution_cache::store(calldata, calldata_hash);\n context.call_public_function_with_calldata_hash(\n self.target_contract,\n calldata_hash,\n /*static=*/\n false,\n )\n }\n\n pub fn enqueue_view(self, context: &mut PrivateContext) {\n let calldata = self.args.push_front(self.selector.to_field());\n let calldata_hash = hash_calldata(calldata);\n execution_cache::store(calldata, calldata_hash);\n context.call_public_function_with_calldata_hash(\n self.target_contract,\n calldata_hash,\n /*static=*/\n true,\n )\n }\n}\n\nimpl CallInterface for PublicCallInterface {\n fn get_args(self) -> [Field] {\n self.args\n }\n\n fn get_selector(self) -> FunctionSelector {\n self.selector\n }\n\n fn get_name(self) -> str {\n self.name\n }\n\n fn get_contract_address(self) -> AztecAddress {\n self.target_contract\n }\n\n fn get_is_static(self) -> bool {\n self.is_static\n }\n}\n\n// PublicVoidCallInterface\n\npub struct PublicVoidCallInterface {\n target_contract: AztecAddress,\n selector: FunctionSelector,\n name: str,\n args: [Field],\n return_type: (), // Unit type () indicates this interface is for functions that return nothing (void)\n is_static: bool,\n gas_opts: GasOpts,\n}\n\nimpl PublicVoidCallInterface {\n pub fn new(\n target_contract: AztecAddress,\n selector: FunctionSelector,\n name: str,\n args: [Field],\n is_static: bool,\n ) -> Self {\n Self {\n target_contract,\n selector,\n name,\n args,\n return_type: (),\n is_static,\n gas_opts: GasOpts::default(),\n }\n }\n\n pub fn with_gas(self: &mut Self, gas_opts: GasOpts) -> &mut Self {\n self.gas_opts = gas_opts;\n self\n }\n\n pub unconstrained fn call(self, context: &mut PublicContext) {\n let returns = context.call_public_function(\n self.target_contract,\n self.selector,\n self.args,\n self.gas_opts,\n );\n assert(returns.len() == 0);\n }\n\n pub unconstrained fn view(self, context: &mut PublicContext) {\n let returns = context.static_call_public_function(\n self.target_contract,\n self.selector,\n self.args,\n self.gas_opts,\n );\n assert(returns.len() == 0);\n }\n\n pub fn enqueue(self, context: &mut PrivateContext) {\n let calldata = self.args.push_front(self.selector.to_field());\n let calldata_hash = hash_calldata(calldata);\n execution_cache::store(calldata, calldata_hash);\n context.call_public_function_with_calldata_hash(\n self.target_contract,\n calldata_hash,\n /*static=*/\n false,\n )\n }\n\n pub fn enqueue_view(self, context: &mut PrivateContext) {\n let calldata = self.args.push_front(self.selector.to_field());\n let calldata_hash = hash_calldata(calldata);\n execution_cache::store(calldata, calldata_hash);\n context.call_public_function_with_calldata_hash(\n self.target_contract,\n calldata_hash,\n /*static=*/\n true,\n )\n }\n\n pub fn set_as_teardown(self, context: &mut PrivateContext) {\n let calldata = self.args.push_front(self.selector.to_field());\n let calldata_hash = hash_calldata(calldata);\n execution_cache::store(calldata, calldata_hash);\n context.set_public_teardown_function_with_calldata_hash(\n self.target_contract,\n calldata_hash,\n false,\n )\n }\n}\n\nimpl CallInterface for PublicVoidCallInterface {\n fn get_args(self) -> [Field] {\n self.args\n }\n\n fn get_selector(self) -> FunctionSelector {\n self.selector\n }\n\n fn get_name(self) -> str {\n self.name\n }\n\n fn get_contract_address(self) -> AztecAddress {\n self.target_contract\n }\n\n fn get_is_static(self) -> bool {\n self.is_static\n }\n}\n\n// PublicStaticCallInterface\n\npub struct PublicStaticCallInterface {\n target_contract: AztecAddress,\n selector: FunctionSelector,\n name: str,\n args: [Field],\n return_type: T,\n is_static: bool,\n gas_opts: GasOpts,\n}\n\nimpl PublicStaticCallInterface {\n pub fn new(\n target_contract: AztecAddress,\n selector: FunctionSelector,\n name: str,\n args: [Field],\n ) -> Self {\n Self {\n target_contract,\n selector,\n name,\n args,\n return_type: std::mem::zeroed(),\n is_static: true,\n gas_opts: GasOpts::default(),\n }\n }\n\n pub fn with_gas(self: &mut Self, gas_opts: GasOpts) -> &mut Self {\n self.gas_opts = gas_opts;\n self\n }\n\n pub unconstrained fn view(self, context: &mut PublicContext) -> T\n where\n T: Deserialize,\n {\n let returns = context.static_call_public_function(\n self.target_contract,\n self.selector,\n self.args,\n self.gas_opts,\n );\n Deserialize::deserialize(returns.as_array::())\n }\n\n pub fn enqueue_view(self, context: &mut PrivateContext) {\n let calldata = self.args.push_front(self.selector.to_field());\n let calldata_hash = hash_calldata(calldata);\n execution_cache::store(calldata, calldata_hash);\n context.call_public_function_with_calldata_hash(\n self.target_contract,\n calldata_hash,\n /*static=*/\n true,\n )\n }\n}\n\nimpl CallInterface for PublicStaticCallInterface {\n fn get_args(self) -> [Field] {\n self.args\n }\n\n fn get_selector(self) -> FunctionSelector {\n self.selector\n }\n\n fn get_name(self) -> str {\n self.name\n }\n\n fn get_contract_address(self) -> AztecAddress {\n self.target_contract\n }\n\n fn get_is_static(self) -> bool {\n self.is_static\n }\n}\n\n// PublicStaticVoidCallInterface\n\npub struct PublicStaticVoidCallInterface {\n target_contract: AztecAddress,\n selector: FunctionSelector,\n name: str,\n args: [Field],\n return_type: (), // Unit type () indicates this interface is for functions that return nothing (void)\n is_static: bool,\n gas_opts: GasOpts,\n}\n\nimpl PublicStaticVoidCallInterface {\n pub fn new(\n target_contract: AztecAddress,\n selector: FunctionSelector,\n name: str,\n args: [Field],\n ) -> Self {\n Self {\n target_contract,\n selector,\n name,\n args,\n return_type: (),\n is_static: true,\n gas_opts: GasOpts::default(),\n }\n }\n\n pub fn with_gas(self: &mut Self, gas_opts: GasOpts) -> &mut Self {\n self.gas_opts = gas_opts;\n self\n }\n\n pub unconstrained fn view(self, context: &mut PublicContext) {\n let returns = context.static_call_public_function(\n self.target_contract,\n self.selector,\n self.args,\n self.gas_opts,\n );\n assert(returns.len() == 0);\n }\n\n pub fn enqueue_view(self, context: &mut PrivateContext) {\n let calldata = self.args.push_front(self.selector.to_field());\n let calldata_hash = hash_calldata(calldata);\n execution_cache::store(calldata, calldata_hash);\n context.call_public_function_with_calldata_hash(\n self.target_contract,\n calldata_hash,\n /*static=*/\n true,\n )\n }\n}\n\nimpl CallInterface for PublicStaticVoidCallInterface {\n fn get_args(self) -> [Field] {\n self.args\n }\n\n fn get_selector(self) -> FunctionSelector {\n self.selector\n }\n\n fn get_name(self) -> str {\n self.name\n }\n\n fn get_contract_address(self) -> AztecAddress {\n self.target_contract\n }\n\n fn get_is_static(self) -> bool {\n self.is_static\n }\n}\n\n// UtilityCallInterface\n\npub struct UtilityCallInterface {\n target_contract: AztecAddress,\n selector: FunctionSelector,\n name: str,\n args_hash: Field,\n args: [Field],\n return_type: T,\n}\n\nimpl UtilityCallInterface {\n pub fn new(\n target_contract: AztecAddress,\n selector: FunctionSelector,\n name: str,\n args: [Field],\n ) -> Self {\n let args_hash = hash_args(args);\n Self { target_contract, selector, name, args_hash, args, return_type: std::mem::zeroed() }\n }\n\n pub fn get_args(self) -> [Field] {\n self.args\n }\n\n pub fn get_selector(self) -> FunctionSelector {\n self.selector\n }\n\n pub fn get_name(self) -> str {\n self.name\n }\n\n pub fn get_contract_address(self) -> AztecAddress {\n self.target_contract\n }\n}\n\n// UtilityVoidCallInterface\n\npub struct UtilityVoidCallInterface {\n target_contract: AztecAddress,\n selector: FunctionSelector,\n name: str,\n args_hash: Field,\n args: [Field],\n return_type: (),\n}\n\nimpl UtilityVoidCallInterface {\n pub fn new(\n target_contract: AztecAddress,\n selector: FunctionSelector,\n name: str,\n args: [Field],\n ) -> Self {\n let args_hash = hash_args(args);\n Self { target_contract, selector, name, args_hash, args, return_type: () }\n }\n\n pub fn get_args(self) -> [Field] {\n self.args\n }\n\n pub fn get_selector(self) -> FunctionSelector {\n self.selector\n }\n\n pub fn get_name(self) -> str {\n self.name\n }\n\n pub fn get_contract_address(self) -> AztecAddress {\n self.target_contract\n }\n}\n" - }, - "59": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/context/private_context.nr", - "source": "use crate::{\n context::{inputs::PrivateContextInputs, returns_hash::ReturnsHash},\n hash::{ArgsHasher, hash_args_array, hash_calldata_array},\n keys::constants::{NULLIFIER_INDEX, NUM_KEY_TYPES, OUTGOING_INDEX, sk_generators},\n messaging::process_l1_to_l2_message,\n oracle::{\n block_header::get_block_header_at,\n call_private_function::call_private_function_internal,\n enqueue_public_function_call::{\n notify_enqueued_public_function_call, notify_set_min_revertible_side_effect_counter,\n notify_set_public_teardown_function_call,\n },\n execution_cache,\n key_validation_request::get_key_validation_request,\n notes::{notify_created_nullifier, notify_nullified_note},\n },\n};\nuse dep::protocol_types::{\n abis::{\n call_context::CallContext,\n function_selector::FunctionSelector,\n gas_settings::GasSettings,\n log_hash::LogHash,\n max_block_number::MaxBlockNumber,\n note_hash::NoteHash,\n nullifier::Nullifier,\n private_call_request::PrivateCallRequest,\n private_circuit_public_inputs::PrivateCircuitPublicInputs,\n private_log::{PrivateLog, PrivateLogData},\n public_call_request::PublicCallRequest,\n read_request::ReadRequest,\n side_effect::Counted,\n validation_requests::{KeyValidationRequest, KeyValidationRequestAndGenerator},\n },\n address::{AztecAddress, EthAddress},\n block_header::BlockHeader,\n constants::{\n MAX_CONTRACT_CLASS_LOGS_PER_CALL, MAX_ENQUEUED_CALLS_PER_CALL,\n MAX_KEY_VALIDATION_REQUESTS_PER_CALL, MAX_L2_TO_L1_MSGS_PER_CALL,\n MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, MAX_NOTE_HASHES_PER_CALL,\n MAX_NULLIFIER_READ_REQUESTS_PER_CALL, MAX_NULLIFIERS_PER_CALL,\n MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, MAX_PRIVATE_LOGS_PER_CALL,\n PRIVATE_LOG_SIZE_IN_FIELDS,\n },\n messaging::l2_to_l1_message::L2ToL1Message,\n traits::{Empty, Hash, ToField},\n utils::arrays::array_concat,\n};\n\n// When finished, one can call .finish() to convert back to the abi\npub struct PrivateContext {\n // docs:start:private-context\n pub inputs: PrivateContextInputs,\n pub side_effect_counter: u32,\n\n pub min_revertible_side_effect_counter: u32,\n pub is_fee_payer: bool,\n\n pub args_hash: Field,\n pub return_hash: Field,\n\n pub max_block_number: MaxBlockNumber,\n\n pub note_hash_read_requests: BoundedVec,\n pub nullifier_read_requests: BoundedVec,\n key_validation_requests_and_generators: BoundedVec,\n\n pub note_hashes: BoundedVec,\n pub nullifiers: BoundedVec,\n\n pub private_call_requests: BoundedVec,\n pub public_call_requests: BoundedVec, MAX_ENQUEUED_CALLS_PER_CALL>,\n pub public_teardown_call_request: PublicCallRequest,\n pub l2_to_l1_msgs: BoundedVec,\n // docs:end:private-context\n\n // Header of a block whose state is used during private execution (not the block the transaction is included in).\n pub historical_header: BlockHeader,\n\n pub private_logs: BoundedVec,\n pub contract_class_logs_hashes: BoundedVec, MAX_CONTRACT_CLASS_LOGS_PER_CALL>,\n\n // Contains the last key validation request for each key type. This is used to cache the last request and avoid\n // fetching the same request multiple times.\n // The index of the array corresponds to the key type (0 nullifier, 1 incoming, 2 outgoing, 3 tagging).\n pub last_key_validation_requests: [Option; NUM_KEY_TYPES],\n}\n\nimpl PrivateContext {\n pub fn new(inputs: PrivateContextInputs, args_hash: Field) -> PrivateContext {\n PrivateContext {\n inputs,\n side_effect_counter: inputs.start_side_effect_counter + 1,\n min_revertible_side_effect_counter: 0,\n is_fee_payer: false,\n args_hash,\n return_hash: 0,\n max_block_number: MaxBlockNumber::empty(),\n note_hash_read_requests: BoundedVec::new(),\n nullifier_read_requests: BoundedVec::new(),\n key_validation_requests_and_generators: BoundedVec::new(),\n note_hashes: BoundedVec::new(),\n nullifiers: BoundedVec::new(),\n historical_header: inputs.historical_header,\n private_call_requests: BoundedVec::new(),\n public_call_requests: BoundedVec::new(),\n public_teardown_call_request: PublicCallRequest::empty(),\n l2_to_l1_msgs: BoundedVec::new(),\n private_logs: BoundedVec::new(),\n contract_class_logs_hashes: BoundedVec::new(),\n last_key_validation_requests: [Option::none(); NUM_KEY_TYPES],\n }\n }\n\n pub fn msg_sender(self) -> AztecAddress {\n self.inputs.call_context.msg_sender\n }\n\n pub fn this_address(self) -> AztecAddress {\n self.inputs.call_context.contract_address\n }\n\n pub fn chain_id(self) -> Field {\n self.inputs.tx_context.chain_id\n }\n\n pub fn version(self) -> Field {\n self.inputs.tx_context.version\n }\n\n pub fn gas_settings(self) -> GasSettings {\n self.inputs.tx_context.gas_settings\n }\n\n pub fn selector(self) -> FunctionSelector {\n self.inputs.call_context.function_selector\n }\n\n pub fn get_args_hash(self) -> Field {\n self.args_hash\n }\n\n pub fn push_note_hash(&mut self, note_hash: Field) {\n self.note_hashes.push(NoteHash { value: note_hash, counter: self.next_counter() });\n }\n\n pub fn push_nullifier(&mut self, nullifier: Field) {\n notify_created_nullifier(nullifier);\n self.nullifiers.push(\n Nullifier { value: nullifier, note_hash: 0, counter: self.next_counter() },\n );\n }\n\n pub fn push_nullifier_for_note_hash(&mut self, nullifier: Field, nullified_note_hash: Field) {\n let nullifier_counter = self.next_counter();\n notify_nullified_note(nullifier, nullified_note_hash, nullifier_counter);\n self.nullifiers.push(\n Nullifier {\n value: nullifier,\n note_hash: nullified_note_hash,\n counter: nullifier_counter,\n },\n );\n }\n\n // Returns the header of a block whose state is used during private execution (not the block the transaction is\n // included in).\n pub fn get_block_header(self) -> BlockHeader {\n self.historical_header\n }\n\n // Returns the header of an arbitrary block whose block number is less than or equal to the block number\n // of historical header.\n pub fn get_block_header_at(self, block_number: u32) -> BlockHeader {\n get_block_header_at(block_number, self)\n }\n\n pub fn set_return_hash(&mut self, returns_hasher: ArgsHasher) {\n self.return_hash = returns_hasher.hash();\n execution_cache::store(returns_hasher.fields, self.return_hash);\n }\n\n pub fn finish(self) -> PrivateCircuitPublicInputs {\n PrivateCircuitPublicInputs {\n call_context: self.inputs.call_context,\n args_hash: self.args_hash,\n returns_hash: self.return_hash,\n min_revertible_side_effect_counter: self.min_revertible_side_effect_counter,\n is_fee_payer: self.is_fee_payer,\n max_block_number: self.max_block_number,\n note_hash_read_requests: self.note_hash_read_requests.storage(),\n nullifier_read_requests: self.nullifier_read_requests.storage(),\n key_validation_requests_and_generators: self\n .key_validation_requests_and_generators\n .storage(),\n note_hashes: self.note_hashes.storage(),\n nullifiers: self.nullifiers.storage(),\n private_call_requests: self.private_call_requests.storage(),\n public_call_requests: self.public_call_requests.storage(),\n public_teardown_call_request: self.public_teardown_call_request,\n l2_to_l1_msgs: self.l2_to_l1_msgs.storage(),\n start_side_effect_counter: self.inputs.start_side_effect_counter,\n end_side_effect_counter: self.side_effect_counter,\n private_logs: self.private_logs.storage(),\n contract_class_logs_hashes: self.contract_class_logs_hashes.storage(),\n historical_header: self.historical_header,\n tx_context: self.inputs.tx_context,\n }\n }\n\n pub fn set_as_fee_payer(&mut self) {\n dep::protocol_types::debug_log::debug_log_format(\n \"Setting {0} as fee payer\",\n [self.this_address().to_field()],\n );\n self.is_fee_payer = true;\n }\n\n pub fn end_setup(&mut self) {\n // dep::protocol_types::debug_log::debug_log_format(\n // \"Ending setup at counter {0}\",\n // [self.side_effect_counter as Field]\n // );\n self.min_revertible_side_effect_counter = self.side_effect_counter;\n notify_set_min_revertible_side_effect_counter(self.min_revertible_side_effect_counter);\n }\n\n // docs:start:max-block-number\n pub fn set_tx_max_block_number(&mut self, max_block_number: u32) {\n // docs:end:max-block-number\n self.max_block_number =\n MaxBlockNumber::min_with_u32(self.max_block_number, max_block_number);\n }\n\n pub fn push_note_hash_read_request(&mut self, note_hash: Field) {\n let side_effect = ReadRequest { value: note_hash, counter: self.next_counter() };\n self.note_hash_read_requests.push(side_effect);\n }\n\n pub fn push_nullifier_read_request(&mut self, nullifier: Field) {\n let request = ReadRequest { value: nullifier, counter: self.next_counter() };\n self.nullifier_read_requests.push(request);\n }\n\n pub fn request_nsk_app(&mut self, npk_m_hash: Field) -> Field {\n self.request_sk_app(npk_m_hash, NULLIFIER_INDEX)\n }\n\n pub fn request_ovsk_app(&mut self, ovpk_m_hash: Field) -> Field {\n self.request_sk_app(ovpk_m_hash, OUTGOING_INDEX)\n }\n\n fn request_sk_app(&mut self, pk_m_hash: Field, key_index: Field) -> Field {\n let cached_request = self.last_key_validation_requests[key_index as u32].unwrap_or(\n KeyValidationRequest::empty(),\n );\n\n if cached_request.pk_m.hash() == pk_m_hash {\n // We get a match so the cached request is the latest one\n cached_request.sk_app\n } else {\n // We didn't get a match meaning the cached result is stale\n // Typically we'd validate keys by showing that they are the preimage of `pk_m_hash`, but that'd require\n // the oracle returning the master secret keys, which could cause malicious contracts to leak it or learn\n // about secrets from other contracts. We therefore silo secret keys, and rely on the private kernel to\n // validate that we siloed secret key corresponds to correct siloing of the master secret key that hashes\n // to `pk_m_hash`.\n\n // Safety: Kernels verify that the key validation request is valid and below we verify that a request\n // for the correct public key has been received.\n let request = unsafe { get_key_validation_request(pk_m_hash, key_index) };\n assert_eq(request.pk_m.hash(), pk_m_hash, \"Obtained invalid key validation request\");\n\n self.key_validation_requests_and_generators.push(\n KeyValidationRequestAndGenerator {\n request,\n sk_app_generator: sk_generators[key_index as u32],\n },\n );\n self.last_key_validation_requests[key_index as u32] = Option::some(request);\n request.sk_app\n }\n }\n\n // docs:start:context_message_portal\n pub fn message_portal(&mut self, recipient: EthAddress, content: Field) {\n // docs:end:context_message_portal\n let message = L2ToL1Message { recipient, content, counter: self.next_counter() };\n self.l2_to_l1_msgs.push(message);\n }\n\n // docs:start:context_consume_l1_to_l2_message\n // docs:start:consume_l1_to_l2_message\n pub fn consume_l1_to_l2_message(\n &mut self,\n content: Field,\n secret: Field,\n sender: EthAddress,\n leaf_index: Field,\n ) {\n // docs:end:context_consume_l1_to_l2_message\n let nullifier = process_l1_to_l2_message(\n self.historical_header.state.l1_to_l2_message_tree.root,\n self.this_address(),\n sender,\n self.chain_id(),\n self.version(),\n content,\n secret,\n leaf_index,\n );\n\n // Push nullifier (and the \"commitment\" corresponding to this can be \"empty\")\n self.push_nullifier(nullifier)\n }\n // docs:end:consume_l1_to_l2_message\n\n pub fn emit_private_log(&mut self, log: [Field; PRIVATE_LOG_SIZE_IN_FIELDS], length: u32) {\n let counter = self.next_counter();\n let private_log =\n PrivateLogData { log: PrivateLog::new(log, length), note_hash_counter: 0, counter };\n self.private_logs.push(private_log);\n }\n\n pub fn emit_raw_note_log(\n &mut self,\n log: [Field; PRIVATE_LOG_SIZE_IN_FIELDS],\n length: u32,\n note_hash_counter: u32,\n ) {\n let counter = self.next_counter();\n let private_log =\n PrivateLogData { log: PrivateLog::new(log, length), note_hash_counter, counter };\n self.private_logs.push(private_log);\n }\n\n pub fn call_private_function(\n &mut self,\n contract_address: AztecAddress,\n function_selector: FunctionSelector,\n args: [Field; ARGS_COUNT],\n ) -> ReturnsHash {\n let args_hash = hash_args_array(args);\n execution_cache::store(args, args_hash);\n self.call_private_function_with_args_hash(\n contract_address,\n function_selector,\n args_hash,\n false,\n )\n }\n\n pub fn static_call_private_function(\n &mut self,\n contract_address: AztecAddress,\n function_selector: FunctionSelector,\n args: [Field; ARGS_COUNT],\n ) -> ReturnsHash {\n let args_hash = hash_args_array(args);\n execution_cache::store(args, args_hash);\n self.call_private_function_with_args_hash(\n contract_address,\n function_selector,\n args_hash,\n true,\n )\n }\n\n pub fn call_private_function_no_args(\n &mut self,\n contract_address: AztecAddress,\n function_selector: FunctionSelector,\n ) -> ReturnsHash {\n self.call_private_function_with_args_hash(contract_address, function_selector, 0, false)\n }\n\n pub fn static_call_private_function_no_args(\n &mut self,\n contract_address: AztecAddress,\n function_selector: FunctionSelector,\n ) -> ReturnsHash {\n self.call_private_function_with_args_hash(contract_address, function_selector, 0, true)\n }\n\n pub fn call_private_function_with_args_hash(\n &mut self,\n contract_address: AztecAddress,\n function_selector: FunctionSelector,\n args_hash: Field,\n is_static_call: bool,\n ) -> ReturnsHash {\n let mut is_static_call = is_static_call | self.inputs.call_context.is_static_call;\n let start_side_effect_counter = self.side_effect_counter;\n\n // Safety: The oracle simulates the private call and returns the value of the side effects counter after\n // execution of the call (which means that end_side_effect_counter - start_side_effect_counter is\n // the number of side effects that took place), along with the hash of the return values. We validate these\n // by requesting a private kernel iteration in which the return values are constrained to hash\n // to `returns_hash` and the side effects counter to increment from start to end.\n let (end_side_effect_counter, returns_hash) = unsafe {\n call_private_function_internal(\n contract_address,\n function_selector,\n args_hash,\n start_side_effect_counter,\n is_static_call,\n )\n };\n\n self.private_call_requests.push(\n PrivateCallRequest {\n call_context: CallContext {\n msg_sender: self.this_address(),\n contract_address,\n function_selector,\n is_static_call,\n },\n args_hash,\n returns_hash,\n start_side_effect_counter,\n end_side_effect_counter,\n },\n );\n\n // TODO (fees) figure out why this crashes the prover and enable it\n // we need this in order to pay fees inside child call contexts\n // assert(\n // (item.public_inputs.min_revertible_side_effect_counter == 0 as u32)\n // | (item.public_inputs.min_revertible_side_effect_counter\n // > self.min_revertible_side_effect_counter)\n // );\n // if item.public_inputs.min_revertible_side_effect_counter\n // > self.min_revertible_side_effect_counter {\n // self.min_revertible_side_effect_counter = item.public_inputs.min_revertible_side_effect_counter;\n // }\n self.side_effect_counter = end_side_effect_counter + 1;\n ReturnsHash::new(returns_hash)\n }\n\n pub fn call_public_function(\n &mut self,\n contract_address: AztecAddress,\n function_selector: FunctionSelector,\n args: [Field; ARGS_COUNT],\n ) {\n let calldata = array_concat([function_selector.to_field()], args);\n let calldata_hash = hash_calldata_array(calldata);\n execution_cache::store(calldata, calldata_hash);\n self.call_public_function_with_calldata_hash(contract_address, calldata_hash, false)\n }\n\n pub fn static_call_public_function(\n &mut self,\n contract_address: AztecAddress,\n function_selector: FunctionSelector,\n args: [Field; ARGS_COUNT],\n ) {\n let calldata = array_concat([function_selector.to_field()], args);\n let calldata_hash = hash_calldata_array(calldata);\n execution_cache::store(calldata, calldata_hash);\n self.call_public_function_with_calldata_hash(contract_address, calldata_hash, true)\n }\n\n pub fn call_public_function_no_args(\n &mut self,\n contract_address: AztecAddress,\n function_selector: FunctionSelector,\n ) {\n let calldata_hash = hash_calldata_array([function_selector.to_field()]);\n self.call_public_function_with_calldata_hash(contract_address, calldata_hash, false)\n }\n\n pub fn static_call_public_function_no_args(\n &mut self,\n contract_address: AztecAddress,\n function_selector: FunctionSelector,\n ) {\n let calldata_hash = hash_calldata_array([function_selector.to_field()]);\n self.call_public_function_with_calldata_hash(contract_address, calldata_hash, true)\n }\n\n pub fn call_public_function_with_calldata_hash(\n &mut self,\n contract_address: AztecAddress,\n calldata_hash: Field,\n is_static_call: bool,\n ) {\n let counter = self.next_counter();\n\n let mut is_static_call = is_static_call | self.inputs.call_context.is_static_call;\n\n notify_enqueued_public_function_call(\n contract_address,\n calldata_hash,\n counter,\n is_static_call,\n );\n\n let call_request = PublicCallRequest {\n msg_sender: self.this_address(),\n contract_address,\n is_static_call,\n calldata_hash,\n };\n\n self.public_call_requests.push(Counted::new(call_request, counter));\n }\n\n pub fn set_public_teardown_function(\n &mut self,\n contract_address: AztecAddress,\n function_selector: FunctionSelector,\n args: [Field; ARGS_COUNT],\n ) {\n let calldata = array_concat([function_selector.to_field()], args);\n let calldata_hash = hash_calldata_array(calldata);\n execution_cache::store(calldata, calldata_hash);\n self.set_public_teardown_function_with_calldata_hash(contract_address, calldata_hash, false)\n }\n\n pub fn set_public_teardown_function_with_calldata_hash(\n &mut self,\n contract_address: AztecAddress,\n calldata_hash: Field,\n is_static_call: bool,\n ) {\n let counter = self.next_counter();\n\n let mut is_static_call = is_static_call | self.inputs.call_context.is_static_call;\n\n notify_set_public_teardown_function_call(\n contract_address,\n calldata_hash,\n counter,\n is_static_call,\n );\n\n self.public_teardown_call_request = PublicCallRequest {\n msg_sender: self.this_address(),\n contract_address,\n is_static_call,\n calldata_hash,\n };\n }\n\n fn next_counter(&mut self) -> u32 {\n let counter = self.side_effect_counter;\n self.side_effect_counter += 1;\n counter\n }\n}\n\nimpl Empty for PrivateContext {\n fn empty() -> Self {\n PrivateContext {\n inputs: PrivateContextInputs::empty(),\n side_effect_counter: 0 as u32,\n min_revertible_side_effect_counter: 0 as u32,\n is_fee_payer: false,\n args_hash: 0,\n return_hash: 0,\n max_block_number: MaxBlockNumber::empty(),\n note_hash_read_requests: BoundedVec::new(),\n nullifier_read_requests: BoundedVec::new(),\n key_validation_requests_and_generators: BoundedVec::new(),\n note_hashes: BoundedVec::new(),\n nullifiers: BoundedVec::new(),\n private_call_requests: BoundedVec::new(),\n public_call_requests: BoundedVec::new(),\n public_teardown_call_request: PublicCallRequest::empty(),\n l2_to_l1_msgs: BoundedVec::new(),\n historical_header: BlockHeader::empty(),\n private_logs: BoundedVec::new(),\n contract_class_logs_hashes: BoundedVec::new(),\n last_key_validation_requests: [Option::none(); NUM_KEY_TYPES],\n }\n }\n}\n" - }, - "6": { - "path": "std/collections/bounded_vec.nr", - "source": "use crate::{cmp::Eq, convert::From, runtime::is_unconstrained, static_assert};\n\n/// A `BoundedVec` is a growable storage similar to a `Vec` except that it\n/// is bounded with a maximum possible length. Unlike `Vec`, `BoundedVec` is not implemented\n/// via slices and thus is not subject to the same restrictions slices are (notably, nested\n/// slices - and thus nested vectors as well - are disallowed).\n///\n/// Since a BoundedVec is backed by a normal array under the hood, growing the BoundedVec by\n/// pushing an additional element is also more efficient - the length only needs to be increased\n/// by one.\n///\n/// For these reasons `BoundedVec` should generally be preferred over `Vec` when there\n/// is a reasonable maximum bound that can be placed on the vector.\n///\n/// Example:\n///\n/// ```noir\n/// let mut vector: BoundedVec = BoundedVec::new();\n/// for i in 0..5 {\n/// vector.push(i);\n/// }\n/// assert(vector.len() == 5);\n/// assert(vector.max_len() == 10);\n/// ```\npub struct BoundedVec {\n storage: [T; MaxLen],\n len: u32,\n}\n\nimpl BoundedVec {\n /// Creates a new, empty vector of length zero.\n ///\n /// Since this container is backed by an array internally, it still needs an initial value\n /// to give each element. To resolve this, each element is zeroed internally. This value\n /// is guaranteed to be inaccessible unless `get_unchecked` is used.\n ///\n /// Example:\n ///\n /// ```noir\n /// let empty_vector: BoundedVec = BoundedVec::new();\n /// assert(empty_vector.len() == 0);\n /// ```\n ///\n /// Note that whenever calling `new` the maximum length of the vector should always be specified\n /// via a type signature:\n ///\n /// ```noir\n /// fn good() -> BoundedVec {\n /// // Ok! MaxLen is specified with a type annotation\n /// let v1: BoundedVec = BoundedVec::new();\n /// let v2 = BoundedVec::new();\n ///\n /// // Ok! MaxLen is known from the type of `good`'s return value\n /// v2\n /// }\n ///\n /// fn bad() {\n /// // Error: Type annotation needed\n /// // The compiler can't infer `MaxLen` from the following code:\n /// let mut v3 = BoundedVec::new();\n /// v3.push(5);\n /// }\n /// ```\n ///\n /// This defaulting of `MaxLen` (and numeric generics in general) to zero may change in future noir versions\n /// but for now make sure to use type annotations when using bounded vectors. Otherwise, you will receive a\n /// constraint failure at runtime when the vec is pushed to.\n pub fn new() -> Self {\n let zeroed = crate::mem::zeroed();\n BoundedVec { storage: [zeroed; MaxLen], len: 0 }\n }\n\n /// Retrieves an element from the vector at the given index, starting from zero.\n ///\n /// If the given index is equal to or greater than the length of the vector, this\n /// will issue a constraint failure.\n ///\n /// Example:\n ///\n /// ```noir\n /// fn foo(v: BoundedVec) {\n /// let first = v.get(0);\n /// let last = v.get(v.len() - 1);\n /// assert(first != last);\n /// }\n /// ```\n pub fn get(self, index: u32) -> T {\n assert(index < self.len, \"Attempted to read past end of BoundedVec\");\n self.get_unchecked(index)\n }\n\n /// Retrieves an element from the vector at the given index, starting from zero, without\n /// performing a bounds check.\n ///\n /// Since this function does not perform a bounds check on length before accessing the element,\n /// it is unsafe! Use at your own risk!\n ///\n /// Example:\n ///\n /// ```noir\n /// fn sum_of_first_three(v: BoundedVec) -> u32 {\n /// // Always ensure the length is larger than the largest\n /// // index passed to get_unchecked\n /// assert(v.len() > 2);\n /// let first = v.get_unchecked(0);\n /// let second = v.get_unchecked(1);\n /// let third = v.get_unchecked(2);\n /// first + second + third\n /// }\n /// ```\n pub fn get_unchecked(self, index: u32) -> T {\n self.storage[index]\n }\n\n /// Writes an element to the vector at the given index, starting from zero.\n ///\n /// If the given index is equal to or greater than the length of the vector, this will issue a constraint failure.\n ///\n /// Example:\n ///\n /// ```noir\n /// fn foo(v: BoundedVec) {\n /// let first = v.get(0);\n /// assert(first != 42);\n /// v.set(0, 42);\n /// let new_first = v.get(0);\n /// assert(new_first == 42);\n /// }\n /// ```\n pub fn set(&mut self, index: u32, value: T) {\n assert(index < self.len, \"Attempted to write past end of BoundedVec\");\n self.set_unchecked(index, value)\n }\n\n /// Writes an element to the vector at the given index, starting from zero, without performing a bounds check.\n ///\n /// Since this function does not perform a bounds check on length before accessing the element, it is unsafe! Use at your own risk!\n ///\n /// Example:\n ///\n /// ```noir\n /// fn set_unchecked_example() {\n /// let mut vec: BoundedVec = BoundedVec::new();\n /// vec.extend_from_array([1, 2]);\n ///\n /// // Here we're safely writing within the valid range of `vec`\n /// // `vec` now has the value [42, 2]\n /// vec.set_unchecked(0, 42);\n ///\n /// // We can then safely read this value back out of `vec`.\n /// // Notice that we use the checked version of `get` which would prevent reading unsafe values.\n /// assert_eq(vec.get(0), 42);\n ///\n /// // We've now written past the end of `vec`.\n /// // As this index is still within the maximum potential length of `v`,\n /// // it won't cause a constraint failure.\n /// vec.set_unchecked(2, 42);\n /// println(vec);\n ///\n /// // This will write past the end of the maximum potential length of `vec`,\n /// // it will then trigger a constraint failure.\n /// vec.set_unchecked(5, 42);\n /// println(vec);\n /// }\n /// ```\n pub fn set_unchecked(&mut self, index: u32, value: T) {\n self.storage[index] = value;\n }\n\n /// Pushes an element to the end of the vector. This increases the length\n /// of the vector by one.\n ///\n /// Panics if the new length of the vector will be greater than the max length.\n ///\n /// Example:\n ///\n /// ```noir\n /// let mut v: BoundedVec = BoundedVec::new();\n ///\n /// v.push(1);\n /// v.push(2);\n ///\n /// // Panics with failed assertion \"push out of bounds\"\n /// v.push(3);\n /// ```\n pub fn push(&mut self, elem: T) {\n assert(self.len < MaxLen, \"push out of bounds\");\n\n self.storage[self.len] = elem;\n self.len += 1;\n }\n\n /// Returns the current length of this vector\n ///\n /// Example:\n ///\n /// ```noir\n /// let mut v: BoundedVec = BoundedVec::new();\n /// assert(v.len() == 0);\n ///\n /// v.push(100);\n /// assert(v.len() == 1);\n ///\n /// v.push(200);\n /// v.push(300);\n /// v.push(400);\n /// assert(v.len() == 4);\n ///\n /// let _ = v.pop();\n /// let _ = v.pop();\n /// assert(v.len() == 2);\n /// ```\n pub fn len(self) -> u32 {\n self.len\n }\n\n /// Returns the maximum length of this vector. This is always\n /// equal to the `MaxLen` parameter this vector was initialized with.\n ///\n /// Example:\n ///\n /// ```noir\n /// let mut v: BoundedVec = BoundedVec::new();\n ///\n /// assert(v.max_len() == 5);\n /// v.push(10);\n /// assert(v.max_len() == 5);\n /// ```\n pub fn max_len(_self: BoundedVec) -> u32 {\n MaxLen\n }\n\n /// Returns the internal array within this vector.\n ///\n /// Since arrays in Noir are immutable, mutating the returned storage array will not mutate\n /// the storage held internally by this vector.\n ///\n /// Note that uninitialized elements may be zeroed out!\n ///\n /// Example:\n ///\n /// ```noir\n /// let mut v: BoundedVec = BoundedVec::new();\n ///\n /// assert(v.storage() == [0, 0, 0, 0, 0]);\n ///\n /// v.push(57);\n /// assert(v.storage() == [57, 0, 0, 0, 0]);\n /// ```\n pub fn storage(self) -> [T; MaxLen] {\n self.storage\n }\n\n /// Pushes each element from the given array to this vector.\n ///\n /// Panics if pushing each element would cause the length of this vector\n /// to exceed the maximum length.\n ///\n /// Example:\n ///\n /// ```noir\n /// let mut vec: BoundedVec = BoundedVec::new();\n /// vec.extend_from_array([2, 4]);\n ///\n /// assert(vec.len == 2);\n /// assert(vec.get(0) == 2);\n /// assert(vec.get(1) == 4);\n /// ```\n pub fn extend_from_array(&mut self, array: [T; Len]) {\n let new_len = self.len + array.len();\n assert(new_len <= MaxLen, \"extend_from_array out of bounds\");\n for i in 0..array.len() {\n self.storage[self.len + i] = array[i];\n }\n self.len = new_len;\n }\n\n /// Pushes each element from the given slice to this vector.\n ///\n /// Panics if pushing each element would cause the length of this vector\n /// to exceed the maximum length.\n ///\n /// Example:\n ///\n /// ```noir\n /// let mut vec: BoundedVec = BoundedVec::new();\n /// vec.extend_from_slice(&[2, 4]);\n ///\n /// assert(vec.len == 2);\n /// assert(vec.get(0) == 2);\n /// assert(vec.get(1) == 4);\n /// ```\n pub fn extend_from_slice(&mut self, slice: [T]) {\n let new_len = self.len + slice.len();\n assert(new_len <= MaxLen, \"extend_from_slice out of bounds\");\n for i in 0..slice.len() {\n self.storage[self.len + i] = slice[i];\n }\n self.len = new_len;\n }\n\n /// Pushes each element from the other vector to this vector. The length of\n /// the other vector is left unchanged.\n ///\n /// Panics if pushing each element would cause the length of this vector\n /// to exceed the maximum length.\n ///\n /// ```noir\n /// let mut v1: BoundedVec = BoundedVec::new();\n /// let mut v2: BoundedVec = BoundedVec::new();\n ///\n /// v2.extend_from_array([1, 2, 3]);\n /// v1.extend_from_bounded_vec(v2);\n ///\n /// assert(v1.storage() == [1, 2, 3, 0, 0]);\n /// assert(v2.storage() == [1, 2, 3, 0, 0, 0, 0]);\n /// ```\n pub fn extend_from_bounded_vec(&mut self, vec: BoundedVec) {\n let append_len = vec.len();\n let new_len = self.len + append_len;\n assert(new_len <= MaxLen, \"extend_from_bounded_vec out of bounds\");\n\n if is_unconstrained() {\n for i in 0..append_len {\n self.storage[self.len + i] = vec.get_unchecked(i);\n }\n } else {\n let mut exceeded_len = false;\n for i in 0..Len {\n exceeded_len |= i == append_len;\n if !exceeded_len {\n self.storage[self.len + i] = vec.get_unchecked(i);\n }\n }\n }\n self.len = new_len;\n }\n\n /// Creates a new vector, populating it with values derived from an array input.\n /// The maximum length of the vector is determined based on the type signature.\n ///\n /// Example:\n ///\n /// ```noir\n /// let bounded_vec: BoundedVec = BoundedVec::from_array([1, 2, 3])\n /// ```\n pub fn from_array(array: [T; Len]) -> Self {\n static_assert(Len <= MaxLen, \"from array out of bounds\");\n let mut vec: BoundedVec = BoundedVec::new();\n vec.extend_from_array(array);\n vec\n }\n\n /// Pops the element at the end of the vector. This will decrease the length\n /// of the vector by one.\n ///\n /// Panics if the vector is empty.\n ///\n /// Example:\n ///\n /// ```noir\n /// let mut v: BoundedVec = BoundedVec::new();\n /// v.push(1);\n /// v.push(2);\n ///\n /// let two = v.pop();\n /// let one = v.pop();\n ///\n /// assert(two == 2);\n /// assert(one == 1);\n ///\n /// // error: cannot pop from an empty vector\n /// let _ = v.pop();\n /// ```\n pub fn pop(&mut self) -> T {\n assert(self.len > 0);\n self.len -= 1;\n\n let elem = self.storage[self.len];\n self.storage[self.len] = crate::mem::zeroed();\n elem\n }\n\n /// Returns true if the given predicate returns true for any element\n /// in this vector.\n ///\n /// Example:\n ///\n /// ```noir\n /// let mut v: BoundedVec = BoundedVec::new();\n /// v.extend_from_array([2, 4, 6]);\n ///\n /// let all_even = !v.any(|elem: u32| elem % 2 != 0);\n /// assert(all_even);\n /// ```\n pub fn any(self, predicate: fn[Env](T) -> bool) -> bool {\n let mut ret = false;\n if is_unconstrained() {\n for i in 0..self.len {\n ret |= predicate(self.storage[i]);\n }\n } else {\n let mut ret = false;\n let mut exceeded_len = false;\n for i in 0..MaxLen {\n exceeded_len |= i == self.len;\n if !exceeded_len {\n ret |= predicate(self.storage[i]);\n }\n }\n }\n ret\n }\n\n /// Creates a new vector of equal size by calling a closure on each element in this vector.\n ///\n /// Example:\n ///\n /// ```noir\n /// let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]);\n /// let result = vec.map(|value| value * 2);\n ///\n /// let expected = BoundedVec::from_array([2, 4, 6, 8]);\n /// assert_eq(result, expected);\n /// ```\n pub fn map(self, f: fn[Env](T) -> U) -> BoundedVec {\n let mut ret = BoundedVec::new();\n ret.len = self.len();\n\n if is_unconstrained() {\n for i in 0..self.len() {\n ret.storage[i] = f(self.get_unchecked(i));\n }\n } else {\n for i in 0..MaxLen {\n if i < self.len() {\n ret.storage[i] = f(self.get_unchecked(i));\n }\n }\n }\n\n ret\n }\n\n /// Creates a new vector of equal size by calling a closure on each element\n /// in this vector, along with its index.\n ///\n /// Example:\n ///\n /// ```noir\n /// let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]);\n /// let result = vec.mapi(|i, value| i + value * 2);\n ///\n /// let expected = BoundedVec::from_array([2, 5, 8, 11]);\n /// assert_eq(result, expected);\n /// ```\n pub fn mapi(self, f: fn[Env](u32, T) -> U) -> BoundedVec {\n let mut ret = BoundedVec::new();\n ret.len = self.len();\n\n if is_unconstrained() {\n for i in 0..self.len() {\n ret.storage[i] = f(i, self.get_unchecked(i));\n }\n } else {\n for i in 0..MaxLen {\n if i < self.len() {\n ret.storage[i] = f(i, self.get_unchecked(i));\n }\n }\n }\n\n ret\n }\n\n /// Calls a closure on each element in this vector.\n ///\n /// Example:\n ///\n /// ```noir\n /// let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]);\n /// let mut result = BoundedVec::::new();\n /// vec.for_each(|value| result.push(value * 2));\n ///\n /// let expected = BoundedVec::from_array([2, 4, 6, 8]);\n /// assert_eq(result, expected);\n /// ```\n pub fn for_each(self, f: fn[Env](T) -> ()) {\n if is_unconstrained() {\n for i in 0..self.len() {\n f(self.get_unchecked(i));\n }\n } else {\n for i in 0..MaxLen {\n if i < self.len() {\n f(self.get_unchecked(i));\n }\n }\n }\n }\n\n /// Calls a closure on each element in this vector, along with its index.\n ///\n /// Example:\n ///\n /// ```noir\n /// let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]);\n /// let mut result = BoundedVec::::new();\n /// vec.for_eachi(|i, value| result.push(i + value * 2));\n ///\n /// let expected = BoundedVec::from_array([2, 5, 8, 11]);\n /// assert_eq(result, expected);\n /// ```\n pub fn for_eachi(self, f: fn[Env](u32, T) -> ()) {\n if is_unconstrained() {\n for i in 0..self.len() {\n f(i, self.get_unchecked(i));\n }\n } else {\n for i in 0..MaxLen {\n if i < self.len() {\n f(i, self.get_unchecked(i));\n }\n }\n }\n }\n\n /// Creates a new BoundedVec from the given array and length.\n /// The given length must be less than or equal to the length of the array.\n ///\n /// This function will zero out any elements at or past index `len` of `array`.\n /// This incurs an extra runtime cost of O(MaxLen). If you are sure your array is\n /// zeroed after that index, you can use `from_parts_unchecked` to remove the extra loop.\n ///\n /// Example:\n ///\n /// ```noir\n /// let vec: BoundedVec = BoundedVec::from_parts([1, 2, 3, 0], 3);\n /// assert_eq(vec.len(), 3);\n /// ```\n pub fn from_parts(mut array: [T; MaxLen], len: u32) -> Self {\n assert(len <= MaxLen);\n let zeroed = crate::mem::zeroed();\n\n if is_unconstrained() {\n for i in len..MaxLen {\n array[i] = zeroed;\n }\n } else {\n for i in 0..MaxLen {\n if i >= len {\n array[i] = zeroed;\n }\n }\n }\n\n BoundedVec { storage: array, len }\n }\n\n /// Creates a new BoundedVec from the given array and length.\n /// The given length must be less than or equal to the length of the array.\n ///\n /// This function is unsafe because it expects all elements past the `len` index\n /// of `array` to be zeroed, but does not check for this internally. Use `from_parts`\n /// for a safe version of this function which does zero out any indices past the\n /// given length. Invalidating this assumption can notably cause `BoundedVec::eq`\n /// to give incorrect results since it will check even elements past `len`.\n ///\n /// Example:\n ///\n /// ```noir\n /// let vec: BoundedVec = BoundedVec::from_parts_unchecked([1, 2, 3, 0], 3);\n /// assert_eq(vec.len(), 3);\n ///\n /// // invalid use!\n /// let vec1: BoundedVec = BoundedVec::from_parts_unchecked([1, 2, 3, 1], 3);\n /// let vec2: BoundedVec = BoundedVec::from_parts_unchecked([1, 2, 3, 2], 3);\n ///\n /// // both vecs have length 3 so we'd expect them to be equal, but this\n /// // fails because elements past the length are still checked in eq\n /// assert_eq(vec1, vec2); // fails\n /// ```\n pub fn from_parts_unchecked(array: [T; MaxLen], len: u32) -> Self {\n assert(len <= MaxLen);\n BoundedVec { storage: array, len }\n }\n}\n\nimpl Eq for BoundedVec\nwhere\n T: Eq,\n{\n fn eq(self, other: BoundedVec) -> bool {\n // TODO: https://github.com/noir-lang/noir/issues/4837\n //\n // We make the assumption that the user has used the proper interface for working with `BoundedVec`s\n // rather than directly manipulating the internal fields as this can result in an inconsistent internal state.\n if self.len == other.len {\n self.storage == other.storage\n } else {\n false\n }\n }\n}\n\nimpl From<[T; Len]> for BoundedVec {\n fn from(array: [T; Len]) -> BoundedVec {\n BoundedVec::from_array(array)\n }\n}\n\nmod bounded_vec_tests {\n\n mod get {\n use crate::collections::bounded_vec::BoundedVec;\n\n #[test(should_fail_with = \"Attempted to read past end of BoundedVec\")]\n fn panics_when_reading_elements_past_end_of_vec() {\n let vec: BoundedVec = BoundedVec::new();\n\n crate::println(vec.get(0));\n }\n }\n\n mod set {\n use crate::collections::bounded_vec::BoundedVec;\n\n #[test]\n fn set_updates_values_properly() {\n let mut vec = BoundedVec::from_array([0, 0, 0, 0, 0]);\n\n vec.set(0, 42);\n assert_eq(vec.storage, [42, 0, 0, 0, 0]);\n\n vec.set(1, 43);\n assert_eq(vec.storage, [42, 43, 0, 0, 0]);\n\n vec.set(2, 44);\n assert_eq(vec.storage, [42, 43, 44, 0, 0]);\n\n vec.set(1, 10);\n assert_eq(vec.storage, [42, 10, 44, 0, 0]);\n\n vec.set(0, 0);\n assert_eq(vec.storage, [0, 10, 44, 0, 0]);\n }\n\n #[test(should_fail_with = \"Attempted to write past end of BoundedVec\")]\n fn panics_when_writing_elements_past_end_of_vec() {\n let mut vec: BoundedVec = BoundedVec::new();\n vec.set(0, 42);\n\n // Need to use println to avoid DIE removing the write operation.\n crate::println(vec.get(0));\n }\n }\n\n mod map {\n use crate::collections::bounded_vec::BoundedVec;\n\n #[test]\n fn applies_function_correctly() {\n // docs:start:bounded-vec-map-example\n let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]);\n let result = vec.map(|value| value * 2);\n // docs:end:bounded-vec-map-example\n let expected = BoundedVec::from_array([2, 4, 6, 8]);\n\n assert_eq(result, expected);\n }\n\n #[test]\n fn applies_function_that_changes_return_type() {\n let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]);\n let result = vec.map(|value| (value * 2) as Field);\n let expected: BoundedVec = BoundedVec::from_array([2, 4, 6, 8]);\n\n assert_eq(result, expected);\n }\n\n #[test]\n fn does_not_apply_function_past_len() {\n let vec: BoundedVec = BoundedVec::from_array([0, 1]);\n let result = vec.map(|value| if value == 0 { 5 } else { value });\n let expected = BoundedVec::from_array([5, 1]);\n\n assert_eq(result, expected);\n assert_eq(result.get_unchecked(2), 0);\n }\n }\n\n mod mapi {\n use crate::collections::bounded_vec::BoundedVec;\n\n #[test]\n fn applies_function_correctly() {\n // docs:start:bounded-vec-mapi-example\n let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]);\n let result = vec.mapi(|i, value| i + value * 2);\n // docs:end:bounded-vec-mapi-example\n let expected = BoundedVec::from_array([2, 5, 8, 11]);\n\n assert_eq(result, expected);\n }\n\n #[test]\n fn applies_function_that_changes_return_type() {\n let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]);\n let result = vec.mapi(|i, value| (i + value * 2) as Field);\n let expected: BoundedVec = BoundedVec::from_array([2, 5, 8, 11]);\n\n assert_eq(result, expected);\n }\n\n #[test]\n fn does_not_apply_function_past_len() {\n let vec: BoundedVec = BoundedVec::from_array([0, 1]);\n let result = vec.mapi(|_, value| if value == 0 { 5 } else { value });\n let expected = BoundedVec::from_array([5, 1]);\n\n assert_eq(result, expected);\n assert_eq(result.get_unchecked(2), 0);\n }\n }\n\n mod for_each {\n use crate::collections::bounded_vec::BoundedVec;\n\n // map in terms of for_each\n fn for_each_map(\n input: BoundedVec,\n f: fn[Env](T) -> U,\n ) -> BoundedVec {\n let mut output = BoundedVec::::new();\n let output_ref = &mut output;\n input.for_each(|x| output_ref.push(f(x)));\n output\n }\n\n #[test]\n fn smoke_test() {\n let mut acc = 0;\n let acc_ref = &mut acc;\n // docs:start:bounded-vec-for-each-example\n let vec: BoundedVec = BoundedVec::from_array([1, 2, 3]);\n vec.for_each(|value| { *acc_ref += value; });\n // docs:end:bounded-vec-for-each-example\n assert_eq(acc, 6);\n }\n\n #[test]\n fn applies_function_correctly() {\n let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]);\n let result = for_each_map(vec, |value| value * 2);\n let expected = BoundedVec::from_array([2, 4, 6, 8]);\n\n assert_eq(result, expected);\n }\n\n #[test]\n fn applies_function_that_changes_return_type() {\n let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]);\n let result = for_each_map(vec, |value| (value * 2) as Field);\n let expected: BoundedVec = BoundedVec::from_array([2, 4, 6, 8]);\n\n assert_eq(result, expected);\n }\n\n #[test]\n fn does_not_apply_function_past_len() {\n let vec: BoundedVec = BoundedVec::from_array([0, 1]);\n let result = for_each_map(vec, |value| if value == 0 { 5 } else { value });\n let expected = BoundedVec::from_array([5, 1]);\n\n assert_eq(result, expected);\n assert_eq(result.get_unchecked(2), 0);\n }\n }\n\n mod for_eachi {\n use crate::collections::bounded_vec::BoundedVec;\n\n // mapi in terms of for_eachi\n fn for_eachi_mapi(\n input: BoundedVec,\n f: fn[Env](u32, T) -> U,\n ) -> BoundedVec {\n let mut output = BoundedVec::::new();\n let output_ref = &mut output;\n input.for_eachi(|i, x| output_ref.push(f(i, x)));\n output\n }\n\n #[test]\n fn smoke_test() {\n let mut acc = 0;\n let acc_ref = &mut acc;\n // docs:start:bounded-vec-for-eachi-example\n let vec: BoundedVec = BoundedVec::from_array([1, 2, 3]);\n vec.for_eachi(|i, value| { *acc_ref += i * value; });\n // docs:end:bounded-vec-for-eachi-example\n\n // 0 * 1 + 1 * 2 + 2 * 3\n assert_eq(acc, 8);\n }\n\n #[test]\n fn applies_function_correctly() {\n let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]);\n let result = for_eachi_mapi(vec, |i, value| i + value * 2);\n let expected = BoundedVec::from_array([2, 5, 8, 11]);\n\n assert_eq(result, expected);\n }\n\n #[test]\n fn applies_function_that_changes_return_type() {\n let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]);\n let result = for_eachi_mapi(vec, |i, value| (i + value * 2) as Field);\n let expected: BoundedVec = BoundedVec::from_array([2, 5, 8, 11]);\n\n assert_eq(result, expected);\n }\n\n #[test]\n fn does_not_apply_function_past_len() {\n let vec: BoundedVec = BoundedVec::from_array([0, 1]);\n let result = for_eachi_mapi(vec, |_, value| if value == 0 { 5 } else { value });\n let expected = BoundedVec::from_array([5, 1]);\n\n assert_eq(result, expected);\n assert_eq(result.get_unchecked(2), 0);\n }\n }\n\n mod from_array {\n use crate::collections::bounded_vec::BoundedVec;\n\n #[test]\n fn empty() {\n let empty_array: [Field; 0] = [];\n let bounded_vec = BoundedVec::from_array([]);\n\n assert_eq(bounded_vec.max_len(), 0);\n assert_eq(bounded_vec.len(), 0);\n assert_eq(bounded_vec.storage(), empty_array);\n }\n\n #[test]\n fn equal_len() {\n let array = [1, 2, 3];\n let bounded_vec = BoundedVec::from_array(array);\n\n assert_eq(bounded_vec.max_len(), 3);\n assert_eq(bounded_vec.len(), 3);\n assert_eq(bounded_vec.storage(), array);\n }\n\n #[test]\n fn max_len_greater_then_array_len() {\n let array = [1, 2, 3];\n let bounded_vec: BoundedVec = BoundedVec::from_array(array);\n\n assert_eq(bounded_vec.max_len(), 10);\n assert_eq(bounded_vec.len(), 3);\n assert_eq(bounded_vec.get(0), 1);\n assert_eq(bounded_vec.get(1), 2);\n assert_eq(bounded_vec.get(2), 3);\n }\n\n #[test(should_fail_with = \"from array out of bounds\")]\n fn max_len_lower_then_array_len() {\n let _: BoundedVec = BoundedVec::from_array([0; 3]);\n }\n }\n\n mod trait_from {\n use crate::collections::bounded_vec::BoundedVec;\n use crate::convert::From;\n\n #[test]\n fn simple() {\n let array = [1, 2];\n let bounded_vec: BoundedVec = BoundedVec::from(array);\n\n assert_eq(bounded_vec.max_len(), 10);\n assert_eq(bounded_vec.len(), 2);\n assert_eq(bounded_vec.get(0), 1);\n assert_eq(bounded_vec.get(1), 2);\n }\n }\n\n mod trait_eq {\n use crate::collections::bounded_vec::BoundedVec;\n\n #[test]\n fn empty_equality() {\n let mut bounded_vec1: BoundedVec = BoundedVec::new();\n let mut bounded_vec2: BoundedVec = BoundedVec::new();\n\n assert_eq(bounded_vec1, bounded_vec2);\n }\n\n #[test]\n fn inequality() {\n let mut bounded_vec1: BoundedVec = BoundedVec::new();\n let mut bounded_vec2: BoundedVec = BoundedVec::new();\n bounded_vec1.push(1);\n bounded_vec2.push(2);\n\n assert(bounded_vec1 != bounded_vec2);\n }\n }\n\n mod from_parts {\n use crate::collections::bounded_vec::BoundedVec;\n\n #[test]\n fn from_parts() {\n // docs:start:from-parts\n let vec: BoundedVec = BoundedVec::from_parts([1, 2, 3, 0], 3);\n assert_eq(vec.len(), 3);\n\n // Any elements past the given length are zeroed out, so these\n // two BoundedVecs will be completely equal\n let vec1: BoundedVec = BoundedVec::from_parts([1, 2, 3, 1], 3);\n let vec2: BoundedVec = BoundedVec::from_parts([1, 2, 3, 2], 3);\n assert_eq(vec1, vec2);\n // docs:end:from-parts\n }\n\n #[test]\n fn from_parts_unchecked() {\n // docs:start:from-parts-unchecked\n let vec: BoundedVec = BoundedVec::from_parts_unchecked([1, 2, 3, 0], 3);\n assert_eq(vec.len(), 3);\n\n // invalid use!\n let vec1: BoundedVec = BoundedVec::from_parts_unchecked([1, 2, 3, 1], 3);\n let vec2: BoundedVec = BoundedVec::from_parts_unchecked([1, 2, 3, 2], 3);\n\n // both vecs have length 3 so we'd expect them to be equal, but this\n // fails because elements past the length are still checked in eq\n assert(vec1 != vec2);\n // docs:end:from-parts-unchecked\n }\n }\n}\n" - }, - "60": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/context/public_context.nr", - "source": "use crate::context::gas::GasOpts;\nuse crate::hash::{\n compute_l1_to_l2_message_hash, compute_l1_to_l2_message_nullifier, compute_secret_hash,\n};\nuse dep::protocol_types::abis::function_selector::FunctionSelector;\nuse dep::protocol_types::address::{AztecAddress, EthAddress};\nuse dep::protocol_types::constants::MAX_FIELD_VALUE;\nuse dep::protocol_types::traits::{Empty, FromField, Packable, Serialize, ToField};\n\npub struct PublicContext {\n pub args_hash: Option,\n pub compute_args_hash: fn() -> Field,\n}\n\nimpl PublicContext {\n pub fn new(compute_args_hash: fn() -> Field) -> Self {\n PublicContext { args_hash: Option::none(), compute_args_hash }\n }\n\n pub fn emit_public_log(_self: &mut Self, log: T)\n where\n T: Serialize,\n {\n // Safety: AVM opcodes are constrained by the AVM itself\n unsafe { emit_public_log(Serialize::serialize(log).as_slice()) };\n }\n\n pub fn note_hash_exists(_self: Self, note_hash: Field, leaf_index: Field) -> bool {\n // Safety: AVM opcodes are constrained by the AVM itself\n unsafe { note_hash_exists(note_hash, leaf_index) } == 1\n }\n\n pub fn l1_to_l2_msg_exists(_self: Self, msg_hash: Field, msg_leaf_index: Field) -> bool {\n // Safety: AVM opcodes are constrained by the AVM itself\n unsafe { l1_to_l2_msg_exists(msg_hash, msg_leaf_index) } == 1\n }\n\n pub fn nullifier_exists(_self: Self, unsiloed_nullifier: Field, address: AztecAddress) -> bool {\n // Safety: AVM opcodes are constrained by the AVM itself\n unsafe { nullifier_exists(unsiloed_nullifier, address.to_field()) } == 1\n }\n\n pub fn consume_l1_to_l2_message(\n &mut self,\n content: Field,\n secret: Field,\n sender: EthAddress,\n leaf_index: Field,\n ) {\n let secret_hash = compute_secret_hash(secret);\n let message_hash = compute_l1_to_l2_message_hash(\n sender,\n self.chain_id(),\n /*recipient=*/\n self.this_address(),\n self.version(),\n content,\n secret_hash,\n leaf_index,\n );\n let nullifier = compute_l1_to_l2_message_nullifier(message_hash, secret);\n\n assert(\n !self.nullifier_exists(nullifier, self.this_address()),\n \"L1-to-L2 message is already nullified\",\n );\n assert(\n self.l1_to_l2_msg_exists(message_hash, leaf_index),\n \"Tried to consume nonexistent L1-to-L2 message\",\n );\n\n self.push_nullifier(nullifier);\n }\n\n pub fn message_portal(_self: &mut Self, recipient: EthAddress, content: Field) {\n // Safety: AVM opcodes are constrained by the AVM itself\n unsafe { send_l2_to_l1_msg(recipient, content) };\n }\n\n pub unconstrained fn call_public_function(\n _self: &mut Self,\n contract_address: AztecAddress,\n function_selector: FunctionSelector,\n args: [Field],\n gas_opts: GasOpts,\n ) -> [Field] {\n let calldata = args.push_front(function_selector.to_field());\n\n call(\n gas_opts.l2_gas.unwrap_or(MAX_FIELD_VALUE),\n gas_opts.da_gas.unwrap_or(MAX_FIELD_VALUE),\n contract_address,\n calldata,\n );\n // Use success_copy to determine whether the call succeeded\n let success = success_copy();\n\n let result_data = returndata_copy(0, returndata_size());\n if !success {\n // Rethrow the revert data.\n avm_revert(result_data);\n }\n result_data\n }\n\n pub unconstrained fn static_call_public_function(\n _self: &mut Self,\n contract_address: AztecAddress,\n function_selector: FunctionSelector,\n args: [Field],\n gas_opts: GasOpts,\n ) -> [Field] {\n let calldata = args.push_front(function_selector.to_field());\n\n call_static(\n gas_opts.l2_gas.unwrap_or(MAX_FIELD_VALUE),\n gas_opts.da_gas.unwrap_or(MAX_FIELD_VALUE),\n contract_address,\n calldata,\n );\n // Use success_copy to determine whether the call succeeded\n let success = success_copy();\n\n let result_data = returndata_copy(0, returndata_size());\n if !success {\n // Rethrow the revert data.\n avm_revert(result_data);\n }\n result_data\n }\n\n pub fn push_note_hash(_self: &mut Self, note_hash: Field) {\n // Safety: AVM opcodes are constrained by the AVM itself\n unsafe { emit_note_hash(note_hash) };\n }\n pub fn push_nullifier(_self: &mut Self, nullifier: Field) {\n // Safety: AVM opcodes are constrained by the AVM itself\n unsafe { emit_nullifier(nullifier) };\n }\n\n pub fn this_address(_self: Self) -> AztecAddress {\n // Safety: AVM opcodes are constrained by the AVM itself\n unsafe {\n address()\n }\n }\n pub fn msg_sender(_self: Self) -> AztecAddress {\n // Safety: AVM opcodes are constrained by the AVM itself\n unsafe {\n sender()\n }\n }\n pub fn selector(_self: Self) -> FunctionSelector {\n // The selector is the first element of the calldata when calling a public function through dispatch.\n // Safety: AVM opcodes are constrained by the AVM itself\n let raw_selector: [Field; 1] = unsafe { calldata_copy(0, 1) };\n FunctionSelector::from_field(raw_selector[0])\n }\n pub fn get_args_hash(mut self) -> Field {\n if !self.args_hash.is_some() {\n self.args_hash = Option::some((self.compute_args_hash)());\n }\n\n self.args_hash.unwrap_unchecked()\n }\n pub fn transaction_fee(_self: Self) -> Field {\n // Safety: AVM opcodes are constrained by the AVM itself\n unsafe {\n transaction_fee()\n }\n }\n\n pub fn chain_id(_self: Self) -> Field {\n // Safety: AVM opcodes are constrained by the AVM itself\n unsafe {\n chain_id()\n }\n }\n pub fn version(_self: Self) -> Field {\n // Safety: AVM opcodes are constrained by the AVM itself\n unsafe {\n version()\n }\n }\n pub fn block_number(_self: Self) -> Field {\n // Safety: AVM opcodes are constrained by the AVM itself\n unsafe {\n block_number()\n }\n }\n pub fn timestamp(_self: Self) -> u64 {\n // Safety: AVM opcodes are constrained by the AVM itself\n unsafe {\n timestamp()\n }\n }\n pub fn fee_per_l2_gas(_self: Self) -> Field {\n // Safety: AVM opcodes are constrained by the AVM itself\n unsafe {\n fee_per_l2_gas()\n }\n }\n pub fn fee_per_da_gas(_self: Self) -> Field {\n // Safety: AVM opcodes are constrained by the AVM itself\n unsafe {\n fee_per_da_gas()\n }\n }\n\n pub fn l2_gas_left(_self: Self) -> Field {\n // Safety: AVM opcodes are constrained by the AVM itself\n unsafe {\n l2_gas_left()\n }\n }\n pub fn da_gas_left(_self: Self) -> Field {\n // Safety: AVM opcodes are constrained by the AVM itself\n unsafe {\n da_gas_left()\n }\n }\n pub fn is_static_call(_self: Self) -> bool {\n // Safety: AVM opcodes are constrained by the AVM itself\n unsafe { is_static_call() } == 1\n }\n\n pub fn raw_storage_read(_self: Self, storage_slot: Field) -> [Field; N] {\n let mut out = [0; N];\n for i in 0..N {\n // Safety: AVM opcodes are constrained by the AVM itself\n out[i] = unsafe { storage_read(storage_slot + i as Field) };\n }\n out\n }\n\n pub fn storage_read(self, storage_slot: Field) -> T\n where\n T: Packable,\n {\n T::unpack(self.raw_storage_read(storage_slot))\n }\n\n pub fn raw_storage_write(_self: Self, storage_slot: Field, values: [Field; N]) {\n for i in 0..N {\n // Safety: AVM opcodes are constrained by the AVM itself\n unsafe { storage_write(storage_slot + i as Field, values[i]) };\n }\n }\n\n pub fn storage_write(self, storage_slot: Field, value: T)\n where\n T: Packable,\n {\n self.raw_storage_write(storage_slot, value.pack());\n }\n}\n\n// Unconstrained opcode wrappers (do not use directly).\nunconstrained fn address() -> AztecAddress {\n address_opcode()\n}\nunconstrained fn sender() -> AztecAddress {\n sender_opcode()\n}\nunconstrained fn transaction_fee() -> Field {\n transaction_fee_opcode()\n}\nunconstrained fn chain_id() -> Field {\n chain_id_opcode()\n}\nunconstrained fn version() -> Field {\n version_opcode()\n}\nunconstrained fn block_number() -> Field {\n block_number_opcode()\n}\nunconstrained fn timestamp() -> u64 {\n timestamp_opcode()\n}\nunconstrained fn fee_per_l2_gas() -> Field {\n fee_per_l2_gas_opcode()\n}\nunconstrained fn fee_per_da_gas() -> Field {\n fee_per_da_gas_opcode()\n}\nunconstrained fn l2_gas_left() -> Field {\n l2_gas_left_opcode()\n}\nunconstrained fn da_gas_left() -> Field {\n da_gas_left_opcode()\n}\nunconstrained fn is_static_call() -> Field {\n is_static_call_opcode()\n}\nunconstrained fn note_hash_exists(note_hash: Field, leaf_index: Field) -> u1 {\n note_hash_exists_opcode(note_hash, leaf_index)\n}\nunconstrained fn emit_note_hash(note_hash: Field) {\n emit_note_hash_opcode(note_hash)\n}\nunconstrained fn nullifier_exists(nullifier: Field, address: Field) -> u1 {\n nullifier_exists_opcode(nullifier, address)\n}\nunconstrained fn emit_nullifier(nullifier: Field) {\n emit_nullifier_opcode(nullifier)\n}\nunconstrained fn emit_public_log(message: [Field]) {\n emit_public_log_opcode(message)\n}\nunconstrained fn l1_to_l2_msg_exists(msg_hash: Field, msg_leaf_index: Field) -> u1 {\n l1_to_l2_msg_exists_opcode(msg_hash, msg_leaf_index)\n}\nunconstrained fn send_l2_to_l1_msg(recipient: EthAddress, content: Field) {\n send_l2_to_l1_msg_opcode(recipient, content)\n}\nunconstrained fn call(\n l2_gas_allocation: Field,\n da_gas_allocation: Field,\n address: AztecAddress,\n args: [Field],\n) {\n call_opcode(l2_gas_allocation, da_gas_allocation, address, args)\n}\n\nunconstrained fn call_static(\n l2_gas_allocation: Field,\n da_gas_allocation: Field,\n address: AztecAddress,\n args: [Field],\n) {\n call_static_opcode(l2_gas_allocation, da_gas_allocation, address, args)\n}\n\npub unconstrained fn calldata_copy(cdoffset: u32, copy_size: u32) -> [Field; N] {\n calldata_copy_opcode(cdoffset, copy_size)\n}\n\n// `success_copy` is placed immediately after the CALL opcode to get the success value\nunconstrained fn success_copy() -> bool {\n success_copy_opcode()\n}\n\nunconstrained fn returndata_size() -> u32 {\n returndata_size_opcode()\n}\n\nunconstrained fn returndata_copy(rdoffset: u32, copy_size: u32) -> [Field] {\n returndata_copy_opcode(rdoffset, copy_size)\n}\n\npub unconstrained fn avm_return(returndata: [Field]) {\n return_opcode(returndata)\n}\n\n// This opcode reverts using the exact data given. In general it should only be used\n// to do rethrows, where the revert data is the same as the original revert data.\n// For normal reverts, use Noir's `assert` which, on top of reverting, will also add\n// an error selector to the revert data.\nunconstrained fn avm_revert(revertdata: [Field]) {\n revert_opcode(revertdata)\n}\n\nunconstrained fn storage_read(storage_slot: Field) -> Field {\n storage_read_opcode(storage_slot)\n}\n\nunconstrained fn storage_write(storage_slot: Field, value: Field) {\n storage_write_opcode(storage_slot, value);\n}\n\nimpl Empty for PublicContext {\n fn empty() -> Self {\n PublicContext::new(|| 0)\n }\n}\n\n// AVM oracles (opcodes) follow, do not use directly.\n#[oracle(avmOpcodeAddress)]\nunconstrained fn address_opcode() -> AztecAddress {}\n\n#[oracle(avmOpcodeSender)]\nunconstrained fn sender_opcode() -> AztecAddress {}\n\n#[oracle(avmOpcodeTransactionFee)]\nunconstrained fn transaction_fee_opcode() -> Field {}\n\n#[oracle(avmOpcodeChainId)]\nunconstrained fn chain_id_opcode() -> Field {}\n\n#[oracle(avmOpcodeVersion)]\nunconstrained fn version_opcode() -> Field {}\n\n#[oracle(avmOpcodeBlockNumber)]\nunconstrained fn block_number_opcode() -> Field {}\n\n#[oracle(avmOpcodeTimestamp)]\nunconstrained fn timestamp_opcode() -> u64 {}\n\n#[oracle(avmOpcodeFeePerL2Gas)]\nunconstrained fn fee_per_l2_gas_opcode() -> Field {}\n\n#[oracle(avmOpcodeFeePerDaGas)]\nunconstrained fn fee_per_da_gas_opcode() -> Field {}\n\n#[oracle(avmOpcodeL2GasLeft)]\nunconstrained fn l2_gas_left_opcode() -> Field {}\n\n#[oracle(avmOpcodeDaGasLeft)]\nunconstrained fn da_gas_left_opcode() -> Field {}\n\n#[oracle(avmOpcodeIsStaticCall)]\nunconstrained fn is_static_call_opcode() -> Field {}\n\n#[oracle(avmOpcodeNoteHashExists)]\nunconstrained fn note_hash_exists_opcode(note_hash: Field, leaf_index: Field) -> u1 {}\n\n#[oracle(avmOpcodeEmitNoteHash)]\nunconstrained fn emit_note_hash_opcode(note_hash: Field) {}\n\n#[oracle(avmOpcodeNullifierExists)]\nunconstrained fn nullifier_exists_opcode(nullifier: Field, address: Field) -> u1 {}\n\n#[oracle(avmOpcodeEmitNullifier)]\nunconstrained fn emit_nullifier_opcode(nullifier: Field) {}\n\n// TODO(#11124): rename unencrypted to public in avm\n#[oracle(avmOpcodeEmitUnencryptedLog)]\nunconstrained fn emit_public_log_opcode(message: [Field]) {}\n\n#[oracle(avmOpcodeL1ToL2MsgExists)]\nunconstrained fn l1_to_l2_msg_exists_opcode(msg_hash: Field, msg_leaf_index: Field) -> u1 {}\n\n#[oracle(avmOpcodeSendL2ToL1Msg)]\nunconstrained fn send_l2_to_l1_msg_opcode(recipient: EthAddress, content: Field) {}\n\n#[oracle(avmOpcodeCalldataCopy)]\nunconstrained fn calldata_copy_opcode(cdoffset: u32, copy_size: u32) -> [Field; N] {}\n\n#[oracle(avmOpcodeReturndataSize)]\nunconstrained fn returndata_size_opcode() -> u32 {}\n\n#[oracle(avmOpcodeReturndataCopy)]\nunconstrained fn returndata_copy_opcode(rdoffset: u32, copy_size: u32) -> [Field] {}\n\n#[oracle(avmOpcodeReturn)]\nunconstrained fn return_opcode(returndata: [Field]) {}\n\n// This opcode reverts using the exact data given. In general it should only be used\n// to do rethrows, where the revert data is the same as the original revert data.\n// For normal reverts, use Noir's `assert` which, on top of reverting, will also add\n// an error selector to the revert data.\n#[oracle(avmOpcodeRevert)]\nunconstrained fn revert_opcode(revertdata: [Field]) {}\n\n#[oracle(avmOpcodeCall)]\nunconstrained fn call_opcode(\n l2_gas_allocation: Field,\n da_gas_allocation: Field,\n address: AztecAddress,\n args: [Field],\n) {}\n\n#[oracle(avmOpcodeStaticCall)]\nunconstrained fn call_static_opcode(\n l2_gas_allocation: Field,\n da_gas_allocation: Field,\n address: AztecAddress,\n args: [Field],\n) {}\n\n#[oracle(avmOpcodeSuccessCopy)]\nunconstrained fn success_copy_opcode() -> bool {}\n\n#[oracle(avmOpcodeStorageRead)]\nunconstrained fn storage_read_opcode(storage_slot: Field) -> Field {}\n\n#[oracle(avmOpcodeStorageWrite)]\nunconstrained fn storage_write_opcode(storage_slot: Field, value: Field) {}\n" - }, - "62": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/context/utility_context.nr", - "source": "use crate::oracle::{\n execution::{get_block_number, get_chain_id, get_contract_address, get_version},\n storage::storage_read,\n};\nuse dep::protocol_types::{address::AztecAddress, traits::Packable};\n\npub struct UtilityContext {\n block_number: u32,\n contract_address: AztecAddress,\n version: Field,\n chain_id: Field,\n}\n\nimpl UtilityContext {\n pub unconstrained fn new() -> Self {\n // We could call these oracles on the getters instead of at creation, which makes sense given that they might\n // not even be accessed. However any performance gains are minimal, and we'd rather fail early if a user\n // incorrectly attempts to create a UtilityContext in an environment in which these oracles are not\n // available.\n let block_number = get_block_number();\n let contract_address = get_contract_address();\n let chain_id = get_chain_id();\n let version = get_version();\n Self { block_number, contract_address, version, chain_id }\n }\n\n pub unconstrained fn at(contract_address: AztecAddress) -> Self {\n let block_number = get_block_number();\n let chain_id = get_chain_id();\n let version = get_version();\n Self { block_number, contract_address, version, chain_id }\n }\n\n pub unconstrained fn at_historical(contract_address: AztecAddress, block_number: u32) -> Self {\n let chain_id = get_chain_id();\n let version = get_version();\n Self { block_number, contract_address, version, chain_id }\n }\n\n pub fn block_number(self) -> u32 {\n self.block_number\n }\n\n pub fn this_address(self) -> AztecAddress {\n self.contract_address\n }\n\n pub fn version(self) -> Field {\n self.version\n }\n\n pub fn chain_id(self) -> Field {\n self.chain_id\n }\n\n pub unconstrained fn raw_storage_read(\n self: Self,\n storage_slot: Field,\n ) -> [Field; N] {\n storage_read(self.this_address(), storage_slot, self.block_number())\n }\n\n pub unconstrained fn storage_read(self, storage_slot: Field) -> T\n where\n T: Packable,\n {\n T::unpack(self.raw_storage_read(storage_slot))\n }\n}\n" - }, - "66": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/hash.nr", - "source": "use dep::protocol_types::{\n address::{AztecAddress, EthAddress},\n constants::{\n GENERATOR_INDEX__FUNCTION_ARGS, GENERATOR_INDEX__MESSAGE_NULLIFIER,\n GENERATOR_INDEX__PUBLIC_CALLDATA, GENERATOR_INDEX__SECRET_HASH,\n },\n hash::{poseidon2_hash_with_separator, poseidon2_hash_with_separator_slice, sha256_to_field},\n point::Point,\n traits::{Hash, ToField},\n};\n\npub use dep::protocol_types::hash::{compute_siloed_nullifier, pedersen_hash};\n\npub fn pedersen_commitment(inputs: [Field; N], hash_index: u32) -> Point {\n std::hash::pedersen_commitment_with_separator(inputs, hash_index)\n}\n\npub fn compute_secret_hash(secret: Field) -> Field {\n poseidon2_hash_with_separator([secret], GENERATOR_INDEX__SECRET_HASH)\n}\n\npub fn compute_l1_to_l2_message_hash(\n sender: EthAddress,\n chain_id: Field,\n recipient: AztecAddress,\n version: Field,\n content: Field,\n secret_hash: Field,\n leaf_index: Field,\n) -> Field {\n let mut hash_bytes = [0 as u8; 224];\n let sender_bytes: [u8; 32] = sender.to_field().to_be_bytes();\n let chain_id_bytes: [u8; 32] = chain_id.to_be_bytes();\n let recipient_bytes: [u8; 32] = recipient.to_field().to_be_bytes();\n let version_bytes: [u8; 32] = version.to_be_bytes();\n let content_bytes: [u8; 32] = content.to_be_bytes();\n let secret_hash_bytes: [u8; 32] = secret_hash.to_be_bytes();\n let leaf_index_bytes: [u8; 32] = leaf_index.to_be_bytes();\n\n for i in 0..32 {\n hash_bytes[i] = sender_bytes[i];\n hash_bytes[i + 32] = chain_id_bytes[i];\n hash_bytes[i + 64] = recipient_bytes[i];\n hash_bytes[i + 96] = version_bytes[i];\n hash_bytes[i + 128] = content_bytes[i];\n hash_bytes[i + 160] = secret_hash_bytes[i];\n hash_bytes[i + 192] = leaf_index_bytes[i];\n }\n\n sha256_to_field(hash_bytes)\n}\n\n// The nullifier of a l1 to l2 message is the hash of the message salted with the secret\npub fn compute_l1_to_l2_message_nullifier(message_hash: Field, secret: Field) -> Field {\n poseidon2_hash_with_separator([message_hash, secret], GENERATOR_INDEX__MESSAGE_NULLIFIER)\n}\n\npub struct ArgsHasher {\n pub fields: [Field],\n}\n\nimpl Hash for ArgsHasher {\n fn hash(self) -> Field {\n hash_args(self.fields)\n }\n}\n\nimpl ArgsHasher {\n pub fn new() -> Self {\n Self { fields: [] }\n }\n\n pub fn add(&mut self, field: Field) {\n self.fields = self.fields.push_back(field);\n }\n\n pub fn add_multiple(&mut self, fields: [Field; N]) {\n for i in 0..N {\n self.fields = self.fields.push_back(fields[i]);\n }\n }\n}\n\n// Computes the hash of input arguments or return values for private functions, or for authwit creation.\npub fn hash_args_array(args: [Field; N]) -> Field {\n if args.len() == 0 {\n 0\n } else {\n poseidon2_hash_with_separator(args, GENERATOR_INDEX__FUNCTION_ARGS)\n }\n}\n\n// Same as `hash_args_array`, but takes a slice instead of an array.\npub fn hash_args(args: [Field]) -> Field {\n if args.len() == 0 {\n 0\n } else {\n poseidon2_hash_with_separator_slice(args, GENERATOR_INDEX__FUNCTION_ARGS)\n }\n}\n\n// Computes the hash of calldata for public functions.\npub fn hash_calldata_array(calldata: [Field; N]) -> Field {\n if calldata.len() == 0 {\n 0\n } else {\n poseidon2_hash_with_separator(calldata, GENERATOR_INDEX__PUBLIC_CALLDATA)\n }\n}\n\n// Same as `hash_calldata_array`, but takes a slice instead of an array.\npub fn hash_calldata(calldata: [Field]) -> Field {\n if calldata.len() == 0 {\n 0\n } else {\n poseidon2_hash_with_separator_slice(calldata, GENERATOR_INDEX__PUBLIC_CALLDATA)\n }\n}\n\n#[test]\nunconstrained fn compute_var_args_hash() {\n let mut input = ArgsHasher::new();\n for i in 0..100 {\n input.add(i as Field);\n }\n let hash = input.hash();\n dep::std::println(hash);\n assert(hash == 0x19b0d74feb06ebde19edd85a28986c97063e84b3b351a8b666c7cac963ce655f);\n}\n" - }, - "83": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/keys/getters/mod.nr", - "source": "use crate::{\n keys::constants::{NULLIFIER_INDEX, OUTGOING_INDEX},\n oracle::{\n key_validation_request::get_key_validation_request,\n keys::get_public_keys_and_partial_address,\n },\n};\nuse dep::protocol_types::{address::AztecAddress, public_keys::PublicKeys};\n\nmod test;\n\npub unconstrained fn get_nsk_app(npk_m_hash: Field) -> Field {\n get_key_validation_request(npk_m_hash, NULLIFIER_INDEX).sk_app\n}\n\n// A helper function that gets app-siloed outgoing viewing key for a given `ovpk_m_hash`. This function is used\n// in unconstrained contexts only - when computing unconstrained note logs. The safe alternative is `request_ovsk_app`\n// function defined on `PrivateContext`.\npub unconstrained fn get_ovsk_app(ovpk_m_hash: Field) -> Field {\n get_key_validation_request(ovpk_m_hash, OUTGOING_INDEX).sk_app\n}\n\n// Returns all public keys for a given account, applying proper constraints to the context. We read all\n// keys at once since the constraints for reading them all are actually fewer than if we read them one at a time - any\n// read keys that are not required by the caller can simply be discarded.\npub fn get_public_keys(account: AztecAddress) -> PublicKeys {\n // Safety: Public keys are constrained by showing their inclusion in the address's preimage.\n let (public_keys, partial_address) = unsafe { get_public_keys_and_partial_address(account) };\n assert_eq(\n account,\n AztecAddress::compute(public_keys, partial_address),\n \"Invalid public keys hint for address\",\n );\n\n public_keys\n}\n" - }, - "87": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/macros/aztec.nr", - "source": "use crate::{\n macros::{\n dispatch::generate_public_dispatch,\n functions::{stub_registry, utils::check_each_fn_macroified},\n notes::{generate_note_export, NOTES},\n storage::STORAGE_LAYOUT_NAME,\n utils::{get_trait_impl_method, module_has_storage},\n },\n messages::discovery::private_notes::MAX_NOTE_PACKED_LEN,\n};\n\n/// Marks a contract as an Aztec contract, generating the interfaces for its functions and notes, as well as injecting\n/// the `sync_private_state` utility function.\n/// Note: This is a module annotation, so the returned quote gets injected inside the module (contract) itself.\npub comptime fn aztec(m: Module) -> Quoted {\n let interface = generate_contract_interface(m);\n\n // Functions that don't have #[private], #[public], #[utility], #[contract_library_method], or #[test] are not\n // allowed in contracts.\n check_each_fn_macroified(m);\n\n let contract_library_method_compute_note_hash_and_nullifier =\n generate_contract_library_method_compute_note_hash_and_nullifier();\n let note_exports = generate_note_exports();\n let public_dispatch = generate_public_dispatch(m);\n let sync_private_state = generate_sync_private_state();\n\n quote {\n $note_exports\n $interface\n $contract_library_method_compute_note_hash_and_nullifier\n $public_dispatch\n $sync_private_state\n }\n}\n\ncomptime fn generate_contract_interface(m: Module) -> Quoted {\n let module_name = m.name();\n let contract_stubs = stub_registry::get(m);\n let fn_stubs_quote = if contract_stubs.is_some() {\n contract_stubs.unwrap().join(quote {})\n } else {\n quote {}\n };\n\n let has_storage_layout = module_has_storage(m) & STORAGE_LAYOUT_NAME.get(m).is_some();\n let storage_layout_getter = if has_storage_layout {\n let storage_layout_name = STORAGE_LAYOUT_NAME.get(m).unwrap();\n quote {\n pub fn storage_layout() -> StorageLayoutFields {\n $storage_layout_name.fields\n }\n }\n } else {\n quote {}\n };\n\n let library_storage_layout_getter = if has_storage_layout {\n quote {\n #[contract_library_method]\n $storage_layout_getter\n }\n } else {\n quote {}\n };\n\n quote {\n pub struct $module_name {\n pub target_contract: dep::aztec::protocol_types::address::AztecAddress\n }\n\n impl $module_name {\n $fn_stubs_quote\n\n pub fn at(\n addr: aztec::protocol_types::address::AztecAddress\n ) -> Self {\n Self { target_contract: addr }\n }\n\n pub fn interface() -> Self {\n Self { target_contract: aztec::protocol_types::address::AztecAddress::zero() }\n }\n\n $storage_layout_getter\n }\n\n #[contract_library_method]\n pub fn at(\n addr: aztec::protocol_types::address::AztecAddress\n ) -> $module_name {\n $module_name { target_contract: addr }\n }\n\n #[contract_library_method]\n pub fn interface() -> $module_name {\n $module_name { target_contract: aztec::protocol_types::address::AztecAddress::zero() }\n }\n\n $library_storage_layout_getter\n\n }\n}\n\n/// Generates a contract library method called `_compute_note_hash_and_nullifier` which is used for note\n/// discovery (to create the `aztec::messages::discovery::ComputeNoteHashAndNullifier` function) and to implement the\n/// `compute_note_hash_and_nullifier` unconstrained contract function.\ncomptime fn generate_contract_library_method_compute_note_hash_and_nullifier() -> Quoted {\n let notes = NOTES.entries();\n\n if notes.len() > 0 {\n let max_note_packed_len = notes.fold(\n 0,\n |acc, (_, (_, len, _, _)): (Type, (TypeDefinition, u32, Field, [(Quoted, u32, bool)]))| {\n if len > acc {\n len\n } else {\n acc\n }\n },\n );\n\n if max_note_packed_len > MAX_NOTE_PACKED_LEN {\n panic(\n f\"One of the notes has packed len {max_note_packed_len} but the maximum is {MAX_NOTE_PACKED_LEN}\",\n );\n }\n\n // Contracts that do define notes produce an if-else chain where `note_type_id` is matched against the\n // `get_note_type_id()` function of each note type that we know of, in order to identify the note type. Once we\n // know it we call we correct `unpack` method from the `Packable` trait to obtain the underlying note type, and\n // compute the note hash (non-siloed) and inner nullifier (also non-siloed).\n\n let mut if_note_type_id_match_statements_list = &[];\n for i in 0..notes.len() {\n let (typ, (_, packed_note_length, _, _)) = notes[i];\n\n let get_note_type_id = get_trait_impl_method(\n typ,\n quote { crate::note::note_interface::NoteType },\n quote { get_id },\n );\n let unpack = get_trait_impl_method(\n typ,\n quote { crate::protocol_types::traits::Packable<_> },\n quote { unpack },\n );\n\n let compute_note_hash = get_trait_impl_method(\n typ,\n quote { crate::note::note_interface::NoteHash },\n quote { compute_note_hash },\n );\n\n let compute_nullifier_unconstrained = get_trait_impl_method(\n typ,\n quote { crate::note::note_interface::NoteHash },\n quote { compute_nullifier_unconstrained },\n );\n\n let if_or_else_if = if i == 0 {\n quote { if }\n } else {\n quote { else if }\n };\n\n if_note_type_id_match_statements_list = if_note_type_id_match_statements_list.push_back(\n quote {\n $if_or_else_if note_type_id == $get_note_type_id() {\n // As an extra safety check we make sure that the packed_note BoundedVec has the expected\n // length, since we're about to interpret it's raw storage as a fixed-size array by calling the\n // unpack function on it.\n let expected_len = $packed_note_length;\n let actual_len = packed_note.len();\n assert(\n actual_len == expected_len,\n f\"Expected packed note of length {expected_len} but got {actual_len} for note type id {note_type_id}\"\n );\n\n let note = $unpack(aztec::utils::array::subarray(packed_note.storage(), 0));\n\n let note_hash = $compute_note_hash(note, storage_slot);\n \n // The message discovery process finds settled notes, that is, notes that were created in prior\n // transactions and are therefore already part of the note hash tree. We therefore compute the\n // nullification note hash by treating the note as a settled note with the provided nonce.\n let note_hash_for_nullify = aztec::note::utils::compute_note_hash_for_nullify(\n aztec::note::retrieved_note::RetrievedNote{ \n note, \n contract_address, \n metadata: aztec::note::note_metadata::SettledNoteMetadata::new(nonce).into() \n }, \n storage_slot,\n );\n\n let inner_nullifier = $compute_nullifier_unconstrained(note, note_hash_for_nullify);\n\n Option::some(\n aztec::messages::discovery::NoteHashAndNullifier {\n note_hash, inner_nullifier\n }\n )\n }\n },\n );\n }\n\n let if_note_type_id_match_statements = if_note_type_id_match_statements_list.join(quote {});\n\n quote {\n /// Unpacks an array into a note corresponding to `note_type_id` and then computes its note hash\n /// (non-siloed) and inner nullifier (non-siloed) assuming the note has been inserted into the note hash\n /// tree with `nonce`.\n ///\n /// The signature of this function notably matches the `aztec::messages::discovery::ComputeNoteHashAndNullifier` type,\n /// and so it can be used to call functions from that module such as `discover_new_messages`, \n /// `do_process_log` and `attempt_note_discovery`.\n ///\n /// This function is automatically injected by the `#[aztec]` macro.\n #[contract_library_method]\n unconstrained fn _compute_note_hash_and_nullifier(\n packed_note: BoundedVec,\n storage_slot: Field,\n note_type_id: Field,\n contract_address: aztec::protocol_types::address::AztecAddress,\n nonce: Field,\n ) -> Option {\n $if_note_type_id_match_statements\n else {\n Option::none()\n }\n }\n }\n } else {\n // Contracts with no notes still implement this function to avoid having special-casing, the implementation\n // simply throws immediately.\n quote {\n /// This contract does not use private notes, so this function should never be called as it will\n /// unconditionally fail.\n ///\n /// This function is automatically injected by the `#[aztec]` macro.\n #[contract_library_method]\n unconstrained fn _compute_note_hash_and_nullifier(\n _packed_note: BoundedVec,\n _storage_slot: Field,\n _note_type_id: Field,\n _contract_address: aztec::protocol_types::address::AztecAddress,\n _nonce: Field,\n ) -> Option {\n panic(f\"This contract does not use private notes\")\n }\n }\n }\n}\n\ncomptime fn generate_note_exports() -> Quoted {\n let notes = NOTES.values();\n // Second value in each tuple is `note_packed_len` and that is ignored here because it's only used when\n // generating partial note helper functions.\n notes\n .map(|(s, _, note_type_id, fields): (TypeDefinition, u32, Field, [(Quoted, u32, bool)])| {\n generate_note_export(s, note_type_id, fields)\n })\n .join(quote {})\n}\n\ncomptime fn generate_sync_private_state() -> Quoted {\n // We obtain the `utility` function on the next line instead of directly doing\n // `#[aztec::macros::functions::utility]` in the returned quote because the latter would result in the function\n // attribute having the full path in the ABI. This is undesirable because we use the information in the ABI only\n // to determine whether a function is `private`, `public`, or `utility`.\n let utility = crate::macros::functions::utility;\n\n // All we need to do here is trigger message discovery, but this is already done by the #[utility] macro - we don't\n // need to do anything extra.\n quote {\n #[$utility]\n unconstrained fn sync_private_state() {\n }\n }\n}\n" - }, - "88": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/macros/dispatch.nr", - "source": "use super::utils::compute_fn_selector;\nuse std::panic;\n\n/// Returns an `fn public_dispatch(...)` function for the given module that's assumed to be an Aztec contract.\npub comptime fn generate_public_dispatch(m: Module) -> Quoted {\n let functions = m.functions();\n let functions =\n functions.filter(|function: FunctionDefinition| function.has_named_attribute(\"public\"));\n\n let unit = get_type::<()>();\n\n let ifs = functions.map(|function: FunctionDefinition| {\n let parameters = function.parameters();\n let return_type = function.return_type();\n\n let selector: Field = compute_fn_selector(function);\n\n let mut parameters_size = 0;\n for param in parameters {\n parameters_size += size_in_fields(param.1);\n }\n\n let initial_read = if parameters.len() == 0 {\n quote {}\n } else {\n // The initial calldata_copy offset is 1 to skip the Field selector\n // The expected calldata is the serialization of\n // - FunctionSelector: the selector of the function intended to dispatch\n // - Parameters: the parameters of the function intended to dispatch\n // That is, exactly what is expected for a call to the target function,\n // but with a selector added at the beginning.\n quote {\n let input_calldata: [Field; $parameters_size] = dep::aztec::context::public_context::calldata_copy(1, $parameters_size);\n let mut reader = dep::aztec::protocol_types::utils::reader::Reader::new(input_calldata);\n }\n };\n\n let parameter_index = &mut 0;\n let reads = parameters.map(|param: (Quoted, Type)| {\n let parameter_index_value = *parameter_index;\n let param_name = f\"arg{parameter_index_value}\".quoted_contents();\n let param_type = param.1;\n let read = quote {\n let $param_name: $param_type = reader.read_struct(dep::aztec::protocol_types::traits::Deserialize::deserialize);\n };\n *parameter_index += 1;\n quote { $read }\n });\n let read = reads.join(quote { });\n\n let mut args = &[];\n for parameter_index in 0..parameters.len() {\n let param_name = f\"arg{parameter_index}\".quoted_contents();\n args = args.push_back(quote { $param_name });\n }\n\n let args = args.join(quote { , });\n // name of the function is assigned just before the call so debug metadata doesn't span most of this macro when figuring out where the call comes from.\n let name = function.name();\n let call = quote { $name($args) };\n\n let return_code = if return_type == unit {\n quote {\n $call;\n // Force early return.\n dep::aztec::context::public_context::avm_return([]);\n }\n } else {\n quote {\n let return_value = dep::aztec::protocol_types::traits::Serialize::serialize($call);\n dep::aztec::context::public_context::avm_return(return_value.as_slice());\n }\n };\n\n let if_ = quote {\n if selector == $selector {\n $initial_read\n $read\n $return_code\n }\n };\n if_\n });\n\n if ifs.len() == 0 {\n // No dispatch function if there are no public functions\n quote {}\n } else {\n let ifs = ifs.push_back(quote { panic(f\"Unknown selector {selector}\") });\n let dispatch = ifs.join(quote { });\n\n let body = quote {\n // We mark this as public because our whole system depends on public\n // functions having this attribute. However, the public MACRO will\n // handle the public_dispatch function specially and do nothing.\n #[public]\n pub unconstrained fn public_dispatch(selector: Field) {\n $dispatch\n }\n };\n\n body\n }\n}\n\ncomptime fn size_in_fields(typ: Type) -> u32 {\n let size = array_size_in_fields(typ);\n let size = size.or_else(|| bool_size_in_fields(typ));\n let size = size.or_else(|| constant_size_in_fields(typ));\n let size = size.or_else(|| field_size_in_fields(typ));\n let size = size.or_else(|| int_size_in_fields(typ));\n let size = size.or_else(|| str_size_in_fields(typ));\n let size = size.or_else(|| struct_size_in_fields(typ));\n let size = size.or_else(|| tuple_size_in_fields(typ));\n if size.is_some() {\n size.unwrap()\n } else {\n panic(f\"Can't determine size in fields of {typ}\")\n }\n}\n\ncomptime fn array_size_in_fields(typ: Type) -> Option {\n typ.as_array().and_then(|typ: (Type, Type)| {\n let (typ, element_size) = typ;\n element_size.as_constant().map(|x: u32| x * size_in_fields(typ))\n })\n}\n\ncomptime fn bool_size_in_fields(typ: Type) -> Option {\n if typ.is_bool() {\n Option::some(1)\n } else {\n Option::none()\n }\n}\n\ncomptime fn field_size_in_fields(typ: Type) -> Option {\n if typ.is_field() {\n Option::some(1)\n } else {\n Option::none()\n }\n}\n\ncomptime fn int_size_in_fields(typ: Type) -> Option {\n if typ.as_integer().is_some() {\n Option::some(1)\n } else {\n Option::none()\n }\n}\n\ncomptime fn constant_size_in_fields(typ: Type) -> Option {\n typ.as_constant()\n}\n\ncomptime fn str_size_in_fields(typ: Type) -> Option {\n typ.as_str().map(|typ| size_in_fields(typ))\n}\n\ncomptime fn struct_size_in_fields(typ: Type) -> Option {\n typ.as_data_type().map(|typ: (TypeDefinition, [Type])| {\n let struct_type = typ.0;\n let generics = typ.1;\n let mut size = 0;\n for field in struct_type.fields(generics) {\n size += size_in_fields(field.1);\n }\n size\n })\n}\n\ncomptime fn tuple_size_in_fields(typ: Type) -> Option {\n typ.as_tuple().map(|types: [Type]| {\n let mut size = 0;\n for typ in types {\n size += size_in_fields(typ);\n }\n size\n })\n}\n\ncomptime fn get_type() -> Type {\n let t: T = std::mem::zeroed();\n std::meta::type_of(t)\n}\n" - }, - "92": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/macros/functions/initialization_utils.nr", - "source": "use dep::protocol_types::{\n abis::function_selector::FunctionSelector, address::AztecAddress,\n constants::GENERATOR_INDEX__CONSTRUCTOR, hash::poseidon2_hash_with_separator, traits::ToField,\n};\n\nuse crate::{\n context::{PrivateContext, PublicContext},\n oracle::get_contract_instance::{\n get_contract_instance, get_contract_instance_deployer_avm,\n get_contract_instance_initialization_hash_avm,\n },\n};\n\npub fn mark_as_initialized_public(context: &mut PublicContext) {\n let init_nullifier =\n compute_unsiloed_contract_initialization_nullifier((*context).this_address());\n context.push_nullifier(init_nullifier);\n}\n\npub fn mark_as_initialized_private(context: &mut PrivateContext) {\n let init_nullifier =\n compute_unsiloed_contract_initialization_nullifier((*context).this_address());\n context.push_nullifier(init_nullifier);\n}\n\npub fn assert_is_initialized_public(context: &mut PublicContext) {\n let init_nullifier = compute_unsiloed_contract_initialization_nullifier(context.this_address());\n assert(context.nullifier_exists(init_nullifier, context.this_address()), \"Not initialized\");\n}\n\npub fn assert_is_initialized_private(context: &mut PrivateContext) {\n let init_nullifier = compute_unsiloed_contract_initialization_nullifier(context.this_address());\n context.push_nullifier_read_request(init_nullifier);\n}\n\nfn compute_unsiloed_contract_initialization_nullifier(address: AztecAddress) -> Field {\n address.to_field()\n}\n\npub fn assert_initialization_matches_address_preimage_public(context: PublicContext) {\n let address = context.this_address();\n let deployer = get_contract_instance_deployer_avm(address).unwrap();\n let initialization_hash = get_contract_instance_initialization_hash_avm(address).unwrap();\n let expected_init = compute_initialization_hash(context.selector(), context.get_args_hash());\n assert(initialization_hash == expected_init, \"Initialization hash does not match\");\n assert(\n (deployer.is_zero()) | (deployer == context.msg_sender()),\n \"Initializer address is not the contract deployer\",\n );\n}\n\npub fn assert_initialization_matches_address_preimage_private(context: PrivateContext) {\n let address = context.this_address();\n let instance = get_contract_instance(address);\n let expected_init = compute_initialization_hash(context.selector(), context.get_args_hash());\n assert(instance.initialization_hash == expected_init, \"Initialization hash does not match\");\n assert(\n (instance.deployer.is_zero()) | (instance.deployer == context.msg_sender()),\n \"Initializer address is not the contract deployer\",\n );\n}\n\n/// This function is not only used in macros but it's also used by external people to check that an instance has been\n/// initialized with the correct constructor arguments. Don't hide this unless you implement factory functionality.\npub fn compute_initialization_hash(\n init_selector: FunctionSelector,\n init_args_hash: Field,\n) -> Field {\n poseidon2_hash_with_separator(\n [init_selector.to_field(), init_args_hash],\n GENERATOR_INDEX__CONSTRUCTOR,\n )\n}\n" - }, - "95": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/macros/functions/utils.nr", - "source": "use crate::macros::{\n functions::{abi_export::create_fn_abi_export, call_interface_stubs::stub_fn, stub_registry},\n notes::NOTES,\n utils::{\n add_to_hasher, fn_has_noinitcheck, get_fn_visibility, is_fn_contract_library_method,\n is_fn_initializer, is_fn_internal, is_fn_private, is_fn_public, is_fn_test, is_fn_utility,\n is_fn_view, modify_fn_body, module_has_initializer, module_has_storage,\n },\n};\nuse protocol_types::meta::generate_serialize_to_fields;\nuse std::meta::type_of;\n\npub(crate) comptime fn transform_private(f: FunctionDefinition) -> Quoted {\n let fn_abi = create_fn_abi_export(f);\n let fn_stub = stub_fn(f);\n stub_registry::register(f.module(), fn_stub);\n\n // If a function is further modified as unconstrained, we throw an error\n if f.is_unconstrained() {\n let name = f.name();\n panic(\n f\"Function {name} is annotated with #[private] but marked as unconstrained, remove unconstrained keyword\",\n );\n }\n\n let module_has_initializer = module_has_initializer(f.module());\n let module_has_storage = module_has_storage(f.module());\n\n // Private functions undergo a lot of transformations from their Aztec.nr form into a circuit that can be fed to the\n // Private Kernel Circuit.\n // First we change the function signature so that it also receives `PrivateContextInputs`, which contain information\n // about the execution context (e.g. the caller).\n let original_params = f.parameters();\n f.set_parameters(&[(\n quote { inputs },\n quote { crate::context::inputs::private_context_inputs::PrivateContextInputs }.as_type(),\n )]\n .append(original_params));\n\n let mut body = f.body().as_block().unwrap();\n\n // The original params are hashed and passed to the `context` object, so that the kernel can verify we've received\n // the correct values.\n // TODO: Optimize args_hasher for small number of arguments\n let args_hasher_name = quote { args_hasher };\n let args_hasher = original_params.fold(\n quote {\n let mut $args_hasher_name = dep::aztec::hash::ArgsHasher::new();\n },\n |args_hasher, param: (Quoted, Type)| {\n let (name, typ) = param;\n let appended_arg = add_to_hasher(args_hasher_name, name, typ);\n quote {\n $args_hasher\n $appended_arg\n }\n },\n );\n\n let context_creation = quote {\n let mut context = dep::aztec::context::private_context::PrivateContext::new(inputs, dep::aztec::protocol_types::traits::Hash::hash($args_hasher_name));\n };\n\n // Modifications introduced by the different marker attributes.\n let internal_check = if is_fn_internal(f) {\n create_internal_check(f)\n } else {\n quote {}\n };\n\n let view_check = if is_fn_view(f) {\n create_view_check(f)\n } else {\n quote {}\n };\n\n let (assert_initializer, mark_as_initialized) = if is_fn_initializer(f) {\n (create_assert_correct_initializer_args(f), create_mark_as_initialized(f))\n } else {\n (quote {}, quote {})\n };\n\n let storage_init = if module_has_storage {\n quote {\n // Some functions don't access storage, but it'd be quite difficult to only inject this variable if it is\n // referenced. We instead ignore 'unused variable' warnings for it.\n #[allow(unused_variables)]\n let storage = Storage::init(&mut context);\n }\n } else {\n quote {}\n };\n\n // Initialization checks are not included in contracts that don't have initializers.\n let init_check = if module_has_initializer & !is_fn_initializer(f) & !fn_has_noinitcheck(f) {\n create_init_check(f)\n } else {\n quote {}\n };\n\n // All private functions perform message discovery, since they may need to access notes. This is slightly\n // inefficient and could be improved by only doing it once we actually attempt to read any.\n let message_discovery_call = if NOTES.len() > 0 {\n create_message_discovery_call()\n } else {\n quote {}\n };\n\n // Finally, we need to change the return type to be `PrivateCircuitPublicInputs`, which is what the Private Kernel\n // circuit expects.\n let return_value_var_name = quote { macro__returned__values };\n\n let return_value_type = f.return_type();\n let return_value = if body.len() == 0 {\n quote {}\n } else if return_value_type != type_of(()) {\n // The original return value is passed to a second args hasher which the context receives.\n let (body_without_return, last_body_expr) = body.pop_back();\n let return_value = last_body_expr.quoted();\n let return_value_assignment =\n quote { let $return_value_var_name: $return_value_type = $return_value; };\n let return_hasher_name = quote { return_hasher };\n let return_value_into_hasher =\n add_to_hasher(return_hasher_name, return_value_var_name, return_value_type);\n\n body = body_without_return;\n\n quote {\n let mut $return_hasher_name = dep::aztec::hash::ArgsHasher::new();\n $return_value_assignment\n $return_value_into_hasher\n context.set_return_hash($return_hasher_name);\n }\n } else {\n let (body_without_return, last_body_expr) = body.pop_back();\n if !last_body_expr.has_semicolon()\n & last_body_expr.as_for().is_none()\n & last_body_expr.as_assert().is_none()\n & last_body_expr.as_for_range().is_none()\n & last_body_expr.as_assert_eq().is_none()\n & last_body_expr.as_let().is_none() {\n let unused_return_value_name = f\"_{return_value_var_name}\".quoted_contents();\n body = body_without_return.push_back(\n quote { let $unused_return_value_name = $last_body_expr; }.as_expr().unwrap(),\n );\n }\n quote {}\n };\n\n let context_finish = quote { context.finish() };\n\n let to_prepend = quote {\n $args_hasher\n $context_creation\n $assert_initializer\n $init_check\n $internal_check\n $view_check\n $storage_init\n $message_discovery_call\n };\n\n let to_append = quote {\n $return_value\n $mark_as_initialized\n $context_finish\n };\n let modified_body = modify_fn_body(body, to_prepend, to_append);\n f.set_body(modified_body);\n f.set_return_type(\n quote { dep::protocol_types::abis::private_circuit_public_inputs::PrivateCircuitPublicInputs }\n .as_type(),\n );\n f.set_return_data();\n\n fn_abi\n}\n\npub(crate) comptime fn transform_public(f: FunctionDefinition) -> Quoted {\n let fn_abi = create_fn_abi_export(f);\n let fn_stub = stub_fn(f);\n stub_registry::register(f.module(), fn_stub);\n\n // If a function is further modified as unconstrained, we throw an error\n if f.is_unconstrained() {\n let name = f.name();\n panic(\n f\"Function {name} is annotated with #[public] but marked as unconstrained, remove unconstrained keyword\",\n );\n }\n\n let module_has_initializer = module_has_initializer(f.module());\n let module_has_storage = module_has_storage(f.module());\n\n // Public functions undergo a lot of transformations from their Aztec.nr form.\n let original_params = f.parameters();\n let args_len = original_params\n .map(|(name, typ): (Quoted, Type)| {\n generate_serialize_to_fields(name, typ, false).0.len()\n })\n .fold(0, |acc: u32, val: u32| acc + val);\n\n // Unlike in the private case, in public the `context` does not need to receive the hash of the original params.\n let context_creation = quote {\n let mut context = dep::aztec::context::public_context::PublicContext::new(|| {\n // We start from 1 because we skip the selector for the dispatch function.\n let serialized_args : [Field; $args_len] = dep::aztec::context::public_context::calldata_copy(1, $args_len);\n dep::aztec::hash::hash_args_array(serialized_args)\n });\n };\n\n // Modifications introduced by the different marker attributes.\n let internal_check = if is_fn_internal(f) {\n create_internal_check(f)\n } else {\n quote {}\n };\n\n let view_check = if is_fn_view(f) {\n create_view_check(f)\n } else {\n quote {}\n };\n\n let (assert_initializer, mark_as_initialized) = if is_fn_initializer(f) {\n (create_assert_correct_initializer_args(f), create_mark_as_initialized(f))\n } else {\n (quote {}, quote {})\n };\n\n let storage_init = if module_has_storage {\n // Some functions don't access storage, but it'd be quite difficult to only inject this variable if it is\n // referenced. We instead ignore 'unused variable' warnings for it.\n quote {\n #[allow(unused_variables)]\n let storage = Storage::init(&mut context);\n }\n } else {\n quote {}\n };\n\n // Initialization checks are not included in contracts that don't have initializers.\n let init_check = if module_has_initializer & !fn_has_noinitcheck(f) & !is_fn_initializer(f) {\n create_init_check(f)\n } else {\n quote {}\n };\n\n let to_prepend = quote {\n $context_creation\n $assert_initializer\n $init_check\n $internal_check\n $view_check\n $storage_init\n };\n\n let to_append = quote {\n $mark_as_initialized\n };\n\n let body = f.body().as_block().unwrap();\n let modified_body = modify_fn_body(body, to_prepend, to_append);\n f.set_body(modified_body);\n\n // All public functions are automatically made unconstrained, even if they were not marked as such. This is because\n // instead of compiling into a circuit, they will compile to bytecode that will be later transpiled into AVM\n // bytecode.\n f.set_unconstrained(true);\n f.set_return_public(true);\n\n fn_abi\n}\n\npub(crate) comptime fn transform_utility(f: FunctionDefinition) -> Quoted {\n let fn_abi = create_fn_abi_export(f);\n let fn_stub = stub_fn(f);\n stub_registry::register(f.module(), fn_stub);\n\n // Check if function is marked as unconstrained\n if !f.is_unconstrained() {\n let name = f.name();\n panic(\n f\"Function {name} is annotated with #[utility] but not marked as unconstrained, add unconstrained keyword\",\n );\n }\n\n // Create utility context\n let context_creation =\n quote { let mut context = dep::aztec::context::utility_context::UtilityContext::new(); };\n let module_has_storage = module_has_storage(f.module());\n\n // Initialize Storage if module has storage\n let storage_init = if module_has_storage {\n quote {\n // Some functions don't access storage, but it'd be quite difficult to only inject this variable if it is\n // referenced. We instead ignore 'unused variable' warnings for it.\n #[allow(unused_variables)]\n let storage = Storage::init(context);\n }\n } else {\n quote {}\n };\n\n // All utility functions perform message discovery, since they may need to access private notes that would be\n // found during this process. This is slightly inefficient and could be improved by only doing it once we actually\n // attempt to read any.\n let message_discovery_call = if NOTES.len() > 0 {\n create_message_discovery_call()\n } else {\n quote {}\n };\n\n // Inject context creation, storage initialization, and message discovery call at the beginning of the function\n // body.\n let to_prepend = quote {\n $context_creation\n $storage_init\n $message_discovery_call\n };\n let body = f.body().as_block().unwrap();\n let modified_body = modify_fn_body(body, to_prepend, quote {});\n f.set_body(modified_body);\n\n f.set_return_public(true);\n\n fn_abi\n}\n\ncomptime fn create_internal_check(f: FunctionDefinition) -> Quoted {\n let name = f.name();\n let assertion_message = f\"Function {name} can only be called internally\";\n quote { assert(context.msg_sender() == context.this_address(), $assertion_message); }\n}\n\ncomptime fn create_view_check(f: FunctionDefinition) -> Quoted {\n let name = f.name();\n let assertion_message = f\"Function {name} can only be called statically\";\n if is_fn_private(f) {\n // Here `context` is of type context::PrivateContext\n quote { assert(context.inputs.call_context.is_static_call == true, $assertion_message); }\n } else {\n // Here `context` is of type context::PublicContext\n quote { assert(context.is_static_call(), $assertion_message); }\n }\n}\n\ncomptime fn create_assert_correct_initializer_args(f: FunctionDefinition) -> Quoted {\n let fn_visibility = get_fn_visibility(f);\n f\"dep::aztec::macros::functions::initialization_utils::assert_initialization_matches_address_preimage_{fn_visibility}(context);\"\n .quoted_contents()\n}\n\ncomptime fn create_mark_as_initialized(f: FunctionDefinition) -> Quoted {\n let fn_visibility = get_fn_visibility(f);\n f\"dep::aztec::macros::functions::initialization_utils::mark_as_initialized_{fn_visibility}(&mut context);\"\n .quoted_contents()\n}\n\ncomptime fn create_init_check(f: FunctionDefinition) -> Quoted {\n let fn_visibility = get_fn_visibility(f);\n f\"dep::aztec::macros::functions::initialization_utils::assert_is_initialized_{fn_visibility}(&mut context);\"\n .quoted_contents()\n}\n\n/// Injects a call to `aztec::messages::discovery::discover_new_messages`, causing for new notes to be added to PXE and made\n/// available for the current execution.\npub(crate) comptime fn create_message_discovery_call() -> Quoted {\n quote {\n /// Safety: message discovery returns nothing and is performed solely for its side-effects. It is therefore\n /// always safe to call.\n unsafe {\n dep::aztec::messages::discovery::discover_new_messages(\n context.this_address(),\n _compute_note_hash_and_nullifier,\n );\n };\n }\n}\n\n/// Checks if each function in the module is marked with either #[private], #[public], #[utility],\n/// #[contract_library_method], or #[test]. Non-macroified functions are not allowed in contracts.\npub(crate) comptime fn check_each_fn_macroified(m: Module) {\n for f in m.functions() {\n let name = f.name();\n if !is_fn_private(f)\n & !is_fn_public(f)\n & !is_fn_utility(f)\n & !is_fn_contract_library_method(f)\n & !is_fn_test(f) {\n panic(\n f\"Function {name} must be marked as either #[private], #[public], #[utility], #[contract_library_method], or #[test]\",\n );\n }\n }\n}\n" - }, - "97": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/macros/notes.nr", - "source": "use crate::{macros::utils::AsStrQuote, note::note_getter_options::PropertySelector};\nuse poseidon::poseidon2::Poseidon2Hasher;\nuse protocol_types::meta::{derive_packable_and_get_packed_len, generate_serialize_to_fields};\nuse std::{\n collections::umap::UHashMap,\n hash::{BuildHasherDefault, Hash, Hasher},\n meta::{type_of, unquote},\n};\n\n/// A map from note type to (note_struct_definition, note_packed_len, note_type_id, fields).\n/// `fields` is an array of tuples where each tuple contains the name of the field/struct member (e.g. `amount`\n/// in `TokenNote`), the index of where the packed member starts in the packed note and a flag indicating\n/// whether the field is nullable or not.\npub comptime mut global NOTES: UHashMap> =\n UHashMap::default();\n\npub comptime mut global NOTE_TYPE_ID_COUNTER: u32 = 0;\n\n/// The note type id is set by enumerating the note types.\ncomptime fn get_next_note_type_id() -> Field {\n // We assert that the note type id fits within 7 bits\n assert(\n NOTE_TYPE_ID_COUNTER < 128 as u32,\n \"A contract can contain at most 128 different note types\",\n );\n\n let note_type_id = NOTE_TYPE_ID_COUNTER as Field;\n NOTE_TYPE_ID_COUNTER += 1;\n note_type_id\n}\n\n/// Generates a quote that implements `Packable` for a given struct `s`.\n/// If the note struct already implements `Packable`, we return an empty quote.\ncomptime fn derive_packable_if_not_implemented_and_get_len(s: TypeDefinition) -> (Quoted, u32) {\n // We try to get the packed length of the note struct. If it does not implement `Packable`, we get Option::none()\n let packed_len_typ = std::meta::typ::fresh_type_variable();\n // We don't care about the result of the implements check. We just want the get the packed length.\n let _ = s.as_type().implements(\n quote { crate::protocol_types::traits::Packable<$packed_len_typ> }.as_trait_constraint(),\n );\n let maybe_packed_length = packed_len_typ.as_constant();\n\n if maybe_packed_length.is_some() {\n // We got some packed length meaning that the note struct implements `Packable`. For this reason we return\n // an empty quote for the implementation and the packed length.\n (quote {}, maybe_packed_length.unwrap())\n } else {\n // We didn't manage to get the packed length which means the note struct doesn't implement `Packable`\n // so we derive it and return it along with the packed length.\n derive_packable_and_get_packed_len(s)\n }\n}\n\n/// Generates default `NoteType` implementation for a given note struct `s` and returns it as a quote.\n///\n/// impl NoteType for NoteStruct {\n/// fn get_id() -> Field {\n/// ...\n/// }\n/// }\ncomptime fn generate_note_interface(s: TypeDefinition, note_type_id: Field) -> Quoted {\n let name = s.name();\n\n quote {\n impl aztec::note::note_interface::NoteType for $name {\n fn get_id() -> Field {\n $note_type_id\n }\n }\n }\n}\n\n/// Generates default `NoteHash` trait implementation for a given note struct `s` and returns it as a quote.\n///\n/// # Generated Implementation\n/// ```\n/// impl NoteHash for NoteStruct {\n/// fn compute_note_hash(self, storage_slot: Field) -> Field { ... }\n///\n/// fn compute_nullifier(self, context: &mut PrivateContext, note_hash_for_nullify: Field) -> Field { ... }\n///\n/// unconstrained fn compute_nullifier_unconstrained(note_hash_for_nullify: Field) -> Field { ... }\n/// }\n/// ```\ncomptime fn generate_note_hash_trait_impl(s: TypeDefinition) -> Quoted {\n let name = s.name();\n\n quote {\n impl aztec::note::note_interface::NoteHash for $name {\n fn compute_note_hash(self, storage_slot: Field) -> Field {\n let inputs = aztec::protocol_types::utils::arrays::array_concat(aztec::protocol_types::traits::Packable::pack(self), [storage_slot]);\n aztec::protocol_types::hash::poseidon2_hash_with_separator(inputs, aztec::protocol_types::constants::GENERATOR_INDEX__NOTE_HASH)\n }\n\n fn compute_nullifier(\n self,\n context: &mut aztec::prelude::PrivateContext,\n note_hash_for_nullify: Field,\n ) -> Field {\n let owner_npk_m = aztec::keys::getters::get_public_keys(self.owner).npk_m;\n // We invoke hash as a static trait function rather than calling owner_npk_m.hash() directly\n // in the quote to avoid \"trait not in scope\" compiler warnings.\n let owner_npk_m_hash = aztec::protocol_types::traits::Hash::hash(owner_npk_m);\n let secret = context.request_nsk_app(owner_npk_m_hash);\n aztec::protocol_types::hash::poseidon2_hash_with_separator(\n [note_hash_for_nullify, secret],\n aztec::protocol_types::constants::GENERATOR_INDEX__NOTE_NULLIFIER as Field,\n )\n }\n\n unconstrained fn compute_nullifier_unconstrained(\n self,\n note_hash_for_nullify: Field,\n ) -> Field {\n let owner_npk_m = aztec::keys::getters::get_public_keys(self.owner).npk_m;\n // We invoke hash as a static trait function rather than calling owner_npk_m.hash() directly\n // in the quote to avoid \"trait not in scope\" compiler warnings.\n let owner_npk_m_hash = aztec::protocol_types::traits::Hash::hash(owner_npk_m);\n let secret = aztec::keys::getters::get_nsk_app(owner_npk_m_hash);\n aztec::protocol_types::hash::poseidon2_hash_with_separator(\n [note_hash_for_nullify, secret],\n aztec::protocol_types::constants::GENERATOR_INDEX__NOTE_NULLIFIER as Field,\n )\n }\n }\n }\n}\n\n/// Generates note properties struct for a given note struct `s`.\n///\n/// Example:\n/// ```\n/// struct TokenNoteProperties {\n/// amount: aztec::note::note_getter_options::PropertySelector,\n/// npk_m_hash: aztec::note::note_getter_options::PropertySelector\n/// randomness: aztec::note::note_getter_options::PropertySelector\n/// }\n///\n/// impl aztec::note::note_interface::NoteProperties for TokenNote {\n/// fn properties() -> TokenNoteProperties {\n/// Self {\n/// amount: aztec::note::note_getter_options::PropertySelector { index: 0, offset: 0, length: 32 },\n/// npk_m_hash: aztec::note::note_getter_options::PropertySelector { index: 1, offset: 0, length: 32 },\n/// randomness: aztec::note::note_getter_options::PropertySelector { index: 2, offset: 0, length: 32 }\n/// }\n/// }\n/// }\n/// ```\ncomptime fn generate_note_properties(s: TypeDefinition) -> Quoted {\n let name = s.name();\n\n let struct_name = f\"{name}Properties\".quoted_contents();\n\n let property_selector_type = type_of(PropertySelector { index: 0, offset: 0, length: 0 });\n\n let note_fields = s.fields_as_written();\n\n let properties_types = note_fields\n .map(|(name, _): (Quoted, Type)| quote { pub $name: $property_selector_type })\n .join(quote {,});\n\n // TODO #8694: Properly handle non-field types https://github.com/AztecProtocol/aztec-packages/issues/8694\n let mut properties_list = &[];\n for i in 0..note_fields.len() {\n let (name, _) = note_fields[i];\n properties_list = properties_list.push_back(\n quote { $name: aztec::note::note_getter_options::PropertySelector { index: $i, offset: 0, length: 32 } },\n );\n }\n\n let properties = properties_list.join(quote {,});\n\n quote {\n pub struct $struct_name {\n $properties_types\n }\n\n impl aztec::note::note_interface::NoteProperties<$struct_name> for $name {\n fn properties() -> $struct_name {\n $struct_name {\n $properties\n }\n }\n }\n }\n}\n\n/// Generates note export for a given note struct `s`. The export is a global variable that contains note type id,\n/// note name and information about note fields (field name, index and whether the field is nullable or not).\n///\n/// Example:\n/// ```\n/// struct TokenNoteFields_5695262104 {\n/// amount: aztec::note::note_field::NoteField,\n/// owner: aztec::note::note_field::NoteField\n/// }\n///\n/// #[abi(notes)]\n/// global TokenNote_EXPORTS_5695262104: (Field, str<8>, TokenNoteFields_5695262104) = (\n/// 0,\n/// \"TokenNote\",\n/// TokenNoteFields_5695262104 {\n/// amount: aztec::note::note_field::NoteField { index: 0, nullable: false },\n/// owner: aztec::note::note_field::NoteField { index: 1, nullable: false }\n/// }\n/// );\n///\n/// Randomly looking value at the end of the export name is generated by hashing the note struct type and is included\n/// to prevent naming collisions in case there are multiple notes with the same name imported in a contract.\npub(crate) comptime fn generate_note_export(\n s: TypeDefinition,\n note_type_id: Field,\n fields: [(Quoted, u32, bool)],\n) -> Quoted {\n let name = s.name();\n let mut hasher = Poseidon2Hasher::default();\n s.as_type().hash(&mut hasher);\n let hash = hasher.finish() as u32;\n let global_export_name = f\"{name}_EXPORTS_{hash}\".quoted_contents();\n let note_fields_name = f\"{name}Fields_{hash}\".quoted_contents();\n let (note_name_as_str, _) = name.as_str_quote();\n let note_name_str_len = unquote!(quote { $note_name_as_str.as_bytes().len() });\n\n let mut note_fields = &[];\n let mut note_field_constructors = &[];\n for field in fields {\n let (name, index, nullable) = field;\n note_fields = note_fields.push_back(quote { $name: aztec::note::note_field::NoteField });\n note_field_constructors = note_field_constructors.push_back(\n quote { $name: aztec::note::note_field::NoteField { index: $index, nullable: $nullable }},\n );\n }\n\n let note_fields = note_fields.join(quote {,});\n let note_field_constructors = note_field_constructors.join(quote {,});\n\n quote {\n pub struct $note_fields_name {\n pub $note_fields\n }\n\n #[abi(notes)]\n global $global_export_name: (Field, str<$note_name_str_len>, $note_fields_name) = ($note_type_id, $note_name_as_str, $note_fields_name { $note_field_constructors });\n }\n}\n\n/// Registers a note struct `note` with the given `note_packed_len`, `note_type_id`, `fixed_fields` and\n/// `nullable_fields` in the global `NOTES` map.\ncomptime fn register_note(\n note: TypeDefinition,\n note_packed_len: u32,\n note_type_id: Field,\n fixed_fields: [(Quoted, Type, u32)],\n nullable_fields: [(Quoted, Type, u32)],\n) {\n let mut fields = &[];\n for field in fixed_fields {\n let (name, _, index) = field;\n fields = fields.push_back((name, index, false));\n }\n for field in nullable_fields {\n let (name, _, index) = field;\n fields = fields.push_back((name, index, true));\n }\n\n NOTES.insert(note.as_type(), (note, note_packed_len, note_type_id, fields));\n}\n\n/// Separates note struct members into fixed and nullable ones. It also stores the index of where each struct member\n/// starts in the serialized note. Note that each struct member can occupy multiple fields (as in Field type).\ncomptime fn index_note_fields(\n s: TypeDefinition,\n nullable_fields: [Quoted],\n) -> ([(Quoted, Type, u32)], [(Quoted, Type, u32)]) {\n let mut indexed_fixed_fields: [(Quoted, Type, u32)] = &[];\n let mut indexed_nullable_fields = &[];\n let mut counter: u32 = 0;\n for field in s.fields_as_written() {\n let (name, typ) = field;\n if nullable_fields.all(|field| field != name) {\n indexed_fixed_fields = indexed_fixed_fields.push_back((name, typ, counter));\n } else {\n indexed_nullable_fields = indexed_nullable_fields.push_back((name, typ, counter));\n }\n let (serialization_fields, _) = generate_serialize_to_fields(name, typ, true);\n // Each struct member can occupy multiple fields so we need to increment the counter accordingly\n counter += serialization_fields.len();\n }\n (indexed_fixed_fields, indexed_nullable_fields)\n}\n\n/// Generates the following:\n/// - NoteTypeProperties\n/// - NoteType trait implementation\n/// - NoteHash trait implementation\n/// - Packable implementation\n///\n/// Registers the note in the global `NOTES` map.\n///\n/// For more details on the generated code, see the individual functions.\npub comptime fn note(s: TypeDefinition) -> Quoted {\n assert_has_owner(s);\n\n let (indexed_fixed_fields, indexed_nullable_fields) = index_note_fields(s, &[]);\n\n let note_properties = generate_note_properties(s);\n let note_type_id = get_next_note_type_id();\n let note_interface_impl = generate_note_interface(s, note_type_id);\n let note_hash_impl = generate_note_hash_trait_impl(s);\n let (packable_impl, note_packed_len) = derive_packable_if_not_implemented_and_get_len(s);\n\n register_note(\n s,\n note_packed_len,\n note_type_id,\n indexed_fixed_fields,\n indexed_nullable_fields,\n );\n\n quote {\n $note_properties\n $note_interface_impl\n $note_hash_impl\n $packable_impl\n }\n}\n\n/// Generates code for a custom note implementation that requires specialized note hash or nullifier computation.\n///\n/// # Generated Code\n/// - NoteTypeProperties: Defines the structure and properties of note fields\n/// - NoteType trait implementation: Provides the note type ID\n/// - Packable implementation: Enables serialization/deserialization of the note\n///\n/// # Registration\n/// Registers the note in the global `NOTES` map with:\n/// - Note type ID\n/// - Packed length\n/// - Field indices and nullability\n///\n/// # Use Cases\n/// Use this macro when implementing a note that needs custom:\n/// - Note hash computation logic\n/// - Nullifier computation logic\n///\n/// The macro omits generating default NoteHash trait implementation, allowing you to provide your own.\n///\n/// # Example\n/// ```\n/// #[custom_note]\n/// struct CustomNote {\n/// value: Field,\n/// metadata: Field\n/// }\n///\n/// impl NoteHash for CustomNote {\n/// // Custom note hash computation...\n/// fn compute_note_hash(...) -> Field { ... }\n///\n/// // Custom nullifier computation...\n/// fn compute_nullifier(...) -> Field { ... }\n/// fn compute_nullifier_unconstrained(...) -> Field { ... }\n/// }\n/// ```\npub comptime fn custom_note(s: TypeDefinition) -> Quoted {\n let (packable_impl, note_packed_len) = derive_packable_if_not_implemented_and_get_len(s);\n let note_type_id = get_next_note_type_id();\n\n let (indexed_fixed_fields, indexed_nullable_fields) = index_note_fields(s, &[]);\n register_note(\n s,\n note_packed_len,\n note_type_id,\n indexed_fixed_fields,\n indexed_nullable_fields,\n );\n\n let note_properties = generate_note_properties(s);\n let note_interface_impl = generate_note_interface(s, note_type_id);\n\n quote {\n $note_properties\n $note_interface_impl\n $packable_impl\n }\n}\n\n/// Asserts that the note has an 'owner' field.\n///\n/// We require notes implemented with #[note] macro macro to have an 'owner' field because our\n/// auto-generated nullifier functions expect it. This requirement is most likely only temporary.\ncomptime fn assert_has_owner(note: TypeDefinition) {\n let fields = note.fields_as_written();\n let mut has_owner = false;\n for i in 0..fields.len() {\n let (field_name, _) = fields[i];\n if field_name == quote { owner } {\n has_owner = true;\n break;\n }\n }\n assert(\n has_owner,\n \"Note must have an 'owner' field. If your notes have no owner, use #[custom_note] insteadof #[note] and implement the NoteHashing trait manually.\",\n );\n}\n" - }, - "98": { - "path": "/mnt/user-data/saleel/nargo/github.com/AztecProtocol/aztec-packages/v0.87.4/noir-projects/aztec-nr/aztec/src/macros/storage.nr", - "source": "use poseidon::poseidon2::Poseidon2Hasher;\nuse std::{collections::umap::UHashMap, hash::BuildHasherDefault};\n\nuse super::utils::AsStrQuote;\nuse super::utils::get_storage_size;\n\n/// Stores a map from a module to the name of the struct that describes its storage layout.\n/// This is then used when generating a `storage_layout()` getter on the contract struct.\npub comptime mut global STORAGE_LAYOUT_NAME: UHashMap> =\n UHashMap::default();\n\n/// Marks a struct as the one describing the storage layout of a contract.\n///\n/// The contract's storage is accessed via the `storage` variable, which will will automatically be made available in\n/// all functions as an instance of the struct this macro was applied to.\n///\n/// Only a single struct in the entire contract should have this macro (or `storage_no_init`) applied to it, and the\n/// struct has to be called 'Storage'.\npub comptime fn storage(s: TypeDefinition) -> Quoted {\n let struct_name = s.name();\n if struct_name != quote { Storage } {\n panic(\n f\"The #[storage] macro can only be applied to a struct with name 'Storage', got '{struct_name}' instead.\",\n )\n }\n\n assert(\n !s.has_named_attribute(\"storage_no_init\"),\n f\"Only one of #[storage] and #[storage_no_init] can be applied to the Storage struct.\",\n );\n\n // This macro performs three things:\n // - it marks the contract as having storage, so that `macros::utils::module_has_storage` will return true and\n // functions will have the storage variable injected and initialized via the `init` function.\n // - it implements said `init` function by allocating appropriate storage slots to each state variable.\n // - it exposes the storage layout by creating a `StorageLayout` struct that is exposed via the `abi(storage)`\n // macro.\n let mut slot: u32 = 1;\n let mut storage_vars_constructors = &[];\n let mut storage_layout_fields = &[];\n let mut storage_layout_constructors = &[];\n\n // TODO(#8658): uncomment the code below to inject the Context type parameter.\n //let mut new_storage_fields = &[];\n //let context_generic = s.add_generic(\"Context\");\n for field in s.fields_as_written() {\n // FIXME: This doesn't handle field types with generics\n let (name, typ) = field;\n let (storage_field_constructor, storage_size) =\n generate_storage_field_constructor(typ, quote { $slot });\n storage_vars_constructors =\n storage_vars_constructors.push_back(quote { $name: $storage_field_constructor });\n // We have `Storable` in a separate `.nr` file instead of defining it in the last quote of this function\n // because that way a dev gets a more reasonable error if he defines a struct with the same name in\n // a contract.\n storage_layout_fields =\n storage_layout_fields.push_back(quote { pub $name: dep::aztec::prelude::Storable });\n storage_layout_constructors = storage_layout_constructors.push_back(\n quote { $name: dep::aztec::prelude::Storable { slot: $slot } },\n );\n //let with_context_generic = add_context_generic(typ, context_generic);\n //println(with_context_generic);\n //new_storage_fields = new_storage_fields.push_back((name, with_context_generic ));\n slot += storage_size;\n }\n\n //s.set_fields(new_storage_fields);\n let storage_vars_constructors = storage_vars_constructors.join(quote {,});\n let storage_impl = quote {\n impl Storage {\n fn init(context: Context) -> Self {\n Self {\n $storage_vars_constructors\n }\n }\n }\n };\n\n let storage_layout_fields = storage_layout_fields.join(quote {,});\n let storage_layout_constructors = storage_layout_constructors.join(quote {,});\n\n let module = s.module();\n let module_name = module.name();\n let storage_layout_name = f\"STORAGE_LAYOUT_{module_name}\".quoted_contents();\n let (module_name_str, module_name_len) = module_name.as_str_quote();\n STORAGE_LAYOUT_NAME.insert(module, storage_layout_name);\n\n quote {\n $storage_impl\n\n pub struct StorageLayoutFields {\n $storage_layout_fields\n }\n\n pub struct StorageLayout {\n pub contract_name: str,\n pub fields: StorageLayoutFields\n }\n\n #[abi(storage)]\n pub global $storage_layout_name: StorageLayout<$module_name_len> = StorageLayout {\n contract_name: $module_name_str,\n fields: StorageLayoutFields { $storage_layout_constructors }\n };\n }\n}\n\n/// Same as `storage`, except the user is in charge of providing an implementation of the `init` constructor function\n/// with signature `fn init(context: Context) -> Self`, which allows for manual control of storage slot\n/// allocation. Similarly, no `StorageLayout` struct will be created.\n///\n/// The contract's storage is accessed via the `storage` variable, which will will automatically be made available in\n/// all functions as an instance of the struct this macro was applied to.\n///\n/// Only a single struct in the entire contract can have this macro (or storage_no_init) applied to it, and the struct\n/// has to be called 'Storage'.\npub comptime fn storage_no_init(s: TypeDefinition) {\n // All `storage` does is provide the `init` implementation, so we don't need to do anything here. Applying this\n // macro however will cause for `macros::utils::module_has_storage` to return true, resulting in the injection of\n // the `storage` variable.\n\n // We do need to make sure that the type is called Storage, since we'll do `Storage::init` later on.\n\n if s.name() != quote { Storage } {\n let name = s.name();\n panic(\n f\"The #[storage_no_init] macro can only be applied to a struct with name 'Storage', got '{name}' instead.\",\n )\n }\n\n assert(\n !s.has_named_attribute(\"storage\"),\n f\"Only one of #[storage] and #[storage_no_init] can be applied to the Storage struct.\",\n );\n}\n\n/// Returns the expression required to initialize a state variable with a given slot, along with its serialization size,\n/// i.e. how many contiguous storage slots the variable requires.\ncomptime fn generate_storage_field_constructor(typ: Type, slot: Quoted) -> (Quoted, u32) {\n assert(\n typ.as_data_type().is_some(),\n \"Storage containers must be generic structs of the form `Container<_, Context>`, or Map\",\n );\n let (container_struct, generics) = typ.as_data_type().unwrap();\n let struct_name = container_struct.name();\n\n let constructor = if is_storage_map(typ) {\n // Map state variables recursively initialize their contents - this includes nested maps.\n let (value_constructor, _) =\n generate_storage_field_constructor(generics[1], quote { slot });\n\n quote { $struct_name::new(context, $slot, | context, slot | { $value_constructor }) }\n } else {\n // We assume below that all state variables implement `fn new(context: Context, slot: Field) -> Self`.\n quote { $struct_name::new(context, $slot)}\n };\n\n (constructor, get_storage_size(typ))\n}\n\n/// Returns true if `typ` is `state_vars::map::Map`.\ncomptime fn is_storage_map(typ: Type) -> bool {\n if typ.as_data_type().is_some() {\n let (def, generics) = typ.as_data_type().unwrap();\n let maybe_map = if (def.name() == quote { Map }) & (generics.len() == 3) {\n let maybe_key = generics[0];\n let maybe_value = generics[1];\n let maybe_context = generics[2];\n quote { crate::state_vars::map::Map<$maybe_key, $maybe_value, $maybe_context> }.as_type()\n } else {\n quote {()}.as_type()\n };\n typ == maybe_map\n } else {\n false\n }\n}\n\ncomptime fn add_context_generic(typ: Type, context_generic: Type) -> Type {\n let (def, mut generics) = typ.as_data_type().expect(\n f\"Storage containers must be generic structs of the form `Container<..., Context>`\",\n );\n let name = def.name();\n\n if is_storage_map(typ) {\n generics[generics.len() - 2] = add_context_generic(generics[1], context_generic);\n generics[generics.len() - 1] = context_generic;\n } else {\n generics[generics.len() - 1] = context_generic;\n }\n\n let generics = generics.map(|typ: Type| quote {$typ}).join(quote {,});\n quote { $name<$generics> }.as_type()\n}\n" - } - } -} diff --git a/app/embedded-wallet.ts b/app/embedded-wallet.ts index 2ca9a57..0443ee2 100644 --- a/app/embedded-wallet.ts +++ b/app/embedded-wallet.ts @@ -3,7 +3,7 @@ import { createLogger, createAztecNodeClient, AztecAddress, - getContractInstanceFromDeployParams, + getContractInstanceFromInstantiationParams, ContractFunctionInteraction, SponsoredFeePaymentMethod, type PXE, @@ -16,7 +16,10 @@ import { getEcdsaRAccount } from '@aztec/accounts/ecdsa/lazy'; import { getSchnorrAccount } from '@aztec/accounts/schnorr/lazy'; import { getPXEServiceConfig } from '@aztec/pxe/config'; import { createPXEService } from '@aztec/pxe/client/lazy'; -import { type ContractArtifact, getDefaultInitializer } from '@aztec/stdlib/abi'; +import { + type ContractArtifact, + getDefaultInitializer, +} from '@aztec/stdlib/abi'; import { getInitialTestAccounts } from '@aztec/accounts/testing'; const PROVER_ENABLED = true; @@ -31,7 +34,7 @@ export class EmbeddedWallet { private pxe!: PXE; connectedAccount: AccountWallet | null = null; - constructor(private nodeUrl: string) {} + constructor(private nodeUrl: string) { } async initialize() { // Create Aztec Node Client @@ -41,7 +44,9 @@ export class EmbeddedWallet { const config = getPXEServiceConfig(); config.l1Contracts = await aztecNode.getL1ContractAddresses(); config.proverEnabled = PROVER_ENABLED; - this.pxe = await createPXEService(aztecNode, config); + this.pxe = await createPXEService(aztecNode, config, { + useLogSuffix: true, + }); // Register Sponsored FPC Contract with PXE await this.pxe.registerContract({ @@ -56,7 +61,7 @@ export class EmbeddedWallet { // Internal method to use the Sponsored FPC Contract for fee payment async #getSponsoredPFCContract() { - const instance = await getContractInstanceFromDeployParams( + const instance = await getContractInstanceFromInstantiationParams( SponsoredFPCContractArtifact, { salt: new Fr(SPONSORED_FPC_SALT), @@ -76,7 +81,12 @@ export class EmbeddedWallet { async connectTestAccount(index: number) { const testAccounts = await getInitialTestAccounts(); const account = testAccounts[index]; - const schnorrAccount = await getSchnorrAccount(this.pxe, account.secret, account.signingKey, account.salt); + const schnorrAccount = await getSchnorrAccount( + this.pxe, + account.secret, + account.signingKey, + account.salt + ); await schnorrAccount.register(); const wallet = await schnorrAccount.getWallet(); @@ -108,6 +118,7 @@ export class EmbeddedWallet { const deployMethod = await ecdsaAccount.getDeployMethod(); const sponsoredPFCContract = await this.#getSponsoredPFCContract(); const deployOpts = { + from: AztecAddress.ZERO, contractAddressSalt: Fr.fromString(ecdsaAccount.salt.toString()), fee: { paymentMethod: await ecdsaAccount.getSelfPaymentMethod( @@ -115,8 +126,8 @@ export class EmbeddedWallet { ), }, universalDeploy: true, - skipClassRegistration: true, - skipPublicDeployment: true, + skipClassPublication: true, + skipInstancePublication: true, }; const provenInteraction = await deployMethod.prove(deployOpts); @@ -172,12 +183,15 @@ export class EmbeddedWallet { deploymentSalt: Fr, constructorArgs: any[] ) { - const instance = await getContractInstanceFromDeployParams(artifact, { - constructorArtifact: getDefaultInitializer(artifact), - constructorArgs: constructorArgs, - deployer: deployer, - salt: deploymentSalt, - }); + const instance = await getContractInstanceFromInstantiationParams( + artifact, + { + constructorArtifact: getDefaultInitializer(artifact), + constructorArgs: constructorArgs, + deployer: deployer, + salt: deploymentSalt, + } + ); await this.pxe.registerContract({ instance, @@ -189,6 +203,7 @@ export class EmbeddedWallet { async sendTransaction(interaction: ContractFunctionInteraction) { const sponsoredPFCContract = await this.#getSponsoredPFCContract(); const provenInteraction = await interaction.prove({ + from: this.connectedAccount.getAddress(), fee: { paymentMethod: new SponsoredFeePaymentMethod( sponsoredPFCContract.address @@ -201,7 +216,9 @@ export class EmbeddedWallet { // Simulate a transaction async simulateTransaction(interaction: ContractFunctionInteraction) { - const res = await interaction.simulate(); + const res = await interaction.simulate({ + from: this.connectedAccount.getAddress(), + }); return res; } -} +} \ No newline at end of file diff --git a/app/main.ts b/app/main.ts index 7e77bf3..bfc4f72 100644 --- a/app/main.ts +++ b/app/main.ts @@ -6,7 +6,7 @@ import { type AccountWallet, } from '@aztec/aztec.js'; import { EmbeddedWallet } from './embedded-wallet'; -import { EasyPrivateVotingContract } from './artifacts/EasyPrivateVoting'; +import { PrivateVotingContract } from './artifacts/PrivateVoting'; // DOM Elements const createAccountButton = @@ -45,7 +45,7 @@ document.addEventListener('DOMContentLoaded', async () => { // Register voting contract with wallet/PXE displayStatusMessage('Registering contracts...'); await wallet.registerContract( - EasyPrivateVotingContract.artifact, + PrivateVotingContract.artifact, AztecAddress.fromString(deployerAddress), Fr.fromString(deploymentSalt), [AztecAddress.fromString(deployerAddress)] @@ -145,7 +145,7 @@ voteButton.addEventListener('click', async (e) => { } // Prepare contract interaction - const votingContract = await EasyPrivateVotingContract.at( + const votingContract = await PrivateVotingContract.at( AztecAddress.fromString(contractAddress), connectedAccount ); @@ -177,7 +177,7 @@ async function updateVoteTally(account: Wallet) { displayStatusMessage('Updating vote tally...'); // Prepare contract interaction - const votingContract = await EasyPrivateVotingContract.at( + const votingContract = await PrivateVotingContract.at( AztecAddress.fromString(contractAddress), account ); diff --git a/contracts/Nargo.toml b/contracts/Nargo.toml index 730a874..d7c4edf 100644 --- a/contracts/Nargo.toml +++ b/contracts/Nargo.toml @@ -3,6 +3,6 @@ name = "private_voting" type = "contract" [dependencies] -aztec = { git="https://github.com/AztecProtocol/aztec-packages/", tag="v0.87.4", directory="noir-projects/aztec-nr/aztec" } -value_note = { git="https://github.com/AztecProtocol/aztec-packages/", tag="v0.87.4", directory="noir-projects/aztec-nr/value-note"} -easy_private_state = { git="https://github.com/AztecProtocol/aztec-packages/", tag="v0.87.4", directory="noir-projects/aztec-nr/easy-private-state"} +aztec = { git="https://github.com/AztecProtocol/aztec-packages/", tag="v2.0.2", directory="noir-projects/aztec-nr/aztec" } +value_note = { git="https://github.com/AztecProtocol/aztec-packages/", tag="v2.0.2", directory="noir-projects/aztec-nr/value-note"} +easy_private_state = { git="https://github.com/AztecProtocol/aztec-packages/", tag="v2.0.2", directory="noir-projects/aztec-nr/easy-private-state"} diff --git a/contracts/src/main.nr b/contracts/src/main.nr index e38162c..f99f495 100644 --- a/contracts/src/main.nr +++ b/contracts/src/main.nr @@ -1,14 +1,14 @@ use dep::aztec::macros::aztec; #[aztec] -pub contract EasyPrivateVoting { - use dep::aztec:: macros::{ - functions::{initializer, internal, private, public, utility}, - storage::storage - }; +pub contract PrivateVoting { use dep::aztec::keys::getters::get_public_keys; - use dep::aztec::prelude::{AztecAddress, Map, PublicImmutable, PublicMutable}; - use dep::aztec::protocol_types::traits::{Hash, ToField}; + use dep::aztec::macros::{ + functions::{initializer, internal, private, public, utility}, + storage::storage, + }; + use dep::aztec::protocol_types::{address::AztecAddress, traits::{Hash, ToField}}; + use dep::aztec::state_vars::{Map, PublicImmutable, PublicMutable}; #[storage] struct Storage { @@ -23,7 +23,7 @@ pub contract EasyPrivateVoting { fn constructor(admin: AztecAddress) { storage.admin.write(admin); storage.vote_ended.write(false); - storage.active_at_block.initialize(context.block_number() as u32); + storage.active_at_block.initialize(context.block_number()); } #[private] @@ -33,7 +33,7 @@ pub contract EasyPrivateVoting { let secret = context.request_nsk_app(msg_sender_npk_m_hash); // get secret key of caller of function let nullifier = std::hash::pedersen_hash([context.msg_sender().to_field(), secret]); // derive nullifier from sender and secret context.push_nullifier(nullifier); - EasyPrivateVoting::at(context.this_address()).add_to_tally_public(candidate).enqueue( + PrivateVoting::at(context.this_address()).add_to_tally_public(candidate).enqueue( &mut context, ); } @@ -56,4 +56,4 @@ pub contract EasyPrivateVoting { unconstrained fn get_vote(candidate: Field) -> Field { storage.tally.at(candidate).read() } -} \ No newline at end of file +} diff --git a/package.json b/package.json index dbabc2f..fd80a33 100644 --- a/package.json +++ b/package.json @@ -8,9 +8,9 @@ }, "scripts": { "clean": "rm -rf app/dist contracts/target contracts/codegenCache.json", - "compile-contracts": "cd contracts && ${AZTEC_NARGO:-aztec-nargo} compile", + "compile-contracts": "cd contracts && ${AZTEC_NARGO:-aztec-nargo} compile && ${AZTEC_POSTPROCESS:-aztec-postprocess-contract}", "codegen-contracts": "cd contracts && ${AZTEC_BUILDER:-aztec} codegen ./target -o ./target", - "copy-artifacts": "cp contracts/target/*.json contracts/target/*.ts app/artifacts", + "copy-artifacts": "mkdir -p app/artifacts && cp contracts/target/*.json contracts/target/*.ts app/artifacts", "build-contracts": "yarn clean && yarn compile-contracts && yarn codegen-contracts && yarn copy-artifacts", "deploy-contracts": "node --experimental-transform-types scripts/deploy.ts", "dev": "webpack serve --mode development", @@ -22,14 +22,14 @@ "lint": "prettier --check ./src" }, "dependencies": { - "@aztec/accounts": "0.87.4", - "@aztec/aztec.js": "0.87.4", - "@aztec/constants": "0.87.4", - "@aztec/foundation": "0.87.4", - "@aztec/kv-store": "0.87.4", - "@aztec/noir-contracts.js": "0.87.4", - "@aztec/pxe": "0.87.4", - "@aztec/stdlib": "0.87.4" + "@aztec/accounts": "2.0.2", + "@aztec/aztec.js": "2.0.2", + "@aztec/constants": "2.0.2", + "@aztec/foundation": "2.0.2", + "@aztec/kv-store": "2.0.2", + "@aztec/noir-contracts.js": "2.0.2", + "@aztec/pxe": "2.0.2", + "@aztec/stdlib": "2.0.2" }, "devDependencies": { "@playwright/test": "1.52.0", diff --git a/playwright.config.ts b/playwright.config.ts index 59f5010..bf666f3 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -17,7 +17,7 @@ export default defineConfig({ expect: { timeout: 20_000, }, - timeout: 400_000, + timeout: 1_600_000, projects: [ { name: 'webkit', diff --git a/scripts/deploy.ts b/scripts/deploy.ts index fef817c..7e2bdc4 100644 --- a/scripts/deploy.ts +++ b/scripts/deploy.ts @@ -5,7 +5,7 @@ import { createAztecNodeClient, DeployMethod, Fr, - getContractInstanceFromDeployParams, + getContractInstanceFromInstantiationParams, PublicKeys, type PXE, SponsoredFeePaymentMethod, @@ -18,7 +18,7 @@ import { getDefaultInitializer } from '@aztec/stdlib/abi'; import { SponsoredFPCContractArtifact } from '@aztec/noir-contracts.js/SponsoredFPC'; import { SPONSORED_FPC_SALT } from '@aztec/constants'; // @ts-ignore -import { EasyPrivateVotingContract } from '../app/artifacts/EasyPrivateVoting.ts'; +import { PrivateVotingContract } from '../app/artifacts/PrivateVoting.ts'; const AZTEC_NODE_URL = process.env.AZTEC_NODE_URL || 'http://localhost:8080'; const PROVER_ENABLED = process.env.PROVER_ENABLED === 'false' ? false : true; @@ -43,19 +43,15 @@ async function setupPXE() { ...config, }; - const pxe = await createPXEService( - aztecNode, - configWithContracts, - { - store, - useLogSuffix: true, - }, - ); + const pxe = await createPXEService(aztecNode, configWithContracts, { + store, + useLogSuffix: true, + }); return pxe; } async function getSponsoredPFCContract() { - const instance = await getContractInstanceFromDeployParams( + const instance = await getContractInstanceFromInstantiationParams( SponsoredFPCContractArtifact, { salt: new Fr(SPONSORED_FPC_SALT), @@ -74,6 +70,7 @@ async function createAccount(pxe: PXE) { const deployMethod = await ecdsaAccount.getDeployMethod(); const sponsoredPFCContract = await getSponsoredPFCContract(); const deployOpts = { + from: AztecAddress.ZERO, contractAddressSalt: Fr.fromString(ecdsaAccount.salt.toString()), fee: { paymentMethod: await ecdsaAccount.getSelfPaymentMethod( @@ -81,8 +78,8 @@ async function createAccount(pxe: PXE) { ), }, universalDeploy: true, - skipClassRegistration: true, - skipPublicDeployment: true, + skipClassPublication: true, + skipInstancePublication: true, }; const provenInteraction = await deployMethod.prove(deployOpts); await provenInteraction.send().wait({ timeout: 120 }); @@ -98,12 +95,12 @@ async function createAccount(pxe: PXE) { async function deployContract(pxe: PXE, deployer: Wallet) { const salt = Fr.random(); - const contract = await getContractInstanceFromDeployParams( - EasyPrivateVotingContract.artifact, + const contract = await getContractInstanceFromInstantiationParams( + PrivateVotingContract.artifact, { publicKeys: PublicKeys.default(), constructorArtifact: getDefaultInitializer( - EasyPrivateVotingContract.artifact + PrivateVotingContract.artifact ), constructorArgs: [deployer.getAddress().toField()], deployer: deployer.getAddress(), @@ -114,16 +111,17 @@ async function deployContract(pxe: PXE, deployer: Wallet) { const deployMethod = new DeployMethod( contract.publicKeys, deployer, - EasyPrivateVotingContract.artifact, + PrivateVotingContract.artifact, (address: AztecAddress, wallet: Wallet) => - EasyPrivateVotingContract.at(address, wallet), + PrivateVotingContract.at(address, wallet), [deployer.getAddress().toField()], - getDefaultInitializer(EasyPrivateVotingContract.artifact)?.name + getDefaultInitializer(PrivateVotingContract.artifact)?.name ); const sponsoredPFCContract = await getSponsoredPFCContract(); const provenInteraction = await deployMethod.prove({ + from: deployer.getAddress(), contractAddressSalt: salt, fee: { paymentMethod: new SponsoredFeePaymentMethod( @@ -134,7 +132,7 @@ async function deployContract(pxe: PXE, deployer: Wallet) { await provenInteraction.send().wait({ timeout: 120 }); await pxe.registerContract({ instance: contract, - artifact: EasyPrivateVotingContract.artifact, + artifact: PrivateVotingContract.artifact, }); return { @@ -175,7 +173,7 @@ async function createAccountAndDeployContract() { }); // Create a new account - const { wallet, /* signingKey */ } = await createAccount(pxe); + const { wallet /* signingKey */ } = await createAccount(pxe); // // Save the wallet info // const walletInfo = { @@ -207,4 +205,4 @@ createAccountAndDeployContract().catch((error) => { process.exit(1); }); -export { createAccountAndDeployContract }; +export { createAccountAndDeployContract }; \ No newline at end of file diff --git a/tests/e2e.spec.ts b/tests/e2e.spec.ts index 39cb3e3..85a2b7e 100644 --- a/tests/e2e.spec.ts +++ b/tests/e2e.spec.ts @@ -1,6 +1,6 @@ import { test, expect } from '@playwright/test'; -const proofTimeout = 300_000; +const proofTimeout = 1_200_000; test.beforeAll(async ({ }, { config }) => { // Make sure the node is running diff --git a/yarn.lock b/yarn.lock index aecf17e..a4f11f6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,59 +7,58 @@ resolved "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.11.0.tgz" integrity sha512-/3DDPKHqqIqxUULp8yP4zODUY1i+2xvVWsv8A79xGWdCAG+8sb0hRh0Rk2QyOJUnnbyPUAZYcpBuRe3nS2OIUg== -"@aztec/accounts@0.87.4": - version "0.87.4" - resolved "https://registry.npmjs.org/@aztec/accounts/-/accounts-0.87.4.tgz" - integrity sha512-TyNvw/0pKNy7MRNqMwlDQEihdiNZE7AbQk2eKZJgVw7JACJ4a/t/tgo4PIgBJEO7Mo2Ia+QguNt5BRiMV4Y7mA== - dependencies: - "@aztec/aztec.js" "0.87.4" - "@aztec/entrypoints" "0.87.4" - "@aztec/ethereum" "0.87.4" - "@aztec/foundation" "0.87.4" - "@aztec/stdlib" "0.87.4" +"@aztec/accounts@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@aztec/accounts/-/accounts-2.0.2.tgz#37986b54968324bc711211d471b81e006869627d" + integrity sha512-BZFQuRBCAJe+IZtlfASRk7Ae0x8B8decq+EhFvd258ShkVqw9uKoH4AKEQqQ3W1hJnJ/mnXRrpgt9Kdl/D67NA== + dependencies: + "@aztec/aztec.js" "2.0.2" + "@aztec/entrypoints" "2.0.2" + "@aztec/ethereum" "2.0.2" + "@aztec/foundation" "2.0.2" + "@aztec/stdlib" "2.0.2" tslib "^2.4.0" -"@aztec/aztec.js@0.87.4": - version "0.87.4" - resolved "https://registry.npmjs.org/@aztec/aztec.js/-/aztec.js-0.87.4.tgz" - integrity sha512-fggZAB0kh2sl/zUj0noSstwqGstF/WZPoI2ScF5KfIozujc9+CrsqyhBxp0LDtiXpQWn9RTfW1OMvd7SjWKrXg== - dependencies: - "@aztec/constants" "0.87.4" - "@aztec/entrypoints" "0.87.4" - "@aztec/ethereum" "0.87.4" - "@aztec/foundation" "0.87.4" - "@aztec/l1-artifacts" "0.87.4" - "@aztec/protocol-contracts" "0.87.4" - "@aztec/stdlib" "0.87.4" +"@aztec/aztec.js@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@aztec/aztec.js/-/aztec.js-2.0.2.tgz#12428a0b100ea22b06e36c477377631a98683bc1" + integrity sha512-BF6ua53EpERew9EMRONw8zuFRCTgqMk8WYI2rAJs/uMkYaQfkP5VFiZA69X3FYhnhHhYUf60sfGfCNXeNVTHDA== + dependencies: + "@aztec/constants" "2.0.2" + "@aztec/entrypoints" "2.0.2" + "@aztec/ethereum" "2.0.2" + "@aztec/foundation" "2.0.2" + "@aztec/l1-artifacts" "2.0.2" + "@aztec/protocol-contracts" "2.0.2" + "@aztec/stdlib" "2.0.2" axios "^1.8.2" tslib "^2.4.0" viem "2.23.7" -"@aztec/bb-prover@0.87.4": - version "0.87.4" - resolved "https://registry.npmjs.org/@aztec/bb-prover/-/bb-prover-0.87.4.tgz" - integrity sha512-jvJnv/rR6jelkDwq6MLQFsZcXFwh6gSHrDBJBMekVyBtzBsr3P9gsvi3ndFXy8KX/oFrKf4EDikW682CnEqHDg== - dependencies: - "@aztec/bb.js" "0.87.4" - "@aztec/constants" "0.87.4" - "@aztec/foundation" "0.87.4" - "@aztec/noir-noirc_abi" "0.87.4" - "@aztec/noir-protocol-circuits-types" "0.87.4" - "@aztec/noir-types" "0.87.4" - "@aztec/simulator" "0.87.4" - "@aztec/stdlib" "0.87.4" - "@aztec/telemetry-client" "0.87.4" - "@aztec/world-state" "0.87.4" +"@aztec/bb-prover@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@aztec/bb-prover/-/bb-prover-2.0.2.tgz#3ba2c3b561c02218936e5da779c559bd339fd246" + integrity sha512-0pcdOAx6KlbWyRJcmYoz2Z768H3z4Ay18MYNPzvLLGL8gmglGN9dEFS8ZsPxmUVxvi2K3Tw3vmMM/onYuHnLkA== + dependencies: + "@aztec/bb.js" "2.0.2" + "@aztec/constants" "2.0.2" + "@aztec/foundation" "2.0.2" + "@aztec/noir-noirc_abi" "2.0.2" + "@aztec/noir-protocol-circuits-types" "2.0.2" + "@aztec/noir-types" "2.0.2" + "@aztec/simulator" "2.0.2" + "@aztec/stdlib" "2.0.2" + "@aztec/telemetry-client" "2.0.2" + "@aztec/world-state" "2.0.2" commander "^12.1.0" pako "^2.1.0" - pidusage "^4.0.1" source-map-support "^0.5.21" tslib "^2.4.0" -"@aztec/bb.js@0.87.4": - version "0.87.4" - resolved "https://registry.npmjs.org/@aztec/bb.js/-/bb.js-0.87.4.tgz" - integrity sha512-PFZmgIJFSExzQZkNBa1DLuUx96BlC+Am6bo940ooMnqX9BpHeFoZsC7x+qmJOSc1rlbc56OUkLzf75wFgO+JeA== +"@aztec/bb.js@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@aztec/bb.js/-/bb.js-2.0.2.tgz#ffc72a1926437243c45ff75c9c9be96feea094fa" + integrity sha512-pBQLGU3aHKBXRGdbxwIBRUxAoTOR8x5WcVTB+Z4Ea/pE0cxlOsEK2ZeiBODev26ChvY/asQ663ZyBvJJM4JbGw== dependencies: comlink "^4.4.1" commander "^12.1.0" @@ -69,66 +68,69 @@ pino "^9.5.0" tslib "^2.4.0" -"@aztec/blob-lib@0.87.4": - version "0.87.4" - resolved "https://registry.npmjs.org/@aztec/blob-lib/-/blob-lib-0.87.4.tgz" - integrity sha512-yLuuLFYdWS8DaOHhT0NzkeGNgeLGjeyaGUqCv9vVtJrOX18CHcXcJiyyeNJrxBazVfAzga0VxdH+qvEMkaUKDw== +"@aztec/blob-lib@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@aztec/blob-lib/-/blob-lib-2.0.2.tgz#fdd011dfab4de6ff7428166ff858ddf29c08653e" + integrity sha512-v5n2QmsaRDZpIOIhI0zcNF5SxdkzmEiX6EvuW5s2Dal279jAGQ+LzCOhB+Z0/Albc1/7D8fRKaRV4a3uWMSzuA== dependencies: - "@aztec/constants" "0.87.4" - "@aztec/foundation" "0.87.4" + "@aztec/constants" "2.0.2" + "@aztec/foundation" "2.0.2" c-kzg "4.0.0-alpha.1" tslib "^2.4.0" -"@aztec/builder@0.87.4": - version "0.87.4" - resolved "https://registry.npmjs.org/@aztec/builder/-/builder-0.87.4.tgz" - integrity sha512-jDU5cbO1uB4sTQhLfq6ZtqGaYbWmQMB/EFRB1XdSpzt1iOIdAheafa+7AXkDiSUP4NEMtbWaeMxkX0ur93mb7Q== +"@aztec/builder@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@aztec/builder/-/builder-2.0.2.tgz#68a00be8de136c8eaf1dfc4a3f6739c92f13a055" + integrity sha512-92NLorOJ4NyVXJ2d3JzQvVB0PTVPu3yAWOyqkqt0Ybt11rFqGD6wGa/qg3+Yb7mRixrU9FCeTDnnyfU4Yr+TzA== dependencies: - "@aztec/foundation" "0.87.4" - "@aztec/stdlib" "0.87.4" + "@aztec/foundation" "2.0.2" + "@aztec/stdlib" "2.0.2" commander "^12.1.0" -"@aztec/constants@0.87.4": - version "0.87.4" - resolved "https://registry.npmjs.org/@aztec/constants/-/constants-0.87.4.tgz" - integrity sha512-1XTtFHEBLVqqykk787wjTOitejv+dM3DSKPnvC5t4OrwFng1h/tr/HFcUo3iMNitju54WwZLRTwYqfLLbH3sVA== +"@aztec/constants@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@aztec/constants/-/constants-2.0.2.tgz#e7c761d523327b06f5bc97992eeb10408378252f" + integrity sha512-fztICMy/1N9pBkpxAeaExEsX4buC1M03P4PbjSMkuw5UIk4z8fs01sF2kvHdSMngs3LA36AWcsCHXt2wfSJw4A== dependencies: tslib "^2.4.0" -"@aztec/entrypoints@0.87.4": - version "0.87.4" - resolved "https://registry.npmjs.org/@aztec/entrypoints/-/entrypoints-0.87.4.tgz" - integrity sha512-M5hbOJfbPBa7N+DgCNuKG4plgkiSRwytbDNFjQvTbVqEIgywVPdTZXAlDzWkd/ru3cV9cjoWuhXzI3X7IHOB1g== +"@aztec/entrypoints@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@aztec/entrypoints/-/entrypoints-2.0.2.tgz#e196f4283ef5a2fee29766e106bc01247b57c1e8" + integrity sha512-nj9Y94OB/VYBNlOvGGvGFBrXCmBFHtJppwmMg4zEHx9IKzmLZCyX0xQx9CfKFuEoW2dkmgqj5dwngP6cyAmNTw== dependencies: - "@aztec/constants" "0.87.4" - "@aztec/foundation" "0.87.4" - "@aztec/protocol-contracts" "0.87.4" - "@aztec/stdlib" "0.87.4" + "@aztec/constants" "2.0.2" + "@aztec/foundation" "2.0.2" + "@aztec/protocol-contracts" "2.0.2" + "@aztec/stdlib" "2.0.2" tslib "^2.4.0" -"@aztec/ethereum@0.87.4": - version "0.87.4" - resolved "https://registry.npmjs.org/@aztec/ethereum/-/ethereum-0.87.4.tgz" - integrity sha512-rPiOH9ziHMC06jzhknGglpMZkCcZE4GHTCJp+w/LqvnrfjysbB9ERb5/AWKWwscW5Vm7c9+L1qpS1iZlDkqB3g== +"@aztec/ethereum@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@aztec/ethereum/-/ethereum-2.0.2.tgz#05925c9c7d38584b6b6a81d9088a1f09890c50ce" + integrity sha512-3cIs9j+TzCacuJ0yZYaw6Ts8uAztaEEQfiOnwrmcyFdyYZrDYe52eBIWluzUO3MQBES0VBf0POSKFYIACuXhqA== dependencies: - "@aztec/blob-lib" "0.87.4" - "@aztec/foundation" "0.87.4" - "@aztec/l1-artifacts" "0.87.4" + "@aztec/blob-lib" "2.0.2" + "@aztec/constants" "2.0.2" + "@aztec/foundation" "2.0.2" + "@aztec/l1-artifacts" "2.0.2" "@viem/anvil" "^0.0.10" dotenv "^16.0.3" + lodash.chunk "^4.2.0" + lodash.pickby "^4.5.0" tslib "^2.4.0" viem "2.23.7" zod "^3.23.8" -"@aztec/foundation@0.87.4": - version "0.87.4" - resolved "https://registry.npmjs.org/@aztec/foundation/-/foundation-0.87.4.tgz" - integrity sha512-+a1wPQ8wG+o94LrD4RoukyV0vUegk6DCuGqOrIQhXdQCv/Iheoq2a259JlmP2Co3atymGlO5Ggk3dT/bD/fmYQ== +"@aztec/foundation@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@aztec/foundation/-/foundation-2.0.2.tgz#bd606aeadfc03705466be1264d9b829bbd3c9ee5" + integrity sha512-A48UexNylbOdtx71T5st1UmcF0PGR+7DutHBd3cnkxT6zApiuWoCrhJmXtUp4ld7ZdskZMjgcJr4ACtwQPXdhg== dependencies: - "@aztec/bb.js" "0.87.4" + "@aztec/bb.js" "2.0.2" "@koa/cors" "^5.0.0" - "@noble/curves" "^1.2.0" - c-kzg "4.0.0-alpha.1" + "@noble/curves" "=1.7.0" + bn.js "^5.2.1" colorette "^2.0.20" detect-node "^2.1.0" hash.js "^1.1.7" @@ -146,139 +148,140 @@ undici "^5.28.5" zod "^3.23.8" -"@aztec/key-store@0.87.4": - version "0.87.4" - resolved "https://registry.npmjs.org/@aztec/key-store/-/key-store-0.87.4.tgz" - integrity sha512-fFU7bp8yvTrZKIQZQ5sIZMu8UyA/M5D9vkQ19+Vg9TNN9UMN6Wp7QX5w2oEDKOUswoav5H7gaoTVvM6UT+7nwg== +"@aztec/key-store@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@aztec/key-store/-/key-store-2.0.2.tgz#b67ddc71ec504fd5b4169189037a0b1acbbf45b3" + integrity sha512-EFYRn3B9YVtWlwF6/WJ7rCkSOrKm1GcjW+W2Kk6Unof+4MYUuJQAuGlyrp3wWa3lmAFledNB4LltikjMFGp63Q== dependencies: - "@aztec/constants" "0.87.4" - "@aztec/foundation" "0.87.4" - "@aztec/kv-store" "0.87.4" - "@aztec/stdlib" "0.87.4" + "@aztec/constants" "2.0.2" + "@aztec/foundation" "2.0.2" + "@aztec/kv-store" "2.0.2" + "@aztec/stdlib" "2.0.2" tslib "^2.4.0" -"@aztec/kv-store@0.87.4": - version "0.87.4" - resolved "https://registry.npmjs.org/@aztec/kv-store/-/kv-store-0.87.4.tgz" - integrity sha512-sO3HxD5WWhGSIf8YRGtAOAvqfjkX5oPBvCmXC1qFztKqB/ubZiYCUIfcZJ29EyNLfnF1zUESG0rB6blWZFTHjA== +"@aztec/kv-store@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@aztec/kv-store/-/kv-store-2.0.2.tgz#8d60bada461fc96a58b479b8366d1e69ff641384" + integrity sha512-6Yip1dNBKW/fG6tm9VhtMI84nXMJCVNSDm2AAW4QlN2n0seQU9XW2dTgdUAAPGjg7ujdmUzrHbYsGYl0ibXBpQ== dependencies: - "@aztec/ethereum" "0.87.4" - "@aztec/foundation" "0.87.4" - "@aztec/native" "0.87.4" - "@aztec/stdlib" "0.87.4" + "@aztec/ethereum" "2.0.2" + "@aztec/foundation" "2.0.2" + "@aztec/native" "2.0.2" + "@aztec/stdlib" "2.0.2" idb "^8.0.0" lmdb "^3.2.0" msgpackr "^1.11.2" ohash "^2.0.11" ordered-binary "^1.5.3" -"@aztec/l1-artifacts@0.87.4": - version "0.87.4" - resolved "https://registry.npmjs.org/@aztec/l1-artifacts/-/l1-artifacts-0.87.4.tgz" - integrity sha512-2L4VL+gk4Wa6Qm4UuJ+zskiKTiyKMiHSvc088HmmF8MYsHvJr38Xu8DxgD4DCv/jjT74doS7ALpGBGQ+bg/icw== +"@aztec/l1-artifacts@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@aztec/l1-artifacts/-/l1-artifacts-2.0.2.tgz#8598d4d7352d4cc1675e52602a0612fa998da8d7" + integrity sha512-CclFsNxN5kuvQZkY2ZD5ZHjHo+JdzSBvK4c5NuBWrrzSuoUv6D6b03ZXnSbcAI2yO0urE+u4g9jm0/ln7kum3Q== dependencies: tslib "^2.4.0" -"@aztec/merkle-tree@0.87.4": - version "0.87.4" - resolved "https://registry.npmjs.org/@aztec/merkle-tree/-/merkle-tree-0.87.4.tgz" - integrity sha512-Fl7R7OXIVPps1sSAeYXuibVsJyZWaeYuTUtO2tjraQrbjus8vFIbAjzvAE5/ClDnQs/I6AK3KuFH+jqnhgldHg== +"@aztec/merkle-tree@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@aztec/merkle-tree/-/merkle-tree-2.0.2.tgz#19f6a125db574c46870bec2846603fd3c3094ee4" + integrity sha512-USZbIPSbrA+qWHdxh5KiUrgfM6L0QUgGfez4HQhpPiVUZ1lXJ61xZSAM8YdsDFTT3XPWPYOLZD28XFlHr+35xA== dependencies: - "@aztec/foundation" "0.87.4" - "@aztec/kv-store" "0.87.4" - "@aztec/stdlib" "0.87.4" + "@aztec/foundation" "2.0.2" + "@aztec/kv-store" "2.0.2" + "@aztec/stdlib" "2.0.2" sha256 "^0.2.0" tslib "^2.4.0" -"@aztec/native@0.87.4": - version "0.87.4" - resolved "https://registry.npmjs.org/@aztec/native/-/native-0.87.4.tgz" - integrity sha512-Y1vrRZj5SCpNU2lkoHNhN/PO+M7juSN4RoCfZ0NPpYRf5eibI2K6nkjCrC9wlj/brb3NdCFLNXX4WsE20lJBQA== +"@aztec/native@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@aztec/native/-/native-2.0.2.tgz#5cbbdbcddf2de6245e2bd2ca80f7de4090259fad" + integrity sha512-BSSGrbcRiIR25koPBccXF4L54O143VgpNJfMVIyC9swcd1dncPB/27QnhsFCW8le1fXuwOxLbMIj7IlHnjFP8w== dependencies: - "@aztec/foundation" "0.87.4" + "@aztec/foundation" "2.0.2" bindings "^1.5.0" msgpackr "^1.11.2" -"@aztec/noir-acvm_js@0.87.4": - version "0.87.4" - resolved "https://registry.npmjs.org/@aztec/noir-acvm_js/-/noir-acvm_js-0.87.4.tgz" - integrity sha512-3I0JwyqHBNbkONgrz/RIkIr0ies+uf8Z2gcSRHynOzp3uNn9nvH7Qq1xuG22xmuSyLSOtQCsBu8BOJsd4dgJzg== +"@aztec/noir-acvm_js@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@aztec/noir-acvm_js/-/noir-acvm_js-2.0.2.tgz#12a61fd2368e9fd63a35e4e3f5c9fc7f9d745cf4" + integrity sha512-EF9iEYopRS9mKoirj7Pwtcvv2tEOSyQIbbnwNat9pIB+F1uYLnGFU5cAnQQJQ18qCtsU0t9jLptnXFmokfMRdw== -"@aztec/noir-contracts.js@0.87.4": - version "0.87.4" - resolved "https://registry.npmjs.org/@aztec/noir-contracts.js/-/noir-contracts.js-0.87.4.tgz" - integrity sha512-qT579J8qydcakWTeEBsrCjRR/qrUa20ZLD5C8TOI60yUtXY9b/sSLxb9TeClzZ3jjO/v/RNi10S3YElJpvyXoA== +"@aztec/noir-contracts.js@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@aztec/noir-contracts.js/-/noir-contracts.js-2.0.2.tgz#773585a56e707d9ac0c6d1fefd40896cc0f63fc8" + integrity sha512-Lw4IgnCIgQNFbL0pmP52CtEikTXWyIiOJc6Ji5MLCjd405Js54OtEuy/I7hcaVhQmswD7/EeDG63hfP0omepsQ== dependencies: - "@aztec/aztec.js" "0.87.4" + "@aztec/aztec.js" "2.0.2" tslib "^2.4.0" -"@aztec/noir-noir_codegen@0.87.4": - version "0.87.4" - resolved "https://registry.npmjs.org/@aztec/noir-noir_codegen/-/noir-noir_codegen-0.87.4.tgz" - integrity sha512-8IzW58DaDQtenZWQAipf81+0nRVz2RSK01DbWPd+PtmKQiZtyyvT0h5YpVgSvhgO2LrUJt3HJq31GbZZ4lhY2g== +"@aztec/noir-noir_codegen@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@aztec/noir-noir_codegen/-/noir-noir_codegen-2.0.2.tgz#0ef2ac024074092924e40f2fce3321d1657b45e2" + integrity sha512-2FP85t3VlLvsI+edoAbX+1zR+SEnCO9wzbHNv5sbexA2A3FVDZGRbGhyHRIQfciubfQvqj8CNVZnsCeSmVa81A== dependencies: - "@aztec/noir-types" "0.87.4" - glob "^11.0.1" + "@aztec/noir-types" "2.0.2" + glob "^11.0.2" ts-command-line-args "^2.5.1" -"@aztec/noir-noirc_abi@0.87.4": - version "0.87.4" - resolved "https://registry.npmjs.org/@aztec/noir-noirc_abi/-/noir-noirc_abi-0.87.4.tgz" - integrity sha512-AflhWqi7aGpPegDnQLpA5Oiqm2+6570y5YySFj7ugz00Bn3zPWUA9o9iMoV3q7EJ88Qwr3KvhE0AU2vgTK1n5w== - dependencies: - "@aztec/noir-types" "0.87.4" - -"@aztec/noir-protocol-circuits-types@0.87.4": - version "0.87.4" - resolved "https://registry.npmjs.org/@aztec/noir-protocol-circuits-types/-/noir-protocol-circuits-types-0.87.4.tgz" - integrity sha512-t/LxvDs5nxwHYZOcl23qDRFExe/IAl/c1zdC7O2piGiSXe7KA7Q2DL+O8yJKSC5QlPSuK37Qivj0bQf0ktyiuA== - dependencies: - "@aztec/blob-lib" "0.87.4" - "@aztec/constants" "0.87.4" - "@aztec/foundation" "0.87.4" - "@aztec/noir-acvm_js" "0.87.4" - "@aztec/noir-noir_codegen" "0.87.4" - "@aztec/noir-noirc_abi" "0.87.4" - "@aztec/noir-types" "0.87.4" - "@aztec/stdlib" "0.87.4" +"@aztec/noir-noirc_abi@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@aztec/noir-noirc_abi/-/noir-noirc_abi-2.0.2.tgz#86c7fdf6a920bca87558bb07ee81bcbc17d5a392" + integrity sha512-5WCQyaPGGsGslB35OylhbKUc0SUqpy9WxoVZhyDzpiB/zJ2qbTKsLNP50SQeD1vSth1qZgbIeRP1fjss/knoMQ== + dependencies: + "@aztec/noir-types" "2.0.2" + +"@aztec/noir-protocol-circuits-types@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@aztec/noir-protocol-circuits-types/-/noir-protocol-circuits-types-2.0.2.tgz#444b40125489a4ff83d7452fd8f4df24b2dbde8f" + integrity sha512-qtN5B0LckRiWlPLcJwiNChv65CsVZ2LRNtSfIsBYk7GkDdtYPRau8eDydPN5ObzEXc++2Tq4566cLjMbZPnecQ== + dependencies: + "@aztec/blob-lib" "2.0.2" + "@aztec/constants" "2.0.2" + "@aztec/foundation" "2.0.2" + "@aztec/noir-acvm_js" "2.0.2" + "@aztec/noir-noir_codegen" "2.0.2" + "@aztec/noir-noirc_abi" "2.0.2" + "@aztec/noir-types" "2.0.2" + "@aztec/stdlib" "2.0.2" change-case "^5.4.4" tslib "^2.4.0" -"@aztec/noir-types@0.87.4": - version "0.87.4" - resolved "https://registry.npmjs.org/@aztec/noir-types/-/noir-types-0.87.4.tgz" - integrity sha512-w8A71YYxZUZAB06mgmPaj09WSKYt3QSodgRqoGDyFsknnATNWbEHJxcTlwD6oyfdGyTfPfK79HoO4aQQBus23A== +"@aztec/noir-types@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@aztec/noir-types/-/noir-types-2.0.2.tgz#ab997738183dd2aea2a6b4ecc915db85516d858c" + integrity sha512-eZoOaYVAcC34/xb42TglCESdBzBl2+Pc+dh9vQqrhsBsgFeD5CkYiYZ9JhwtWU0pxjdqz7b4fP1Z8QdeWxh/zA== -"@aztec/protocol-contracts@0.87.4": - version "0.87.4" - resolved "https://registry.npmjs.org/@aztec/protocol-contracts/-/protocol-contracts-0.87.4.tgz" - integrity sha512-WLqCQFKwxOAbGnFUUc7F2Ayw8Y+m4Xnt0TLX8UDwuKnyxQT+zGXtLQiZQxAN9MRPBHVLC0TMfHtpe6fAIiZo2Q== +"@aztec/protocol-contracts@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@aztec/protocol-contracts/-/protocol-contracts-2.0.2.tgz#c5ebdbd68b9a54e488710c00f82659960aaffdd6" + integrity sha512-FTwvPLug3Eae9Y+olYB+ssvyzeXe7n247JS1dADjoKEwc3PC/tGlfn9vSzQtPswTrorFXVjE9DdIjMKZokV1Kg== dependencies: - "@aztec/constants" "0.87.4" - "@aztec/foundation" "0.87.4" - "@aztec/stdlib" "0.87.4" + "@aztec/constants" "2.0.2" + "@aztec/foundation" "2.0.2" + "@aztec/stdlib" "2.0.2" lodash.chunk "^4.2.0" lodash.omit "^4.5.0" tslib "^2.4.0" -"@aztec/pxe@0.87.4": - version "0.87.4" - resolved "https://registry.npmjs.org/@aztec/pxe/-/pxe-0.87.4.tgz" - integrity sha512-zQoB4Bcpd1N2Ae+Jkt+5X0+OpErpRAgZA+6685OjzrCs9Dn3wnQdMJPuAIaCDjT4LRS0izl+qRfFLWnhzKnGRg== - dependencies: - "@aztec/bb-prover" "0.87.4" - "@aztec/bb.js" "0.87.4" - "@aztec/builder" "0.87.4" - "@aztec/constants" "0.87.4" - "@aztec/ethereum" "0.87.4" - "@aztec/foundation" "0.87.4" - "@aztec/key-store" "0.87.4" - "@aztec/kv-store" "0.87.4" - "@aztec/noir-protocol-circuits-types" "0.87.4" - "@aztec/noir-types" "0.87.4" - "@aztec/protocol-contracts" "0.87.4" - "@aztec/simulator" "0.87.4" - "@aztec/stdlib" "0.87.4" +"@aztec/pxe@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@aztec/pxe/-/pxe-2.0.2.tgz#7011d36a8e04b9495e51d9816b8f6ff45ecd03f3" + integrity sha512-kcGZHJPAjJqqjRnoATuqMTZLvAW6Ik9L6TvvbhdSIm0emeeSaV2ZYJ6WDMplY1us8BpmPVheFdQaI90WJAF/QQ== + dependencies: + "@aztec/bb-prover" "2.0.2" + "@aztec/bb.js" "2.0.2" + "@aztec/builder" "2.0.2" + "@aztec/constants" "2.0.2" + "@aztec/ethereum" "2.0.2" + "@aztec/foundation" "2.0.2" + "@aztec/key-store" "2.0.2" + "@aztec/kv-store" "2.0.2" + "@aztec/noir-protocol-circuits-types" "2.0.2" + "@aztec/noir-types" "2.0.2" + "@aztec/protocol-contracts" "2.0.2" + "@aztec/simulator" "2.0.2" + "@aztec/stdlib" "2.0.2" + json-stringify-deterministic "1.0.12" koa "^2.16.1" koa-router "^12.0.0" lodash.omit "^4.5.0" @@ -286,36 +289,37 @@ tslib "^2.4.0" viem "2.23.7" -"@aztec/simulator@0.87.4": - version "0.87.4" - resolved "https://registry.npmjs.org/@aztec/simulator/-/simulator-0.87.4.tgz" - integrity sha512-ufTRjFFX/3UvPQVhPxsfmMFXvUHHf2lyJgq9Grck/Es+0zzQzQl03bUi+w+6+pOgyCqaViiY2UG8dOjW5vEFlg== - dependencies: - "@aztec/constants" "0.87.4" - "@aztec/foundation" "0.87.4" - "@aztec/noir-acvm_js" "0.87.4" - "@aztec/noir-noirc_abi" "0.87.4" - "@aztec/noir-protocol-circuits-types" "0.87.4" - "@aztec/noir-types" "0.87.4" - "@aztec/protocol-contracts" "0.87.4" - "@aztec/stdlib" "0.87.4" - "@aztec/telemetry-client" "0.87.4" - "@aztec/world-state" "0.87.4" +"@aztec/simulator@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@aztec/simulator/-/simulator-2.0.2.tgz#2b7a3da2f46189599ac591da843294e3ef5c426b" + integrity sha512-LE01H5ryiEu84C3xYBieji5TXSam9AzU10w9shx+wIKR7+/aJ4426CaXrDssT/zTBiHrCr+BOAIX8FQiT3KCdg== + dependencies: + "@aztec/constants" "2.0.2" + "@aztec/foundation" "2.0.2" + "@aztec/noir-acvm_js" "2.0.2" + "@aztec/noir-noirc_abi" "2.0.2" + "@aztec/noir-protocol-circuits-types" "2.0.2" + "@aztec/noir-types" "2.0.2" + "@aztec/protocol-contracts" "2.0.2" + "@aztec/stdlib" "2.0.2" + "@aztec/telemetry-client" "2.0.2" + "@aztec/world-state" "2.0.2" lodash.clonedeep "^4.5.0" lodash.merge "^4.6.2" tslib "^2.4.0" -"@aztec/stdlib@0.87.4": - version "0.87.4" - resolved "https://registry.npmjs.org/@aztec/stdlib/-/stdlib-0.87.4.tgz" - integrity sha512-QYBQ61Sx1QloTVF5klnMBdSBaaCwsHqAq4Zhxa9zfPSYx2ML/196GuH2coi+Zwn3+KPbXfAnhJzAlSR01yA4Pg== - dependencies: - "@aztec/bb.js" "0.87.4" - "@aztec/blob-lib" "0.87.4" - "@aztec/constants" "0.87.4" - "@aztec/ethereum" "0.87.4" - "@aztec/foundation" "0.87.4" - "@aztec/noir-noirc_abi" "0.87.4" +"@aztec/stdlib@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@aztec/stdlib/-/stdlib-2.0.2.tgz#b3114a0b139e7d0057cb81532e95e9c03695ca69" + integrity sha512-nsOZJwz2v+rzHUrGE2e6a+qRerK5EYaqX8IqJOngi6e5Uq1oTFhsthgYV45H2jM5aNYsf2WgU69zlFKgODXCXQ== + dependencies: + "@aztec/bb.js" "2.0.2" + "@aztec/blob-lib" "2.0.2" + "@aztec/constants" "2.0.2" + "@aztec/ethereum" "2.0.2" + "@aztec/foundation" "2.0.2" + "@aztec/l1-artifacts" "2.0.2" + "@aztec/noir-noirc_abi" "2.0.2" "@google-cloud/storage" "^7.15.0" axios "^1.9.0" json-stringify-deterministic "1.0.12" @@ -329,13 +333,13 @@ viem "2.23.7" zod "^3.23.8" -"@aztec/telemetry-client@0.87.4": - version "0.87.4" - resolved "https://registry.npmjs.org/@aztec/telemetry-client/-/telemetry-client-0.87.4.tgz" - integrity sha512-bSaZJbp7vjsHj4LjmMWinXXvlqrVrQloTrcET9QZnf10IqroC5XEUbKSuoNmxzRqKHhnuv+AbJSyIL1HccRuLQ== +"@aztec/telemetry-client@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@aztec/telemetry-client/-/telemetry-client-2.0.2.tgz#38a5858d7608ade219004489316fdfe4e8254154" + integrity sha512-qvMjCsger+Pd61iyxqeCw3nhRjDTTftPHwWY7up7C433apD3f7OGRx33N/TDOIlvmD976SNi2lIemeRvcrtEiQ== dependencies: - "@aztec/foundation" "0.87.4" - "@aztec/stdlib" "0.87.4" + "@aztec/foundation" "2.0.2" + "@aztec/stdlib" "2.0.2" "@opentelemetry/api" "^1.9.0" "@opentelemetry/api-logs" "^0.55.0" "@opentelemetry/core" "^1.28.0" @@ -353,19 +357,19 @@ prom-client "^15.1.3" viem "2.23.7" -"@aztec/world-state@0.87.4": - version "0.87.4" - resolved "https://registry.npmjs.org/@aztec/world-state/-/world-state-0.87.4.tgz" - integrity sha512-/9gxoxhKSCRle2jQNAyyWucX1Jmlz/DAnhZ5oZl3RiA9w+IcVTuZeMy2vR8WiEuSucWo9moV2I8kZBqWhykMLQ== - dependencies: - "@aztec/constants" "0.87.4" - "@aztec/foundation" "0.87.4" - "@aztec/kv-store" "0.87.4" - "@aztec/merkle-tree" "0.87.4" - "@aztec/native" "0.87.4" - "@aztec/protocol-contracts" "0.87.4" - "@aztec/stdlib" "0.87.4" - "@aztec/telemetry-client" "0.87.4" +"@aztec/world-state@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@aztec/world-state/-/world-state-2.0.2.tgz#c9fe6e92e4ae68eb840d3be7753c5d368dbc7994" + integrity sha512-cIrnRgxCPZ33mlluOkBROrqcGvzXvaP64UEYNCTYyA3KznPZXNRUjLxNXvNNoPpu5p5eeitskaI0I59UpRm2RA== + dependencies: + "@aztec/constants" "2.0.2" + "@aztec/foundation" "2.0.2" + "@aztec/kv-store" "2.0.2" + "@aztec/merkle-tree" "2.0.2" + "@aztec/native" "2.0.2" + "@aztec/protocol-contracts" "2.0.2" + "@aztec/stdlib" "2.0.2" + "@aztec/telemetry-client" "2.0.2" tslib "^2.4.0" zod "^3.23.8" @@ -423,6 +427,18 @@ resolved "https://registry.npmjs.org/@hapi/bourne/-/bourne-3.0.0.tgz" integrity sha512-Waj1cwPXJDucOib4a3bAISsKJVb15MKi9IvmTI/7ssVEm6sywXGjVJDhl6/umt1pK1ZS7PacXU3A1PmFKHEZ2w== +"@isaacs/balanced-match@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz#3081dadbc3460661b751e7591d7faea5df39dd29" + integrity sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ== + +"@isaacs/brace-expansion@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz#4b3dabab7d8e75a429414a96bd67bf4c1d13e0f3" + integrity sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA== + dependencies: + "@isaacs/balanced-match" "^4.0.1" + "@isaacs/cliui@^8.0.2": version "8.0.2" resolved "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz" @@ -579,7 +595,14 @@ dependencies: "@noble/hashes" "1.7.1" -"@noble/curves@^1.2.0", "@noble/curves@^1.6.0", "@noble/curves@~1.9.0": +"@noble/curves@=1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.7.0.tgz#0512360622439256df892f21d25b388f52505e45" + integrity sha512-UTMhXK9SeDhFJVrHeUJ5uZlI6ajXg10O6Ddocf9S6GjbSBVZsJo88HzKwXznNfGpMTRDyJkqMjNDPYgf0qFWnw== + dependencies: + "@noble/hashes" "1.6.0" + +"@noble/curves@^1.6.0", "@noble/curves@~1.9.0": version "1.9.1" resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.9.1.tgz" integrity sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA== @@ -593,6 +616,11 @@ dependencies: "@noble/hashes" "1.7.2" +"@noble/hashes@1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.6.0.tgz#d4bfb516ad6e7b5111c216a5cc7075f4cf19e6c5" + integrity sha512-YUULf0Uk4/mAA89w+k3+yUYh6NrEvxZa5T6SY3wlMvE2chHkxFUUIDI8/XW1QSC357iA5pSnqt7XEhvFOqmDyQ== + "@noble/hashes@1.7.1": version "1.7.1" resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz" @@ -1462,11 +1490,6 @@ axios@^1.8.2, axios@^1.9.0: form-data "^4.0.0" proxy-from-env "^1.1.0" -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== - base64-js@^1.3.0, base64-js@^1.3.1: version "1.5.1" resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" @@ -1540,13 +1563,6 @@ boolbase@^1.0.0: resolved "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz" integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== -brace-expansion@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz" - integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== - dependencies: - balanced-match "^1.0.0" - braces@^3.0.3, braces@~3.0.2: version "3.0.3" resolved "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz" @@ -2601,9 +2617,9 @@ for-each@^0.3.5: dependencies: is-callable "^1.2.7" -foreground-child@^3.1.0: +foreground-child@^3.3.1: version "3.3.1" - resolved "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.1.tgz#32e8e9ed1b68a3497befb9ac2b6adf92a638576f" integrity sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw== dependencies: cross-spawn "^7.0.6" @@ -2721,14 +2737,14 @@ glob-to-regexp@^0.4.1: resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== -glob@^11.0.1: - version "11.0.2" - resolved "https://registry.npmjs.org/glob/-/glob-11.0.2.tgz" - integrity sha512-YT7U7Vye+t5fZ/QMkBFrTJ7ZQxInIUjwyAjVj84CYXqgBdv30MFUPGnBR6sQaVq6Is15wYJUsnzTuWaGRBhBAQ== +glob@^11.0.2: + version "11.0.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-11.0.3.tgz#9d8087e6d72ddb3c4707b1d2778f80ea3eaefcd6" + integrity sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA== dependencies: - foreground-child "^3.1.0" - jackspeak "^4.0.1" - minimatch "^10.0.0" + foreground-child "^3.3.1" + jackspeak "^4.1.1" + minimatch "^10.0.3" minipass "^7.1.2" package-json-from-dist "^1.0.0" path-scurry "^2.0.0" @@ -3218,9 +3234,9 @@ isows@1.0.6: resolved "https://registry.npmjs.org/isows/-/isows-1.0.6.tgz" integrity sha512-lPHCayd40oW98/I0uvgaHKWCSvkzY27LjWLbtzOm64yQ+G3Q5npjjbdppU65iZXkK1Zt+kH9pfegli0AYfwYYw== -jackspeak@^4.0.1: +jackspeak@^4.1.1: version "4.1.1" - resolved "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-4.1.1.tgz#96876030f450502047fc7e8c7fcf8ce8124e43ae" integrity sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ== dependencies: "@isaacs/cliui" "^8.0.2" @@ -3462,6 +3478,11 @@ lodash.omit@^4.5.0: resolved "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz" integrity sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg== +lodash.pickby@^4.5.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.pickby/-/lodash.pickby-4.6.0.tgz#7dea21d8c18d7703a27c704c15d3b84a67e33aff" + integrity sha512-AZV+GsS/6ckvPOVQPXSiFFacKvKB4kOQu6ynt9wz0F3LO4R9Ij4K1ddYsIytDpSgLz88JHd9P+oaLeej5/Sl7Q== + lodash.times@^4.3.2: version "4.3.2" resolved "https://registry.npmjs.org/lodash.times/-/lodash.times-4.3.2.tgz" @@ -3591,12 +3612,12 @@ minimalistic-crypto-utils@^1.0.1: resolved "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz" integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== -minimatch@^10.0.0: - version "10.0.1" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz" - integrity sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ== +minimatch@^10.0.3: + version "10.0.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.0.3.tgz#cf7a0314a16c4d9ab73a7730a0e8e3c3502d47aa" + integrity sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw== dependencies: - brace-expansion "^2.0.1" + "@isaacs/brace-expansion" "^5.0.0" minimist@^1.2.6: version "1.2.8" @@ -3979,13 +4000,6 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== -pidusage@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/pidusage/-/pidusage-4.0.1.tgz" - integrity sha512-yCH2dtLHfEBnzlHUJymR/Z1nN2ePG3m392Mv8TFlTP1B0xkpMQNHAnfkY0n2tAi6ceKO6YWhxYfZ96V4vVkh/g== - dependencies: - safe-buffer "^5.2.1" - pino-abstract-transport@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz"