From f8613503c65c211bdd451de4aba8bfb39c9ff55a Mon Sep 17 00:00:00 2001 From: spypsy Date: Wed, 5 Feb 2025 16:45:22 +0000 Subject: [PATCH 01/16] lots of changes and merges... --- .github/workflows/ci.yml | 55 + .github/workflows/devnet-deploy.yml | 2 +- .github/workflows/network-deploy.yml | 16 +- barretenberg/.gitrepo | 4 +- barretenberg/acir_tests/bootstrap.sh | 4 + barretenberg/acir_tests/flows/sol_honk_zk.sh | 30 + barretenberg/acir_tests/sol-test/HonkTest.sol | 4 +- .../acir_tests/sol-test/ZKHonkTest.sol | 18 + barretenberg/acir_tests/sol-test/src/index.js | 34 +- barretenberg/cpp/src/barretenberg/bb/main.cpp | 58 +- .../commitment_schemes/commitment_key.hpp | 20 + .../small_subgroup_ipa/small_subgroup_ipa.hpp | 71 +- .../small_subgroup_ipa.test.cpp | 2 +- .../dsl/acir_proofs/honk_contract.hpp | 49 +- .../dsl/acir_proofs/honk_zk_contract.hpp | 1886 +++++++++++++++++ .../lmdb_store/lmdb_store_wrapper.cpp | 11 + .../nodejs_module/util/message_processor.hpp | 7 +- .../proof_system/verification_key/sol_gen.hpp | 4 - .../composer/composer_lib.test.cpp | 15 +- .../circuits/add_2_circuit.hpp | 7 +- .../circuits/blake_circuit.hpp | 3 +- .../circuits/ecdsa_circuit.hpp | 6 +- .../circuits/recursive_circuit.hpp | 47 +- .../solidity_helpers/honk_key_gen.cpp | 55 +- .../solidity_helpers/honk_proof_gen.cpp | 59 +- .../barretenberg/solidity_helpers/key_gen.cpp | 49 +- .../solidity_helpers/proof_gen.cpp | 42 +- .../plookup_tables/plookup_tables.cpp | 14 +- .../plookup_tables/types.hpp | 6 +- .../plookup_tables/uint.hpp | 32 +- .../barretenberg/transcript/transcript.hpp | 1 + .../ultra_honk/mega_honk.test.cpp | 17 + .../barretenberg/ultra_honk/oink_prover.cpp | 147 +- .../barretenberg/ultra_honk/oink_prover.hpp | 5 +- barretenberg/sol/README.md | 2 +- barretenberg/sol/bootstrap.sh | 6 +- barretenberg/sol/scripts/init.sh | 10 +- barretenberg/sol/scripts/init_honk.sh | 9 +- barretenberg/sol/scripts/run_fuzzer.sh | 10 +- .../sol/src/honk/BaseHonkVerifier.sol | 50 +- .../sol/src/honk/BaseZKHonkVerifier.sol | 494 +++++ .../sol/src/honk/CommitmentScheme.sol | 22 +- barretenberg/sol/src/honk/Fr.sol | 5 + barretenberg/sol/src/honk/HonkTypes.sol | 33 + barretenberg/sol/src/honk/Relations.sol | 28 +- barretenberg/sol/src/honk/Transcript.sol | 7 +- barretenberg/sol/src/honk/ZKTranscript.sol | 390 ++++ .../sol/src/honk/instance/Add2HonkZK.sol | 16 + .../sol/src/honk/instance/BlakeHonkZK.sol | 17 + .../sol/src/honk/instance/EcdsaHonkZK.sol | 17 + barretenberg/sol/src/honk/utils.sol | 55 +- .../ultra/keys/Add2UltraVerificationKey.sol | 24 +- .../ultra/keys/BlakeUltraVerificationKey.sol | 96 +- .../ultra/keys/EcdsaUltraVerificationKey.sol | 96 +- .../keys/RecursiveUltraVerificationKey.sol | 96 +- .../sol/test/base/DifferentialFuzzer.sol | 61 +- barretenberg/sol/test/honk/Add2.t.sol | 3 +- barretenberg/sol/test/honk/Add2ZK.t.sol | 46 + barretenberg/sol/test/honk/Blake.t.sol | 2 +- barretenberg/sol/test/honk/BlakeZK.t.sol | 48 + barretenberg/sol/test/honk/ECDSA.t.sol | 3 +- barretenberg/sol/test/honk/ECDSAZK.t.sol | 48 + barretenberg/sol/test/honk/TestBaseHonk.sol | 2 +- barretenberg/sol/test/honk/TestBaseHonkZK.sol | 27 + barretenberg/sol/test/ultra/Add2.t.sol | 2 +- barretenberg/sol/test/ultra/Blake.t.sol | 2 +- barretenberg/sol/test/ultra/ECDSA.t.sol | 2 +- barretenberg/sol/test/ultra/Recursive.t.sol | 2 +- barretenberg/sol/test/ultra/TestBaseUltra.sol | 2 +- boxes/bootstrap.sh | 2 +- boxes/boxes/react/webpack.config.js | 4 +- boxes/boxes/vite/.env | 1 - boxes/boxes/vite/.gitignore | 1 + boxes/boxes/vite/package.json | 8 +- boxes/boxes/vite/playwright.config.ts | 37 + boxes/boxes/vite/src/config.ts | 22 +- boxes/boxes/vite/src/main.tsx | 5 + boxes/boxes/vite/tests/browser.spec.ts | 25 + boxes/boxes/vite/tests/node.test.ts | 45 + boxes/docker-compose.yml | 1 + boxes/yarn.lock | 62 +- docs/docs/aztec/concepts/accounts/keys.md | 171 +- .../guides/developer_guides/js_apps/test.md | 2 +- docs/docs/migration_notes.md | 2 +- gaztec/package.json | 8 +- gaztec/src/aztecEnv.ts | 106 +- .../components/deployContractDialog.tsx | 6 +- gaztec/src/components/contract/contract.tsx | 278 ++- gaztec/src/components/contract/dropzone.css | 16 +- gaztec/src/components/home/home.tsx | 65 +- gaztec/src/components/logPanel/logPanel.tsx | 168 ++ .../sidebar/components/addNetworkDialog.tsx | 64 + .../components/createAccountDialog.tsx | 4 +- .../sidebar/components/txsPanel.tsx | 100 + gaztec/src/components/sidebar/sidebar.tsx | 172 +- gaztec/src/utils/storage.ts | 38 + gaztec/yarn.lock | 706 +++--- l1-contracts/bootstrap.sh | 7 +- .../src/core/ProofCommitmentEscrow.sol | 2 +- l1-contracts/src/core/Rollup.sol | 17 +- l1-contracts/src/core/ValidatorSelection.sol | 180 +- .../interfaces/IProofCommitmentEscrow.sol | 2 +- l1-contracts/src/core/interfaces/IRollup.sol | 2 +- l1-contracts/src/core/interfaces/IStaking.sol | 9 + .../core/interfaces/IValidatorSelection.sol | 6 +- .../src/core/libraries/DataStructures.sol | 2 +- l1-contracts/src/core/libraries/Errors.sol | 2 +- .../libraries/RollupLibs/EpochProofLib.sol | 2 +- .../RollupLibs/EpochProofQuoteLib.sol | 2 +- .../libraries/RollupLibs/ExtRollupLib.sol | 2 +- .../libraries/RollupLibs/ValidationLib.sol | 2 +- l1-contracts/src/core/libraries/TimeLib.sol | 63 + l1-contracts/src/core/libraries/TimeMath.sol | 47 - .../ValidatorSelectionLib.sol | 2 +- .../src/core/libraries/staking/StakingLib.sol | 145 ++ l1-contracts/src/core/staking/Staking.sol | 193 -- l1-contracts/src/governance/Governance.sol | 2 +- .../src/governance/interfaces/IGovernance.sol | 2 +- .../interfaces/IGovernanceProposer.sol | 2 +- .../governance/libraries/ConfigurationLib.sol | 2 +- .../governance/libraries/DataStructures.sol | 2 +- .../src/governance/libraries/Errors.sol | 2 +- .../src/governance/libraries/ProposalLib.sol | 2 +- .../src/governance/libraries/UserLib.sol | 2 +- .../src/governance/proposer/EmpireBase.sol | 2 +- l1-contracts/src/periphery/SlashFactory.sol | 2 +- l1-contracts/src/periphery/SlashPayload.sol | 2 +- .../periphery/interfaces/ISlashFactory.sol | 2 +- l1-contracts/test/Rollup.t.sol | 44 +- l1-contracts/test/base/Base.sol | 13 +- l1-contracts/test/fees/FeeRollup.t.sol | 4 +- l1-contracts/test/fees/MinimalFeeModel.sol | 17 +- l1-contracts/test/fees/MinimalFeeModel.t.sol | 2 +- .../governance-proposer/executeProposal.t.sol | 6 +- .../governance/governance-proposer/vote.t.sol | 2 +- .../test/governance/governance/base.t.sol | 2 +- .../test/governance/governance/deposit.t.sol | 2 +- .../governance/finaliseWithdraw.t.sol | 2 +- .../governance/getProposalState.t.sol | 2 +- .../governance/initiateWithdraw.t.sol | 2 +- .../governance/proposallib/static.t.sol | 2 +- .../test/governance/governance/propose.t.sol | 2 +- .../governance/proposeWithLock.t.sol | 2 +- .../governance/scenarios/noVoteAndExit.t.sol | 2 +- .../governance/updateConfiguration.t.sol | 3 +- .../governance/governance/userlib/add.t.sol | 2 +- .../governance/governance/userlib/base.t.sol | 2 +- .../governance/userlib/powerAt.t.sol | 2 +- .../governance/userlib/powerNow.t.sol | 2 +- .../governance/governance/userlib/sub.t.sol | 2 +- .../test/governance/governance/vote.t.sol | 2 +- .../UpgradeGovernanceProposerTest.t.sol | 3 +- .../scenario/slashing/Slashing.t.sol | 9 +- .../ProofCommitmentEscrow.t.sol | 2 +- l1-contracts/test/staking/StakingCheater.sol | 90 +- l1-contracts/test/staking/base.t.sol | 2 +- l1-contracts/test/staking/deposit.t.sol | 2 +- .../test/staking/finaliseWithdraw.t.sol | 9 +- l1-contracts/test/staking/getters.t.sol | 2 +- .../test/staking/initiateWithdraw.t.sol | 9 +- l1-contracts/test/staking/slash.t.sol | 5 +- .../ValidatorSelection.t.sol | 14 +- noir-projects/aztec-nr/.gitrepo | 4 +- noir-projects/noir-contracts/Nargo.toml | 1 + noir-projects/noir-contracts/bootstrap.sh | 2 +- .../contracts/amm_contract/src/main.nr | 2 +- .../app_subscription_contract/src/main.nr | 2 +- .../contracts/auth_contract/src/main.nr | 2 +- .../auth_registry_contract/src/main.nr | 2 +- .../auth_wit_test_contract/src/main.nr | 2 +- .../avm_gadgets_test_contract/Nargo.toml | 8 + .../avm_gadgets_test_contract/src/main.nr | 93 + .../avm_initializer_test_contract/src/main.nr | 2 +- .../contracts/avm_test_contract/src/main.nr | 15 +- .../benchmarking_contract/src/main.nr | 2 +- .../contracts/card_game_contract/src/main.nr | 2 +- .../contracts/child_contract/src/main.nr | 2 +- .../contracts/claim_contract/src/main.nr | 2 +- .../src/main.nr | 2 +- .../src/main.nr | 2 +- .../contracts/counter_contract/src/main.nr | 2 +- .../crowdfunding_contract/src/main.nr | 2 +- .../docs_example_contract/src/main.nr | 2 +- .../easy_private_token_contract/src/main.nr | 2 +- .../easy_private_voting_contract/src/main.nr | 2 +- .../ecdsa_k_account_contract/src/main.nr | 2 +- .../ecdsa_r_account_contract/src/main.nr | 2 +- .../contracts/escrow_contract/src/main.nr | 2 +- .../contracts/fee_juice_contract/src/main.nr | 2 +- .../contracts/fpc_contract/src/main.nr | 2 +- .../import_test_contract/src/main.nr | 2 +- .../inclusion_proofs_contract/src/main.nr | 2 +- .../contracts/lending_contract/src/main.nr | 2 +- .../src/main.nr | 2 +- .../contracts/nft_contract/src/main.nr | 2 +- .../contracts/parent_contract/src/main.nr | 2 +- .../pending_note_hashes_contract/src/main.nr | 2 +- .../contracts/price_feed_contract/src/main.nr | 2 +- .../contracts/router_contract/src/main.nr | 2 +- .../schnorr_account_contract/src/main.nr | 4 +- .../src/main.nr | 2 +- .../src/main.nr | 2 +- .../contracts/spam_contract/src/main.nr | 2 +- .../stateful_test_contract/src/main.nr | 2 +- .../static_child_contract/src/main.nr | 2 +- .../static_parent_contract/src/main.nr | 2 +- .../contracts/test_contract/src/main.nr | 2 +- .../contracts/test_log_contract/src/main.nr | 2 +- .../token_blacklist_contract/src/main.nr | 2 +- .../token_bridge_contract/src/main.nr | 2 +- .../contracts/token_contract/src/main.nr | 2 +- .../contracts/uniswap_contract/src/main.nr | 2 +- .../crates/blob/src/blob.nr | 99 +- .../crates/blob/src/blob_public_inputs.nr | 2 +- .../crates/blob/src/unconstrained_config.nr | 4 +- noir/.rebuild_patterns | 1 + .../files/config/get-private-key.sh | 5 + .../files/config/get-validator-addresses.sh | 5 + .../aztec-network/templates/boot-node.yaml | 2 +- .../templates/deploy-l1-verifier.yaml | 2 + .../templates/eth/eth-execution.yaml | 2 +- spartan/aztec-network/templates/faucet.yaml | 4 +- .../aztec-network/templates/prover-agent.yaml | 2 +- .../templates/prover-broker.yaml | 2 +- .../aztec-network/templates/prover-node.yaml | 2 +- spartan/aztec-network/templates/pxe.yaml | 4 +- spartan/aztec-network/templates/rbac.yaml | 4 +- .../templates/setup-l2-contracts.yaml | 2 + .../templates/transaction-bot.yaml | 2 +- .../aztec-network/templates/validator.yaml | 2 +- spartan/aztec-network/values.yaml | 8 +- .../values/3-validators-with-metrics.yaml | 14 + spartan/aztec-network/values/ci-sepolia.yaml | 72 + spartan/aztec-network/values/rc-2.yaml | 8 +- .../sepolia-3-validators-with-metrics.yaml | 27 +- .../aztec-network/values/zombienet-reth.yaml | 75 + .../values/zombienet-sepolia.yaml | 86 + spartan/scripts/deploy_kind.sh | 32 +- spartan/scripts/test_kind.sh | 43 +- spartan/terraform/deploy-release/main.tf | 24 - spartan/terraform/deploy-release/variables.tf | 21 - spartan/terraform/gke-cluster/cluster/main.tf | 6 +- .../archiver/src/archiver/archiver.ts | 2 +- yarn-project/archiver/src/archiver/config.ts | 5 +- .../aztec-node/src/aztec-node/config.ts | 3 - .../aztec-node/src/aztec-node/server.ts | 4 +- .../aztec.js/src/utils/cheat_codes.ts | 6 +- yarn-project/aztec.js/webpack.config.js | 3 +- .../aztec/src/cli/cmds/start_archiver.ts | 2 +- .../aztec/src/cli/cmds/start_p2p_bootstrap.ts | 2 +- .../avm_check_circuit1.test.ts | 114 + .../avm_check_circuit2.test.ts | 118 ++ .../avm_check_circuit3.test.ts | 106 + .../avm_contract_class_limits.test.ts | 84 + .../src/avm_proving_tests/avm_proving.test.ts | 486 ----- .../avm_proving_and_verification.test.ts | 60 + .../avm_proving_tests/avm_proving_tester.ts | 7 +- .../avm_public_fee_payment.test.ts | 73 + .../src/avm_proving_tests/avm_v2.test.ts | 64 + .../src/blobstore/blob_store_test_suite.ts | 6 +- .../src/blobstore/disk_blob_store.test.ts | 4 +- .../src/blobstore/disk_blob_store.ts | 17 +- .../src/blobstore/memory_blob_store.test.ts | 2 +- .../blob-sink/src/client/http.test.ts | 103 +- yarn-project/blob-sink/src/client/http.ts | 73 +- yarn-project/blob-sink/src/server/factory.ts | 6 +- yarn-project/blob-sink/src/server/server.ts | 4 +- yarn-project/bootstrap.sh | 1 + .../cli/src/cmds/infrastructure/sequencers.ts | 2 +- .../cli/src/cmds/l1/update_l1_validators.ts | 4 +- .../scripts/native-network/test-transfer.sh | 1 + .../composed/integration_l1_publisher.test.ts | 51 +- .../src/e2e_l1_with_wall_time.test.ts | 40 +- .../end-to-end/src/e2e_p2p/p2p_network.ts | 2 +- .../end-to-end/src/e2e_p2p/reqresp.test.ts | 2 +- yarn-project/end-to-end/src/e2e_p2p/shared.ts | 45 +- .../end-to-end/src/e2e_p2p/slashing.test.ts | 8 +- .../upgrade_governance_proposer.test.ts | 8 +- .../src/e2e_sequencer/gov_proposal.test.ts | 99 + .../end-to-end/src/e2e_synching.test.ts | 29 +- yarn-project/end-to-end/src/fixtures/utils.ts | 7 +- .../src/shared/submit-transactions.ts | 37 + .../end-to-end/src/spartan/4epochs.test.ts | 36 +- .../upgrade_governance_proposer.test.ts | 168 ++ yarn-project/end-to-end/src/spartan/utils.ts | 53 +- .../ethereum/src/contracts/empire_base.ts | 19 + .../ethereum/src/contracts/forwarder.test.ts | 142 ++ .../ethereum/src/contracts/forwarder.ts | 51 +- .../src/contracts/governance_proposer.ts | 58 +- yarn-project/ethereum/src/contracts/index.ts | 3 +- yarn-project/ethereum/src/contracts/rollup.ts | 28 +- .../src/contracts/slashing_proposer.ts | 31 +- .../ethereum/src/deploy_l1_contracts.ts | 6 + yarn-project/ethereum/src/l1_tx_utils.ts | 28 +- yarn-project/ethereum/src/utils.ts | 32 +- .../foundation/src/abi/encoder.test.ts | 2 +- yarn-project/foundation/src/abi/encoder.ts | 2 +- yarn-project/kv-store/src/lmdb-v2/factory.ts | 10 + yarn-project/kv-store/src/lmdb-v2/store.ts | 32 +- .../kv-store/src/stores/l2_tips_store.test.ts | 6 +- .../scripts/generate-artifacts.sh | 14 + .../generate_client_artifacts_helper.ts | 4 +- yarn-project/p2p-bootstrap/src/index.ts | 2 +- yarn-project/p2p/src/client/factory.ts | 10 +- yarn-project/p2p/src/mocks/index.ts | 4 +- .../p2p/src/services/libp2p/libp2p_service.ts | 2 +- .../reqresp/reqresp.integration.test.ts | 10 +- yarn-project/p2p/src/utils.test.ts | 12 +- yarn-project/package.json | 2 +- .../prover-node/src/prover-node-publisher.ts | 17 +- .../prover-node/src/prover-node.test.ts | 4 +- .../pxe/src/pxe_service/pxe_service.ts | 9 +- .../pxe/src/synchronizer/synchronizer.ts | 7 +- .../src/client/sequencer-client.ts | 25 +- .../sequencer-client/src/publisher/config.ts | 25 +- .../src/publisher/sequencer-publisher.test.ts | 41 +- .../src/publisher/sequencer-publisher.ts | 158 +- .../sequencer-client/src/slasher/factory.ts | 6 +- .../src/slasher/slasher_client.test.ts | 29 +- .../src/slasher/slasher_client.ts | 71 +- .../simulator/src/avm/avm_simulator.test.ts | 39 +- .../simulator/src/avm/avm_tree.test.ts | 12 +- .../src/avm/fixtures/avm_simulation_tester.ts | 5 +- .../simulator/src/avm/fixtures/index.ts | 39 + .../fixtures/public_tx_simulation_tester.ts | 5 +- .../src/public/public_tx_simulator.test.ts | 5 +- .../simulator/src/public/side_effect_trace.ts | 43 +- .../src/public/side_effect_trace_interface.ts | 21 - yarn-project/world-state/package.json | 1 + .../src/native/native_world_state_cmp.test.ts | 258 --- yarn-project/world-state/src/test/index.ts | 1 + .../world-state/src/world-state-db/index.ts | 3 - .../src/world-state-db/merkle_tree_map.ts | 11 - .../merkle_tree_operations_facade.ts | 199 -- .../merkle_tree_snapshot_operations_facade.ts | 151 -- .../src/world-state-db/merkle_trees.ts | 727 ------- .../world-state/src/world-state-db/metrics.ts | 64 - 337 files changed, 8336 insertions(+), 4483 deletions(-) create mode 100755 barretenberg/acir_tests/flows/sol_honk_zk.sh create mode 100644 barretenberg/acir_tests/sol-test/ZKHonkTest.sol create mode 100644 barretenberg/cpp/src/barretenberg/dsl/acir_proofs/honk_zk_contract.hpp create mode 100644 barretenberg/sol/src/honk/BaseZKHonkVerifier.sol create mode 100644 barretenberg/sol/src/honk/ZKTranscript.sol create mode 100644 barretenberg/sol/src/honk/instance/Add2HonkZK.sol create mode 100644 barretenberg/sol/src/honk/instance/BlakeHonkZK.sol create mode 100644 barretenberg/sol/src/honk/instance/EcdsaHonkZK.sol create mode 100644 barretenberg/sol/test/honk/Add2ZK.t.sol create mode 100644 barretenberg/sol/test/honk/BlakeZK.t.sol create mode 100644 barretenberg/sol/test/honk/ECDSAZK.t.sol create mode 100644 barretenberg/sol/test/honk/TestBaseHonkZK.sol delete mode 100644 boxes/boxes/vite/.env create mode 100644 boxes/boxes/vite/playwright.config.ts create mode 100644 boxes/boxes/vite/tests/browser.spec.ts create mode 100644 boxes/boxes/vite/tests/node.test.ts create mode 100644 gaztec/src/components/logPanel/logPanel.tsx create mode 100644 gaztec/src/components/sidebar/components/addNetworkDialog.tsx create mode 100644 gaztec/src/components/sidebar/components/txsPanel.tsx create mode 100644 l1-contracts/src/core/libraries/TimeLib.sol create mode 100644 l1-contracts/src/core/libraries/staking/StakingLib.sol delete mode 100644 l1-contracts/src/core/staking/Staking.sol create mode 100644 noir-projects/noir-contracts/contracts/avm_gadgets_test_contract/Nargo.toml create mode 100644 noir-projects/noir-contracts/contracts/avm_gadgets_test_contract/src/main.nr create mode 100644 spartan/aztec-network/values/ci-sepolia.yaml create mode 100644 spartan/aztec-network/values/zombienet-reth.yaml create mode 100644 spartan/aztec-network/values/zombienet-sepolia.yaml create mode 100644 yarn-project/bb-prover/src/avm_proving_tests/avm_check_circuit1.test.ts create mode 100644 yarn-project/bb-prover/src/avm_proving_tests/avm_check_circuit2.test.ts create mode 100644 yarn-project/bb-prover/src/avm_proving_tests/avm_check_circuit3.test.ts create mode 100644 yarn-project/bb-prover/src/avm_proving_tests/avm_contract_class_limits.test.ts delete mode 100644 yarn-project/bb-prover/src/avm_proving_tests/avm_proving.test.ts create mode 100644 yarn-project/bb-prover/src/avm_proving_tests/avm_proving_and_verification.test.ts create mode 100644 yarn-project/bb-prover/src/avm_proving_tests/avm_public_fee_payment.test.ts create mode 100644 yarn-project/bb-prover/src/avm_proving_tests/avm_v2.test.ts create mode 100644 yarn-project/end-to-end/src/e2e_sequencer/gov_proposal.test.ts create mode 100644 yarn-project/end-to-end/src/shared/submit-transactions.ts create mode 100644 yarn-project/end-to-end/src/spartan/upgrade_governance_proposer.test.ts create mode 100644 yarn-project/ethereum/src/contracts/empire_base.ts create mode 100644 yarn-project/ethereum/src/contracts/forwarder.test.ts delete mode 100644 yarn-project/world-state/src/native/native_world_state_cmp.test.ts create mode 100644 yarn-project/world-state/src/test/index.ts delete mode 100644 yarn-project/world-state/src/world-state-db/merkle_tree_map.ts delete mode 100644 yarn-project/world-state/src/world-state-db/merkle_tree_operations_facade.ts delete mode 100644 yarn-project/world-state/src/world-state-db/merkle_tree_snapshot_operations_facade.ts delete mode 100644 yarn-project/world-state/src/world-state-db/merkle_trees.ts delete mode 100644 yarn-project/world-state/src/world-state-db/metrics.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 493f7b59489d..687b82271682 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,6 +33,9 @@ env: BUILD_INSTANCE_SSH_KEY: ${{ secrets.BUILD_INSTANCE_SSH_KEY }} GITHUB_RUN_URL: "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}/attempts/${{ github.run_attempt }}" GIT_COMMIT: ${{ github.event.pull_request.head.sha || github.sha }} + EXTERNAL_ETHEREUM_HOST: "https://json-rpc.${{ secrets.SEPOLIA_EXTERNAL_HOST }}?key=${{ secrets.SEPOLIA_API_KEY }}" + EXTERNAL_ETHEREUM_CONSENSUS_HOST: "https://beacon.${{ secrets.SEPOLIA_EXTERNAL_HOST }}" + GCP_API_KEY_HEADER: "X-goog-api-key" RUNNER_CONCURRENCY: 0 jobs: configure: @@ -395,6 +398,58 @@ jobs: name: kind-network-smoke.log path: test_kind.log + kind-sepolia-test: + needs: [images-e2e, configure] + if: needs.configure.outputs.network-all == 'true' + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + config: + - test: 4epochs.test.ts + values: ci-sepolia.yaml + runner_type: 16core-tester-x86 + timeout: 40 + steps: + - uses: actions/checkout@v4 + with: { ref: "${{ env.GIT_COMMIT }}" } + - name: Setup and KIND Network Test + timeout-minutes: ${{ matrix.config.timeout }} + uses: ./.github/ensure-tester + with: + username: ${{ needs.configure.outputs.username }} + runner_type: ${{ matrix.config.runner_type }} + spot_strategy: None # use on-demand machines + ttl: ${{ matrix.config.timeout }} + run: | + until docker info &>/dev/null; do sleep 1; done + export CI=1 USE_CACHE=1 + artifact="kind-network-${{matrix.config.test}}-$(./spartan/bootstrap.sh hash)" + if ci3/test_should_run "$artifact"; then + docker pull aztecprotocol/aztec:${{ env.GIT_COMMIT }} + docker pull aztecprotocol/end-to-end:${{ env.GIT_COMMIT }} + + # Set the sepolia run variables + export EXTERNAL_ETHEREUM_HOST=${{ env.EXTERNAL_ETHEREUM_HOST }} + export EXTERNAL_ETHEREUM_CONSENSUS_HOST=${{ env.EXTERNAL_ETHEREUM_CONSENSUS_HOST }} + export EXTERNAL_ETHEREUM_CONSENSUS_HOST_API_KEY=${{ secrets.SEPOLIA_API_KEY }} + export EXTERNAL_ETHEREUM_CONSENSUS_HOST_API_KEY_HEADER=${{ env.GCP_API_KEY_HEADER }} + export L1_DEPLOYMENT_PRIVATE_KEY=${{ secrets.SEPOLIA_DEPLOYMENT_PRIVATE_KEY }} + export L1_ACCOUNTS_MNEMONIC="${{ secrets.SEPOLIA_ACCOUNTS_MNEMONIC }}" + SEPOLIA_RUN=true INSTALL_METRICS=false ./spartan/scripts/test_kind.sh "./src/spartan/${{ matrix.config.test }}" "${{ matrix.config.values }}" + ci3/cache_upload_flag "$artifact" + fi + - name: Copy Network Logs + if: always() + run: scripts/copy_from_tester spartan/scripts/logs/test_kind.log test_kind.log || true + + - name: Upload Network Logs + if: always() + uses: actions/upload-artifact@v4 + with: + name: kind-network-test-${{ matrix.config.values }}-${{ matrix.config.test }}.log + path: test_kind.log + kind-network-test: needs: [images-e2e, configure] if: needs.configure.outputs.network-all == 'true' diff --git a/.github/workflows/devnet-deploy.yml b/.github/workflows/devnet-deploy.yml index e3b2537fc603..60b75ef0c48e 100644 --- a/.github/workflows/devnet-deploy.yml +++ b/.github/workflows/devnet-deploy.yml @@ -138,7 +138,7 @@ jobs: port_forward_pids+=($!) # port-forward directly to the pod because the Eth node does not have a service definition - ETH_POD_NAME=$(kubectl get pods -n $NAMESPACE -l app=ethereum -o jsonpath='{.items[0].metadata.name}') + ETH_POD_NAME=$(kubectl get pods -n $NAMESPACE -l app=eth-execution -o jsonpath='{.items[0].metadata.name}') kubectl port-forward -n $NAMESPACE pod/$ETH_POD_NAME $ETHEREUM_PORT &>/dev/null & port_forward_pids+=($!) diff --git a/.github/workflows/network-deploy.yml b/.github/workflows/network-deploy.yml index d521117338fd..49c0773a13e0 100644 --- a/.github/workflows/network-deploy.yml +++ b/.github/workflows/network-deploy.yml @@ -116,7 +116,7 @@ jobs: TF_STATE_BUCKET: aztec-terraform GKE_CLUSTER_CONTEXT: "gke_testnet-440309_us-west1-a_${{ inputs.cluster }}" GCP_API_KEY_HEADER: "X-goog-api-key" - EXTERNAL_ETHEREUM_HOST: "https://json-rpc.${{ secrets.SEPOLIA_EXTERNAL_HOST }}" + EXTERNAL_ETHEREUM_HOST: "https://json-rpc.${{ secrets.SEPOLIA_EXTERNAL_HOST }}?key=${{ secrets.SEPOLIA_API_KEY }}" EXTERNAL_ETHEREUM_CONSENSUS_HOST: "https://beacon.${{ secrets.SEPOLIA_EXTERNAL_HOST }}" steps: @@ -187,16 +187,15 @@ jobs: continue-on-error: true run: | if ${{ inputs.sepolia_deployment == 'true' }}; then + export L1_DEPLOYMENT_MNEMONIC="${{ secrets.SEPOLIA_ACCOUNTS_MNEMONIC }}" terraform destroy -auto-approve \ -var="RELEASE_NAME=${{ env.NAMESPACE }}" \ -var="VALUES_FILE=${{ env.VALUES_FILE }}" \ -var="GKE_CLUSTER_CONTEXT=${{ env.GKE_CLUSTER_CONTEXT }}" \ -var="AZTEC_DOCKER_IMAGE=${{ env.AZTEC_DOCKER_IMAGE }}" \ -var="L1_DEPLOYMENT_PRIVATE_KEY=${{ secrets.SEPOLIA_L1_DEPLOYMENT_PRIVATE_KEY }}" \ - -var="VALIDATOR_KEYS=${{ secrets.SEPOLIA_VALIDATOR_KEYS }}" \ - -var="BOOT_NODE_SEQ_PUBLISHER_PRIVATE_KEY=${{ secrets.SEPOLIA_BOOT_NODE_SEQ_PUBLISHER_PRIVATE_KEY }}" \ - -var="PROVER_PUBLISHER_PRIVATE_KEY=${{ secrets.SEPOLIA_PROVER_PUBLISHER_PRIVATE_KEY }}" \ - -var="EXTERNAL_ETHEREUM_HOST=${{ env.EXTERNAL_ETHEREUM_HOST }}?key=${{ secrets.SEPOLIA_API_KEY }}" \ + -var="L1_DEPLOYMENT_MNEMONIC=$L1_DEPLOYMENT_MNEMONIC" \ + -var="EXTERNAL_ETHEREUM_HOST=${{ env.EXTERNAL_ETHEREUM_HOST }}" \ -var="EXTERNAL_ETHEREUM_CONSENSUS_HOST=${{ env.EXTERNAL_ETHEREUM_CONSENSUS_HOST }}" \ -var="EXTERNAL_ETHEREUM_CONSENSUS_HOST_API_KEY=${{ secrets.SEPOLIA_API_KEY }}" \ -var="EXTERNAL_ETHEREUM_CONSENSUS_HOST_API_KEY_HEADER=${{ env.GCP_API_KEY_HEADER }}" \ @@ -215,17 +214,16 @@ jobs: working-directory: ./spartan/terraform/deploy-release run: | if ${{ inputs.sepolia_deployment == 'true' }}; then + export L1_DEPLOYMENT_MNEMONIC="${{ secrets.SEPOLIA_ACCOUNTS_MNEMONIC }}" terraform plan \ -var="RELEASE_NAME=${{ env.NAMESPACE }}" \ -var="VALUES_FILE=${{ env.VALUES_FILE }}" \ -var="GKE_CLUSTER_CONTEXT=${{ env.GKE_CLUSTER_CONTEXT }}" \ -var="AZTEC_DOCKER_IMAGE=${{ env.AZTEC_DOCKER_IMAGE }}" \ -var="L1_DEPLOYMENT_PRIVATE_KEY=${{ secrets.SEPOLIA_L1_DEPLOYMENT_PRIVATE_KEY }}" \ + -var="L1_DEPLOYMENT_MNEMONIC=$L1_DEPLOYMENT_MNEMONIC" \ -var="L1_DEPLOYMENT_SALT=${DEPLOYMENT_SALT:-$RANDOM}" \ - -var="VALIDATOR_KEYS=${{ secrets.SEPOLIA_VALIDATOR_KEYS }}" \ - -var="BOOT_NODE_SEQ_PUBLISHER_PRIVATE_KEY=${{ secrets.SEPOLIA_BOOT_NODE_SEQ_PUBLISHER_PRIVATE_KEY }}" \ - -var="PROVER_PUBLISHER_PRIVATE_KEY=${{ secrets.SEPOLIA_PROVER_PUBLISHER_PRIVATE_KEY }}" \ - -var="EXTERNAL_ETHEREUM_HOST=${{ env.EXTERNAL_ETHEREUM_HOST }}?key=${{ secrets.SEPOLIA_API_KEY }}" \ + -var="EXTERNAL_ETHEREUM_HOST=${{ env.EXTERNAL_ETHEREUM_HOST }}" \ -var="EXTERNAL_ETHEREUM_CONSENSUS_HOST=${{ env.EXTERNAL_ETHEREUM_CONSENSUS_HOST }}" \ -var="EXTERNAL_ETHEREUM_CONSENSUS_HOST_API_KEY=${{ secrets.SEPOLIA_API_KEY }}" \ -var="EXTERNAL_ETHEREUM_CONSENSUS_HOST_API_KEY_HEADER=${{ env.GCP_API_KEY_HEADER }}" \ diff --git a/barretenberg/.gitrepo b/barretenberg/.gitrepo index 28423710d268..326579c73966 100644 --- a/barretenberg/.gitrepo +++ b/barretenberg/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/AztecProtocol/barretenberg branch = master - commit = b4b7b180ec8f4fde32d8f05e884bf56061b08a73 - parent = ea0f28ea7ab41aaec33995d2ddad8201b4a30bea + commit = 56503f9cc78e45654f431720318316ca2cc9789d + parent = 61614b1a0fa4a766b1ad5090a29f92a122511806 method = merge cmdver = 0.4.6 diff --git a/barretenberg/acir_tests/bootstrap.sh b/barretenberg/acir_tests/bootstrap.sh index a8ca58ae1dc6..6b035807794c 100755 --- a/barretenberg/acir_tests/bootstrap.sh +++ b/barretenberg/acir_tests/bootstrap.sh @@ -99,6 +99,10 @@ function test_cmds { echo FLOW=sol_honk $run_test 1_mul echo FLOW=sol_honk $run_test slices echo FLOW=sol_honk $run_test verify_honk_proof + echo FLOW=sol_honk_zk $run_test assert_statement + echo FLOW=sol_honk_zk $run_test 1_mul + echo FLOW=sol_honk_zk $run_test slices + echo FLOW=sol_honk_zk $run_test verify_honk_proof # barretenberg-acir-tests-bb.js: # Browser tests. diff --git a/barretenberg/acir_tests/flows/sol_honk_zk.sh b/barretenberg/acir_tests/flows/sol_honk_zk.sh new file mode 100755 index 000000000000..7d728914f6bb --- /dev/null +++ b/barretenberg/acir_tests/flows/sol_honk_zk.sh @@ -0,0 +1,30 @@ +#!/bin/sh +set -eux + +VFLAG=${VERBOSE:+-v} +BFLAG="-b ./target/program.json" +FLAGS="-c $CRS_PATH $VFLAG" + +export PROOF="$PWD/sol_honk_zk_proof" +export PROOF_AS_FIELDS="$PWD/sol_honk_zk_proof_fields.json" +export VK="$PWD/sol_honk_zk_vk" + +# Create a proof, write the solidity contract, write the proof as fields in order to extract the public inputs +$BIN prove_ultra_keccak_honk_zk -o $PROOF $FLAGS $BFLAG +$BIN write_vk_ultra_keccak_honk -o $VK $FLAGS $BFLAG +$BIN verify_ultra_keccak_honk_zk -k $VK -p $PROOF $FLAGS +$BIN proof_as_fields_honk $FLAGS -p $PROOF -o $PROOF_AS_FIELDS +$BIN contract_ultra_honk_zk -k $VK $FLAGS -o ZKVerifier.sol + +# Export the paths to the environment variables for the js test runner +export VERIFIER_PATH="$PWD/ZKVerifier.sol" +export TEST_PATH=$(realpath "../../sol-test/ZKHonkTest.sol") +export TESTING_HONK="true" +export HAS_ZK="true" + + +# Use solcjs to compile the generated key contract with the template verifier and test contract +# index.js will start an anvil, on a random port +# Deploy the verifier then send a test transaction +export TEST_NAME=$(basename $PWD) +node ../../sol-test/src/index.js diff --git a/barretenberg/acir_tests/sol-test/HonkTest.sol b/barretenberg/acir_tests/sol-test/HonkTest.sol index 03bf0397a21d..79e16d0fab47 100644 --- a/barretenberg/acir_tests/sol-test/HonkTest.sol +++ b/barretenberg/acir_tests/sol-test/HonkTest.sol @@ -9,10 +9,10 @@ contract Test { HonkVerifier verifier; constructor() { - verifier = new HonkVerifier(); + verifier = new HonkVerifier(); } - function test(bytes calldata proof, bytes32[] calldata publicInputs) view public returns(bool) { + function test(bytes calldata proof, bytes32[] calldata publicInputs) public view returns (bool) { return verifier.verify(proof, publicInputs); } } diff --git a/barretenberg/acir_tests/sol-test/ZKHonkTest.sol b/barretenberg/acir_tests/sol-test/ZKHonkTest.sol new file mode 100644 index 000000000000..b069dbcbd6cd --- /dev/null +++ b/barretenberg/acir_tests/sol-test/ZKHonkTest.sol @@ -0,0 +1,18 @@ +// THIS FILE WILL NOT COMPILE BY ITSELF +// Compilation is handled in `src/index.js` where solcjs gathers the dependencies + +pragma solidity >=0.8.4; + +import {HonkVerifier} from "./ZKVerifier.sol"; + +contract Test { + HonkVerifier verifier; + + constructor() { + verifier = new HonkVerifier(); + } + + function test(bytes calldata proof, bytes32[] calldata publicInputs) public view returns (bool) { + return verifier.verify(proof, publicInputs); + } +} diff --git a/barretenberg/acir_tests/sol-test/src/index.js b/barretenberg/acir_tests/sol-test/src/index.js index e8ceb1dc260e..465b309042a5 100644 --- a/barretenberg/acir_tests/sol-test/src/index.js +++ b/barretenberg/acir_tests/sol-test/src/index.js @@ -7,10 +7,13 @@ import solc from "solc"; // Size excluding number of public inputs const NUMBER_OF_FIELDS_IN_PLONK_PROOF = 93; const NUMBER_OF_FIELDS_IN_HONK_PROOF = 443; +const NUMBER_OF_FIELDS_IN_HONK_ZK_PROOF = 494; const WRONG_PUBLIC_INPUTS_LENGTH = "0xfa066593"; const SUMCHECK_FAILED = "0x9fc3a218"; const SHPLEMINI_FAILED = "0xa5d82e8a"; +const CONSISTENCY_FAILED = "0xa2a2ac83"; +const GEMINI_CHALLENGE_IN_SUBGROUP = "0x835eb8f7"; // We use the solcjs compiler version in this test, although it is slower than foundry, to run the test end to end // it simplifies of parallelising the test suite @@ -52,13 +55,19 @@ const [test, verifier] = await Promise.all([ fsPromises.readFile(verifierPath, encoding), ]); +// If testing honk is set, then we compile the honk test suite +const testingHonk = getEnvVarCanBeUndefined("TESTING_HONK"); +const hasZK = getEnvVarCanBeUndefined("HAS_ZK"); + +const verifierContract = hasZK ? "ZKVerifier.sol" : "Verifier.sol"; +console.log(verifierContract); export const compilationInput = { language: "Solidity", sources: { "Test.sol": { content: test, }, - "Verifier.sol": { + [verifierContract]: { content: verifier, }, }, @@ -76,10 +85,10 @@ export const compilationInput = { }, }; -// If testing honk is set, then we compile the honk test suite -const testingHonk = getEnvVarCanBeUndefined("TESTING_HONK"); const NUMBER_OF_FIELDS_IN_PROOF = testingHonk - ? NUMBER_OF_FIELDS_IN_HONK_PROOF + ? hasZK + ? NUMBER_OF_FIELDS_IN_HONK_ZK_PROOF + : NUMBER_OF_FIELDS_IN_HONK_PROOF : NUMBER_OF_FIELDS_IN_PLONK_PROOF; if (!testingHonk) { const keyPath = getEnvVar("KEY_PATH"); @@ -98,9 +107,16 @@ if (!testingHonk) { } var output = JSON.parse(solc.compile(JSON.stringify(compilationInput))); -if (output.errors.some((e) => e.severity == "error")) { - throw new Error(JSON.stringify(output.errors, null, 2)); -} + +output.errors.forEach((e) => { + // Stop execution if the contract exceeded the allowed bytecode size + if (e.errorCode == "5574") throw new Error(JSON.stringify(e)); + // Throw if there are compilation errors + if (e.severity == "error") { + throw new Error(JSON.stringify(output.errors, null, 2)); + } +}); + const contract = output.contracts["Test.sol"]["Test"]; const bytecode = contract.evm.bytecode.object; const abi = contract.abi; @@ -250,6 +266,10 @@ try { throw new Error("Sumcheck round failed"); case SHPLEMINI_FAILED: throw new Error("PCS round failed"); + case CONSISTENCY_FAILED: + throw new Error("ZK contract: Subgroup IPA consistency check error"); + case GEMINI_CHALLENGE_IN_SUBGROUP: + throw new Error("ZK contract: Gemini challenge error"); default: throw e; } diff --git a/barretenberg/cpp/src/barretenberg/bb/main.cpp b/barretenberg/cpp/src/barretenberg/bb/main.cpp index f56f49c84541..cc6c23ecd5fd 100644 --- a/barretenberg/cpp/src/barretenberg/bb/main.cpp +++ b/barretenberg/cpp/src/barretenberg/bb/main.cpp @@ -14,6 +14,7 @@ #include "barretenberg/dsl/acir_format/proof_surgeon.hpp" #include "barretenberg/dsl/acir_proofs/acir_composer.hpp" #include "barretenberg/dsl/acir_proofs/honk_contract.hpp" +#include "barretenberg/dsl/acir_proofs/honk_zk_contract.hpp" #include "barretenberg/honk/proof_system/types/proof.hpp" #include "barretenberg/numeric/bitop/get_msb.hpp" #include "barretenberg/plonk/proof_system/proving_key/serialize.hpp" @@ -570,7 +571,7 @@ void contract(const std::string& output_path, const std::string& vk_path) } /** - * @brief Writes a Honk Solidity verifier contract for an ACIR circuit to a file + * @brief Writes a Honk Zero Knowledge Solidity verifier contract for an ACIR circuit to a file * * Communication: * - stdout: The Solidity verifier contract is written to stdout as a string @@ -603,6 +604,40 @@ void contract_honk(const std::string& output_path, const std::string& vk_path) } } +/** + * @brief Writes a zero-knowledge Honk Solidity verifier contract for an ACIR circuit to a file + * + * Communication: + * - stdout: The Solidity verifier contract is written to stdout as a string + * - Filesystem: The Solidity verifier contract is written to the path specified by outputPath + * + * Note: The fact that the contract was computed is for an ACIR circuit is not of importance + * because this method uses the verification key to compute the Solidity verifier contract + * + * @param output_path Path to write the contract to + * @param vk_path Path to the file containing the serialized verification key + */ +void contract_honk_zk(const std::string& output_path, const std::string& vk_path) +{ + using VerificationKey = UltraKeccakZKFlavor::VerificationKey; + using VerifierCommitmentKey = bb::VerifierCommitmentKey; + + auto g2_data = get_bn254_g2_data(CRS_PATH); + srs::init_crs_factory({}, g2_data); + auto vk = std::make_shared(from_buffer(read_file(vk_path))); + vk->pcs_verification_key = std::make_shared(); + + std::string contract = get_honk_zk_solidity_verifier(vk); + + if (output_path == "-") { + writeStringToStdout(contract); + vinfo("contract written to stdout"); + } else { + write_file(output_path, { contract.begin(), contract.end() }); + vinfo("contract written to: ", output_path); + } +} + /** * @brief Converts a proof from a byte array into a list of field elements * @@ -870,7 +905,7 @@ UltraProver_ compute_valid_prover(const std::string& bytecodePath, using Prover = UltraProver_; uint32_t honk_recursion = 0; - if constexpr (IsAnyOf) { + if constexpr (IsAnyOf) { honk_recursion = 1; } else if constexpr (IsAnyOf) { honk_recursion = 2; @@ -886,7 +921,14 @@ UltraProver_ compute_valid_prover(const std::string& bytecodePath, auto builder = acir_format::create_circuit(program, metadata); auto prover = Prover{ builder }; - init_bn254_crs(prover.proving_key->proving_key.circuit_size); + size_t required_crs_size = prover.proving_key->proving_key.circuit_size; + if constexpr (Flavor::HasZK) { + // Ensure there are enough points to commit to the libra polynomials required for zero-knowledge sumcheck + if (required_crs_size < curve::BN254::SUBGROUP_SIZE * 2) { + required_crs_size = curve::BN254::SUBGROUP_SIZE * 2; + } + } + init_bn254_crs(required_crs_size); // output the vk typename Flavor::VerificationKey vk(prover.proving_key->proving_key); @@ -1247,7 +1289,7 @@ void prove_honk_output_all(const std::string& bytecodePath, using VerificationKey = Flavor::VerificationKey; uint32_t honk_recursion = 0; - if constexpr (IsAnyOf) { + if constexpr (IsAnyOf) { honk_recursion = 1; } else if constexpr (IsAnyOf) { honk_recursion = 2; @@ -1429,6 +1471,9 @@ int main(int argc, char* argv[]) } else if (command == "contract_ultra_honk") { std::string output_path = get_option(args, "-o", "./target/contract.sol"); contract_honk(output_path, vk_path); + } else if (command == "contract_ultra_honk_zk") { + std::string output_path = get_option(args, "-o", "./target/contract.sol"); + contract_honk_zk(output_path, vk_path); } else if (command == "write_vk") { std::string output_path = get_option(args, "-o", "./target/vk"); write_vk(bytecode_path, output_path, recursive); @@ -1485,6 +1530,9 @@ int main(int argc, char* argv[]) } else if (command == "prove_ultra_keccak_honk") { std::string output_path = get_option(args, "-o", "./proofs/proof"); prove_honk(bytecode_path, witness_path, output_path, recursive); + } else if (command == "prove_ultra_keccak_honk_zk") { + std::string output_path = get_option(args, "-o", "./proofs/proof"); + prove_honk(bytecode_path, witness_path, output_path, recursive); } else if (command == "prove_ultra_rollup_honk") { std::string output_path = get_option(args, "-o", "./proofs/proof"); prove_honk(bytecode_path, witness_path, output_path, recursive); @@ -1492,6 +1540,8 @@ int main(int argc, char* argv[]) return verify_honk(proof_path, vk_path) ? 0 : 1; } else if (command == "verify_ultra_keccak_honk") { return verify_honk(proof_path, vk_path) ? 0 : 1; + } else if (command == "verify_ultra_keccak_honk_zk") { + return verify_honk(proof_path, vk_path) ? 0 : 1; } else if (command == "verify_ultra_rollup_honk") { return verify_honk(proof_path, vk_path) ? 0 : 1; } else if (command == "write_vk_ultra_honk") { diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/commitment_key.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/commitment_key.hpp index 4108a072d72c..8204e5e4305c 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/commitment_key.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/commitment_key.hpp @@ -337,6 +337,26 @@ template class CommitmentKey { return result; } + + enum class CommitType { Default, Structured, Sparse, StructuredNonZeroComplement }; + + Commitment commit_with_type(PolynomialSpan poly, + CommitType type, + const std::vector>& active_ranges = {}, + size_t final_active_wire_idx = 0) + { + switch (type) { + case CommitType::Structured: + return commit_structured(poly, active_ranges, final_active_wire_idx); + case CommitType::Sparse: + return commit_sparse(poly); + case CommitType::StructuredNonZeroComplement: + return commit_structured_with_nonzero_complement(poly, active_ranges, final_active_wire_idx); + case CommitType::Default: + default: + return commit(poly); + } + } }; } // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/small_subgroup_ipa/small_subgroup_ipa.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/small_subgroup_ipa/small_subgroup_ipa.hpp index 1cddeb6fd0bc..791bb6a601f7 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/small_subgroup_ipa/small_subgroup_ipa.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/small_subgroup_ipa/small_subgroup_ipa.hpp @@ -457,8 +457,6 @@ template class SmallSubgroupIPAVerifier { const FF& inner_product_eval_claim) { - const FF subgroup_generator_inverse = Curve::subgroup_generator_inverse; - // Compute the evaluation of the vanishing polynomia Z_H(X) at X = gemini_evaluation_challenge const FF vanishing_poly_eval = gemini_evaluation_challenge.pow(SUBGROUP_SIZE) - FF(1); @@ -479,11 +477,8 @@ template class SmallSubgroupIPAVerifier { // Compute the evaluations of the challenge polynomial, Lagrange first, and Lagrange last for the fixed small // subgroup - auto [challenge_poly, lagrange_first, lagrange_last] = - compute_batched_barycentric_evaluations(challenge_polynomial_lagrange, - gemini_evaluation_challenge, - subgroup_generator_inverse, - vanishing_poly_eval); + auto [challenge_poly, lagrange_first, lagrange_last] = compute_batched_barycentric_evaluations( + challenge_polynomial_lagrange, gemini_evaluation_challenge, vanishing_poly_eval); const FF& concatenated_at_r = libra_evaluations[0]; const FF& big_sum_shifted_eval = libra_evaluations[1]; @@ -493,7 +488,7 @@ template class SmallSubgroupIPAVerifier { // Compute the evaluation of // L_1(X) * A(X) + (X - 1/g) (A(gX) - A(X) - F(X) G(X)) + L_{|H|}(X)(A(X) - s) - Z_H(X) * Q(X) FF diff = lagrange_first * big_sum_eval; - diff += (gemini_evaluation_challenge - subgroup_generator_inverse) * + diff += (gemini_evaluation_challenge - Curve::subgroup_generator_inverse) * (big_sum_shifted_eval - big_sum_eval - concatenated_at_r * challenge_poly); diff += lagrange_last * (big_sum_eval - inner_product_eval_claim) - vanishing_poly_eval * quotient_eval; @@ -526,14 +521,15 @@ template class SmallSubgroupIPAVerifier { challenge_polynomial_lagrange[0] = FF{ 1 }; // Populate the vector with the powers of the challenges - for (size_t idx_poly = 0; idx_poly < CONST_PROOF_SIZE_LOG_N; idx_poly++) { - size_t current_idx = 1 + LIBRA_UNIVARIATES_LENGTH * idx_poly; + size_t round_idx = 0; + for (auto challenge : multivariate_challenge) { + size_t current_idx = 1 + LIBRA_UNIVARIATES_LENGTH * round_idx; // Compute the current index into the vector challenge_polynomial_lagrange[current_idx] = FF(1); - for (size_t idx = 1; idx < LIBRA_UNIVARIATES_LENGTH; idx++) { - // Recursively compute the powers of the challenge - challenge_polynomial_lagrange[current_idx + idx] = - challenge_polynomial_lagrange[current_idx + idx - 1] * multivariate_challenge[idx_poly]; + for (size_t idx = current_idx + 1; idx < current_idx + LIBRA_UNIVARIATES_LENGTH; idx++) { + // Recursively compute the powers of the challenge up to the length of libra univariates + challenge_polynomial_lagrange[idx] = challenge_polynomial_lagrange[idx - 1] * challenge; } + round_idx++; } return challenge_polynomial_lagrange; } @@ -547,51 +543,40 @@ template class SmallSubgroupIPAVerifier { * interpolation domain is given by \f$ (1, g, g^2, \ldots, g^{|H| -1 } )\f$ * * @param coeffs Coefficients of the polynomial to be evaluated, in our case it is the challenge polynomial - * @param z Evaluation point, we are using the Gemini evaluation challenge + * @param r Evaluation point, we are using the Gemini evaluation challenge * @param inverse_root_of_unity Inverse of the generator of the subgroup H * @return std::array */ static std::array compute_batched_barycentric_evaluations(const std::vector& coeffs, const FF& r, - const FF& inverse_root_of_unity, const FF& vanishing_poly_eval) { - std::array denominators; FF one = FF{ 1 }; - FF numerator = vanishing_poly_eval; - - numerator *= one / FF(SUBGROUP_SIZE); // (r^n - 1) / n - - denominators[0] = r - one; - FF work_root = inverse_root_of_unity; // g^{-1} - // - // Compute the denominators of the Lagrange polynomials evaluated at r - for (size_t i = 1; i < SUBGROUP_SIZE; ++i) { - denominators[i] = work_root * r; - denominators[i] -= one; // r * g^{-i} - 1 - work_root *= inverse_root_of_unity; + + // Construct the denominators of the Lagrange polynomials evaluated at r + std::array denominators; + FF running_power = one; + for (size_t i = 0; i < SUBGROUP_SIZE; ++i) { + denominators[i] = running_power * r - one; // r * g^{-i} - 1 + running_power *= Curve::subgroup_generator_inverse; } // Invert/Batch invert denominators if constexpr (Curve::is_stdlib_type) { - for (FF& denominator : denominators) { - denominator = one / denominator; - } + std::transform( + denominators.begin(), denominators.end(), denominators.begin(), [](FF& d) { return d.invert(); }); } else { FF::batch_invert(&denominators[0], SUBGROUP_SIZE); } - std::array result; - - // Accumulate the evaluation of the polynomials given by `coeffs` vector - result[0] = FF{ 0 }; - for (const auto& [coeff, denominator] : zip_view(coeffs, denominators)) { - result[0] += coeff * denominator; // + coeffs_i * 1/(r * g^{-i} - 1) - } - - result[0] = result[0] * numerator; // The evaluation of the polynomials given by its evaluations over H - result[1] = denominators[0] * numerator; // Lagrange first evaluated at r - result[2] = denominators[SUBGROUP_SIZE - 1] * numerator; // Lagrange last evaluated at r + // Construct the evaluation of the polynomial using its evaluations over H, Lagrange first evaluated at r, + // Lagrange last evaluated at r + FF numerator = vanishing_poly_eval * FF(SUBGROUP_SIZE).invert(); // (r^n - 1) / n + std::array result{ std::inner_product(coeffs.begin(), coeffs.end(), denominators.begin(), FF(0)), + denominators[0], + denominators[SUBGROUP_SIZE - 1] }; + std::transform( + result.begin(), result.end(), result.begin(), [&](FF& denominator) { return denominator * numerator; }); return result; } }; diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/small_subgroup_ipa/small_subgroup_ipa.test.cpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/small_subgroup_ipa/small_subgroup_ipa.test.cpp index cc445d3a3944..cd58a2c854a8 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/small_subgroup_ipa/small_subgroup_ipa.test.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/small_subgroup_ipa/small_subgroup_ipa.test.cpp @@ -126,7 +126,7 @@ TYPED_TEST(SmallSubgroupIPATest, VerifierEvaluations) // Compute required evaluations using efficient batch evaluation const auto [challenge_poly_eval, lagrange_first, lagrange_last] = SmallSubgroupIPA::compute_batched_barycentric_evaluations( - challenge_poly_lagrange, this->evaluation_challenge, subgroup_generator_inverse, vanishing_poly_eval); + challenge_poly_lagrange, this->evaluation_challenge, vanishing_poly_eval); // Compute the evaluations differently, namely, using Lagrange interpolation std::array interpolation_domain; diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/honk_contract.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/honk_contract.hpp index b8638c83a840..5cc27bc8868f 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/honk_contract.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/honk_contract.hpp @@ -630,35 +630,6 @@ function bytesToG1ProofPoint(bytes calldata proofSection) pure returns (Honk.G1P }); } -function ecMul(Honk.G1Point memory point, Fr scalar) view returns (Honk.G1Point memory) { - bytes memory input = abi.encodePacked(point.x, point.y, Fr.unwrap(scalar)); - (bool success, bytes memory result) = address(0x07).staticcall(input); - require(success, "ecMul failed"); - - (uint256 x, uint256 y) = abi.decode(result, (uint256, uint256)); - return Honk.G1Point({x: x, y: y}); -} - -function ecAdd(Honk.G1Point memory point0, Honk.G1Point memory point1) view returns (Honk.G1Point memory) { - bytes memory input = abi.encodePacked(point0.x, point0.y, point1.x, point1.y); - (bool success, bytes memory result) = address(0x06).staticcall(input); - require(success, "ecAdd failed"); - - (uint256 x, uint256 y) = abi.decode(result, (uint256, uint256)); - return Honk.G1Point({x: x, y: y}); -} - -function ecSub(Honk.G1Point memory point0, Honk.G1Point memory point1) view returns (Honk.G1Point memory) { - // We negate the second point - uint256 negativePoint1Y = (Q - point1.y) % Q; - bytes memory input = abi.encodePacked(point0.x, point0.y, point1.x, negativePoint1Y); - (bool success, bytes memory result) = address(0x06).staticcall(input); - require(success, "ecAdd failed"); - - (uint256 x, uint256 y) = abi.decode(result, (uint256, uint256)); - return Honk.G1Point({x: x, y: y}); -} - function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point memory) { point.y = (Q - point.y) % Q; return point; @@ -1568,35 +1539,25 @@ abstract contract BaseHonkVerifier is IVerifier { Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000013b0) ]; - Fr[BATCHED_RELATION_PARTIAL_LENGTH] memory BARYCENTRIC_DOMAIN = [ - Fr.wrap(0x00), - Fr.wrap(0x01), - Fr.wrap(0x02), - Fr.wrap(0x03), - Fr.wrap(0x04), - Fr.wrap(0x05), - Fr.wrap(0x06), - Fr.wrap(0x07) - ]; // To compute the next target sum, we evaluate the given univariate at a point u (challenge). // Performing Barycentric evaluations // Compute B(x) Fr numeratorValue = Fr.wrap(1); - for (uint256 i; i < BATCHED_RELATION_PARTIAL_LENGTH; ++i) { + for (uint256 i = 0; i < BATCHED_RELATION_PARTIAL_LENGTH; ++i) { numeratorValue = numeratorValue * (roundChallenge - Fr.wrap(i)); } // Calculate domain size N of inverses Fr[BATCHED_RELATION_PARTIAL_LENGTH] memory denominatorInverses; - for (uint256 i; i < BATCHED_RELATION_PARTIAL_LENGTH; ++i) { + for (uint256 i = 0; i < BATCHED_RELATION_PARTIAL_LENGTH; ++i) { Fr inv = BARYCENTRIC_LAGRANGE_DENOMINATORS[i]; - inv = inv * (roundChallenge - BARYCENTRIC_DOMAIN[i]); + inv = inv * (roundChallenge - Fr.wrap(i)); inv = FrLib.invert(inv); denominatorInverses[i] = inv; } - for (uint256 i; i < BATCHED_RELATION_PARTIAL_LENGTH; ++i) { + for (uint256 i = 0; i < BATCHED_RELATION_PARTIAL_LENGTH; ++i) { Fr term = roundUnivariates[i]; term = term * denominatorInverses[i]; targetSum = targetSum + term; @@ -1703,7 +1664,7 @@ abstract contract BaseHonkVerifier is IVerifier { mem.constantTermAccumulator = Fr.wrap(0); mem.batchingChallenge = tp.shplonkNu.sqr(); - for (uint256 i; i < CONST_PROOF_SIZE_LOG_N - 1; ++i) { + for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N - 1; ++i) { bool dummy_round = i >= (logN - 1); Fr scalingFactor = Fr.wrap(0); diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/honk_zk_contract.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/honk_zk_contract.hpp new file mode 100644 index 000000000000..92a4cd944e08 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/honk_zk_contract.hpp @@ -0,0 +1,1886 @@ +#pragma once +#include "barretenberg/honk/utils/honk_key_gen.hpp" +#include + +// Source code for the Ultrahonk Solidity verifier. +// It's expected that the AcirComposer will inject a library which will load the verification key into memory. +// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays) +static const char HONK_ZK_CONTRACT_SOURCE[] = R"( + +pragma solidity ^0.8.27; + +type Fr is uint256; + +using { add as + } for Fr global; +using { sub as - } for Fr global; +using { mul as * } for Fr global; +using { exp as ^ } for Fr global; +using { notEqual as != } for Fr global; +using { equal as == } for Fr global; + +uint256 constant MODULUS = + 21888242871839275222246405745257275088548364400416034343698204186575808495617; // Prime field order + +Fr constant MINUS_ONE = Fr.wrap(MODULUS - 1); +Fr constant ONE = Fr.wrap(1); +Fr constant ZERO = Fr.wrap(0); + + +uint256 constant SUBGROUP_SIZE = 256; + +Fr constant SUBGROUP_GENERATOR = Fr.wrap(0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76); +Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap(0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6); + +// Instantiation +library FrLib +{ + function from(uint256 value) internal pure returns(Fr) + { + return Fr.wrap(value % MODULUS); + } + + function fromBytes32(bytes32 value) internal pure returns(Fr) + { + return Fr.wrap(uint256(value) % MODULUS); + } + + function toBytes32(Fr value) internal pure returns(bytes32) + { + return bytes32(Fr.unwrap(value)); + } + + function invert(Fr value) internal view returns(Fr) + { + uint256 v = Fr.unwrap(value); + uint256 result; + + // Call the modexp precompile to invert in the field + assembly + { + let free := mload(0x40) + mstore(free, 0x20) + mstore(add(free, 0x20), 0x20) + mstore(add(free, 0x40), 0x20) + mstore(add(free, 0x60), v) + mstore(add(free, 0x80), sub(MODULUS, 2)) + mstore(add(free, 0xa0), MODULUS) + let success := staticcall(gas(), 0x05, free, 0xc0, 0x00, 0x20) + if iszero(success) { + revert(0, 0) + } + result := mload(0x00) + } + + return Fr.wrap(result); + } + + function pow(Fr base, uint256 v) internal view returns(Fr) + { + uint256 b = Fr.unwrap(base); + uint256 result; + + // Call the modexp precompile to invert in the field + assembly + { + let free := mload(0x40) + mstore(free, 0x20) + mstore(add(free, 0x20), 0x20) + mstore(add(free, 0x40), 0x20) + mstore(add(free, 0x60), b) + mstore(add(free, 0x80), v) + mstore(add(free, 0xa0), MODULUS) + let success := staticcall(gas(), 0x05, free, 0xc0, 0x00, 0x20) + if iszero(success) { + revert(0, 0) + } + result := mload(0x00) + } + + return Fr.wrap(result); + } + + function div(Fr numerator, Fr denominator) internal view returns(Fr) + { + return numerator * invert(denominator); + } + + function sqr(Fr value) internal pure returns (Fr) { + return value * value; + } + + function unwrap(Fr value) internal pure returns (uint256) { + return Fr.unwrap(value); + } + + function neg(Fr value) internal pure returns (Fr) { + return Fr.wrap(MODULUS - Fr.unwrap(value)); + } +} + +// Free functions +function add(Fr a, Fr b) pure returns(Fr) +{ + return Fr.wrap(addmod(Fr.unwrap(a), Fr.unwrap(b), MODULUS)); +} + +function mul(Fr a, Fr b) pure returns(Fr) +{ + return Fr.wrap(mulmod(Fr.unwrap(a), Fr.unwrap(b), MODULUS)); +} + +function sub(Fr a, Fr b) pure returns(Fr) +{ + return Fr.wrap(addmod(Fr.unwrap(a), MODULUS - Fr.unwrap(b), MODULUS)); +} + +function exp(Fr base, Fr exponent) pure returns(Fr) +{ + if (Fr.unwrap(exponent) == 0) return ONE; + + for (uint256 i = 1; i < Fr.unwrap(exponent); i += i) { + base = base * base; + } + return base; +} + +function notEqual(Fr a, Fr b) pure returns(bool) +{ + return Fr.unwrap(a) != Fr.unwrap(b); +} + +function equal(Fr a, Fr b) pure returns(bool) +{ + return Fr.unwrap(a) == Fr.unwrap(b); +} + +uint256 constant CONST_PROOF_SIZE_LOG_N = 28; + +uint256 constant NUMBER_OF_SUBRELATIONS = 26; +uint256 constant ZK_BATCHED_RELATION_PARTIAL_LENGTH = 9; +uint256 constant NUMBER_OF_ENTITIES = 40; +uint256 constant NUMBER_UNSHIFTED = 35; +uint256 constant NUMBER_TO_BE_SHIFTED = 5; + +// Alphas are used as relation separators so there should be NUMBER_OF_SUBRELATIONS - 1 +uint256 constant NUMBER_OF_ALPHAS = 25; + +// Prime field order +uint256 constant Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; // EC group order. F_q +uint256 constant P = 21888242871839275222246405745257275088548364400416034343698204186575808495617; // Prime field order, F_r + +// ENUM FOR WIRES +enum WIRE { + Q_M, + Q_C, + Q_L, + Q_R, + Q_O, + Q_4, + Q_LOOKUP, + Q_ARITH, + Q_RANGE, + Q_ELLIPTIC, + Q_AUX, + Q_POSEIDON2_EXTERNAL, + Q_POSEIDON2_INTERNAL, + SIGMA_1, + SIGMA_2, + SIGMA_3, + SIGMA_4, + ID_1, + ID_2, + ID_3, + ID_4, + TABLE_1, + TABLE_2, + TABLE_3, + TABLE_4, + LAGRANGE_FIRST, + LAGRANGE_LAST, + W_L, + W_R, + W_O, + W_4, + Z_PERM, + LOOKUP_INVERSES, + LOOKUP_READ_COUNTS, + LOOKUP_READ_TAGS, + W_L_SHIFT, + W_R_SHIFT, + W_O_SHIFT, + W_4_SHIFT, + Z_PERM_SHIFT +} + +library Honk { + struct G1Point { + uint256 x; + uint256 y; + } + + struct G1ProofPoint { + uint256 x_0; + uint256 x_1; + uint256 y_0; + uint256 y_1; + } + + struct VerificationKey { + // Misc Params + uint256 circuitSize; + uint256 logCircuitSize; + uint256 publicInputsSize; + // Selectors + G1Point qm; + G1Point qc; + G1Point ql; + G1Point qr; + G1Point qo; + G1Point q4; + G1Point qLookup; // Lookup + G1Point qArith; // Arithmetic widget + G1Point qDeltaRange; // Delta Range sort + G1Point qAux; // Auxillary + G1Point qElliptic; // Auxillary + G1Point qPoseidon2External; + G1Point qPoseidon2Internal; + // Copy cnstraints + G1Point s1; + G1Point s2; + G1Point s3; + G1Point s4; + // Copy identity + G1Point id1; + G1Point id2; + G1Point id3; + G1Point id4; + // Precomputed lookup table + G1Point t1; + G1Point t2; + G1Point t3; + G1Point t4; + // Fixed first and last + G1Point lagrangeFirst; + G1Point lagrangeLast; + } + + struct RelationParameters { + // challenges + Fr eta; + Fr etaTwo; + Fr etaThree; + Fr beta; + Fr gamma; + // derived + Fr publicInputsDelta; + } + +struct ZKProof { + uint256 circuitSize; + uint256 publicInputsSize; + uint256 publicInputsOffset; + // Commitments to wire polynomials + Honk.G1ProofPoint w1; + Honk.G1ProofPoint w2; + Honk.G1ProofPoint w3; + Honk.G1ProofPoint w4; + // Commitments to logup witness polynomials + Honk.G1ProofPoint lookupReadCounts; + Honk.G1ProofPoint lookupReadTags; + Honk.G1ProofPoint lookupInverses; + // Commitment to grand permutation polynomial + Honk.G1ProofPoint zPerm; + Honk.G1ProofPoint[3] libraCommitments; + // Sumcheck + Fr libraSum; + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH][CONST_PROOF_SIZE_LOG_N] sumcheckUnivariates; + Fr[NUMBER_OF_ENTITIES] sumcheckEvaluations; + Fr libraEvaluation; + // ZK + Honk.G1ProofPoint geminiMaskingPoly; + Fr geminiMaskingEval; + // Shplemini + Honk.G1ProofPoint[CONST_PROOF_SIZE_LOG_N - 1] geminiFoldComms; + Fr[CONST_PROOF_SIZE_LOG_N] geminiAEvaluations; + Fr[4] libraPolyEvals; + Honk.G1ProofPoint shplonkQ; + Honk.G1ProofPoint kzgQuotient; + } +} + +struct ZKTranscript { + // Oink + Honk.RelationParameters relationParameters; + Fr[NUMBER_OF_ALPHAS] alphas; + Fr[CONST_PROOF_SIZE_LOG_N] gateChallenges; + // Sumcheck + Fr libraChallenge; + Fr[CONST_PROOF_SIZE_LOG_N] sumCheckUChallenges; + // Shplemini + Fr rho; + Fr geminiR; + Fr shplonkNu; + Fr shplonkZ; + // Derived + Fr publicInputsDelta; +} + +library ZKTranscriptLib { + function generateTranscript(Honk.ZKProof memory proof, bytes32[] calldata publicInputs, uint256 publicInputsSize) + internal + pure + returns (ZKTranscript memory t) + { + Fr previousChallenge; + (t.relationParameters, previousChallenge) = + generateRelationParametersChallenges(proof, publicInputs, publicInputsSize, previousChallenge); + + (t.alphas, previousChallenge) = generateAlphaChallenges(previousChallenge, proof); + + (t.gateChallenges, previousChallenge) = generateGateChallenges(previousChallenge); + (t.libraChallenge, previousChallenge) = generateLibraChallenge(previousChallenge, proof); + (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges(proof, previousChallenge); + + (t.rho, previousChallenge) = generateRhoChallenge(proof, previousChallenge); + + (t.geminiR, previousChallenge) = generateGeminiRChallenge(proof, previousChallenge); + + (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge(proof, previousChallenge); + + (t.shplonkZ, previousChallenge) = generateShplonkZChallenge(proof, previousChallenge); + return t; + } + + function splitChallenge(Fr challenge) internal pure returns (Fr first, Fr second) { + uint256 challengeU256 = uint256(Fr.unwrap(challenge)); + uint256 lo = challengeU256 & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; + uint256 hi = challengeU256 >> 128; + first = FrLib.fromBytes32(bytes32(lo)); + second = FrLib.fromBytes32(bytes32(hi)); + } + + function generateRelationParametersChallenges( + Honk.ZKProof memory proof, + bytes32[] calldata publicInputs, + uint256 publicInputsSize, + Fr previousChallenge + ) internal pure returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) { + (rp.eta, rp.etaTwo, rp.etaThree, previousChallenge) = + generateEtaChallenge(proof, publicInputs, publicInputsSize); + + (rp.beta, rp.gamma, nextPreviousChallenge) = generateBetaAndGammaChallenges(previousChallenge, proof); + } + + function generateEtaChallenge(Honk.ZKProof memory proof, bytes32[] calldata publicInputs, uint256 publicInputsSize) + internal + pure + returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) + { + bytes32[] memory round0 = new bytes32[](3 + publicInputsSize + 12); + round0[0] = bytes32(proof.circuitSize); + round0[1] = bytes32(proof.publicInputsSize); + round0[2] = bytes32(proof.publicInputsOffset); + for (uint256 i = 0; i < publicInputsSize; i++) { + round0[3 + i] = bytes32(publicInputs[i]); + } + + // Create the first challenge + // Note: w4 is added to the challenge later on + round0[3 + publicInputsSize] = bytes32(proof.w1.x_0); + round0[3 + publicInputsSize + 1] = bytes32(proof.w1.x_1); + round0[3 + publicInputsSize + 2] = bytes32(proof.w1.y_0); + round0[3 + publicInputsSize + 3] = bytes32(proof.w1.y_1); + round0[3 + publicInputsSize + 4] = bytes32(proof.w2.x_0); + round0[3 + publicInputsSize + 5] = bytes32(proof.w2.x_1); + round0[3 + publicInputsSize + 6] = bytes32(proof.w2.y_0); + round0[3 + publicInputsSize + 7] = bytes32(proof.w2.y_1); + round0[3 + publicInputsSize + 8] = bytes32(proof.w3.x_0); + round0[3 + publicInputsSize + 9] = bytes32(proof.w3.x_1); + round0[3 + publicInputsSize + 10] = bytes32(proof.w3.y_0); + round0[3 + publicInputsSize + 11] = bytes32(proof.w3.y_1); + + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round0))); + (eta, etaTwo) = splitChallenge(previousChallenge); + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); + + (etaThree,) = splitChallenge(previousChallenge); + } + + function generateBetaAndGammaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) + internal + pure + returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) + { + bytes32[13] memory round1; + round1[0] = FrLib.toBytes32(previousChallenge); + round1[1] = bytes32(proof.lookupReadCounts.x_0); + round1[2] = bytes32(proof.lookupReadCounts.x_1); + round1[3] = bytes32(proof.lookupReadCounts.y_0); + round1[4] = bytes32(proof.lookupReadCounts.y_1); + round1[5] = bytes32(proof.lookupReadTags.x_0); + round1[6] = bytes32(proof.lookupReadTags.x_1); + round1[7] = bytes32(proof.lookupReadTags.y_0); + round1[8] = bytes32(proof.lookupReadTags.y_1); + round1[9] = bytes32(proof.w4.x_0); + round1[10] = bytes32(proof.w4.x_1); + round1[11] = bytes32(proof.w4.y_0); + round1[12] = bytes32(proof.w4.y_1); + + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round1))); + (beta, gamma) = splitChallenge(nextPreviousChallenge); + } + + // Alpha challenges non-linearise the gate contributions + function generateAlphaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) + internal + pure + returns (Fr[NUMBER_OF_ALPHAS] memory alphas, Fr nextPreviousChallenge) + { + // Generate the original sumcheck alpha 0 by hashing zPerm and zLookup + uint256[9] memory alpha0; + alpha0[0] = Fr.unwrap(previousChallenge); + alpha0[1] = proof.lookupInverses.x_0; + alpha0[2] = proof.lookupInverses.x_1; + alpha0[3] = proof.lookupInverses.y_0; + alpha0[4] = proof.lookupInverses.y_1; + alpha0[5] = proof.zPerm.x_0; + alpha0[6] = proof.zPerm.x_1; + alpha0[7] = proof.zPerm.y_0; + alpha0[8] = proof.zPerm.y_1; + + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(alpha0))); + (alphas[0], alphas[1]) = splitChallenge(nextPreviousChallenge); + + for (uint256 i = 1; i < NUMBER_OF_ALPHAS / 2; i++) { + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(nextPreviousChallenge)))); + (alphas[2 * i], alphas[2 * i + 1]) = splitChallenge(nextPreviousChallenge); + } + if (((NUMBER_OF_ALPHAS & 1) == 1) && (NUMBER_OF_ALPHAS > 2)) { + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(nextPreviousChallenge)))); + + (alphas[NUMBER_OF_ALPHAS - 1],) = splitChallenge(nextPreviousChallenge); + } + } + + function generateGateChallenges(Fr previousChallenge) + internal + pure + returns (Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, Fr nextPreviousChallenge) + { + for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N; i++) { + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); + + (gateChallenges[i],) = splitChallenge(previousChallenge); + } + nextPreviousChallenge = previousChallenge; + } + + function generateLibraChallenge(Fr previousChallenge, Honk.ZKProof memory proof) + internal + pure + returns (Fr libraChallenge, Fr nextPreviousChallenge) + { + // 4 comm, 1 sum, 1 challenge + uint256[6] memory challengeData; + challengeData[0] = Fr.unwrap(previousChallenge); + challengeData[1] = proof.libraCommitments[0].x_0; + challengeData[2] = proof.libraCommitments[0].x_1; + challengeData[3] = proof.libraCommitments[0].y_0; + challengeData[4] = proof.libraCommitments[0].y_1; + challengeData[5] = Fr.unwrap(proof.libraSum); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(challengeData))); + (libraChallenge,) = splitChallenge(nextPreviousChallenge); + } + + function generateSumcheckChallenges(Honk.ZKProof memory proof, Fr prevChallenge) + internal + pure + returns (Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, Fr nextPreviousChallenge) + { + for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N; i++) { + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH + 1] memory univariateChal; + univariateChal[0] = prevChallenge; + + // TODO(https://github.com/AztecProtocol/barretenberg/issues/1098): memcpy + for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { + univariateChal[j + 1] = proof.sumcheckUnivariates[i][j]; + } + prevChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(univariateChal))); + + (sumcheckChallenges[i],) = splitChallenge(prevChallenge); + } + nextPreviousChallenge = prevChallenge; + } + + // We add Libra claimed eval + 3 comm + 1 more eval + function generateRhoChallenge(Honk.ZKProof memory proof, Fr prevChallenge) + internal + pure + returns (Fr rho, Fr nextPreviousChallenge) + { + uint256[NUMBER_OF_ENTITIES + 15] memory rhoChallengeElements; + rhoChallengeElements[0] = Fr.unwrap(prevChallenge); + // TODO(https://github.com/AztecProtocol/barretenberg/issues/1098): memcpy + uint256 i; + for (i = 1; i <= NUMBER_OF_ENTITIES; i++) { + rhoChallengeElements[i] = Fr.unwrap(proof.sumcheckEvaluations[i - 1]); + } + rhoChallengeElements[i] = Fr.unwrap(proof.libraEvaluation); + + i += 1; + rhoChallengeElements[i] = proof.libraCommitments[1].x_0; + rhoChallengeElements[i + 1] = proof.libraCommitments[1].x_1; + rhoChallengeElements[i + 2] = proof.libraCommitments[1].y_0; + rhoChallengeElements[i + 3] = proof.libraCommitments[1].y_1; + i += 4; + rhoChallengeElements[i] = proof.libraCommitments[2].x_0; + rhoChallengeElements[i + 1] = proof.libraCommitments[2].x_1; + rhoChallengeElements[i + 2] = proof.libraCommitments[2].y_0; + rhoChallengeElements[i + 3] = proof.libraCommitments[2].y_1; + i += 4; + rhoChallengeElements[i] = proof.geminiMaskingPoly.x_0; + rhoChallengeElements[i + 1] = proof.geminiMaskingPoly.x_1; + rhoChallengeElements[i + 2] = proof.geminiMaskingPoly.y_0; + rhoChallengeElements[i + 3] = proof.geminiMaskingPoly.y_1; + + i += 4; + rhoChallengeElements[i] = Fr.unwrap(proof.geminiMaskingEval); + + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(rhoChallengeElements))); + (rho,) = splitChallenge(nextPreviousChallenge); + } + + function generateGeminiRChallenge(Honk.ZKProof memory proof, Fr prevChallenge) + internal + pure + returns (Fr geminiR, Fr nextPreviousChallenge) + { + uint256[(CONST_PROOF_SIZE_LOG_N - 1) * 4 + 1] memory gR; + gR[0] = Fr.unwrap(prevChallenge); + + for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N - 1; i++) { + gR[1 + i * 4] = proof.geminiFoldComms[i].x_0; + gR[2 + i * 4] = proof.geminiFoldComms[i].x_1; + gR[3 + i * 4] = proof.geminiFoldComms[i].y_0; + gR[4 + i * 4] = proof.geminiFoldComms[i].y_1; + } + + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(gR))); + + (geminiR,) = splitChallenge(nextPreviousChallenge); + } + + function generateShplonkNuChallenge(Honk.ZKProof memory proof, Fr prevChallenge) + internal + pure + returns (Fr shplonkNu, Fr nextPreviousChallenge) + { + uint256[(CONST_PROOF_SIZE_LOG_N) + 1 + 4] memory shplonkNuChallengeElements; + shplonkNuChallengeElements[0] = Fr.unwrap(prevChallenge); + + for (uint256 i = 1; i <= CONST_PROOF_SIZE_LOG_N; i++) { + shplonkNuChallengeElements[i] = Fr.unwrap(proof.geminiAEvaluations[i - 1]); + } + + uint256 libraIdx = 0; + for (uint256 i = CONST_PROOF_SIZE_LOG_N + 1; i <= CONST_PROOF_SIZE_LOG_N + 4; i++) { + shplonkNuChallengeElements[i] = Fr.unwrap(proof.libraPolyEvals[libraIdx]); + libraIdx++; + } + + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkNuChallengeElements))); + (shplonkNu,) = splitChallenge(nextPreviousChallenge); + } + + function generateShplonkZChallenge(Honk.ZKProof memory proof, Fr prevChallenge) + internal + pure + returns (Fr shplonkZ, Fr nextPreviousChallenge) + { + uint256[5] memory shplonkZChallengeElements; + shplonkZChallengeElements[0] = Fr.unwrap(prevChallenge); + + shplonkZChallengeElements[1] = proof.shplonkQ.x_0; + shplonkZChallengeElements[2] = proof.shplonkQ.x_1; + shplonkZChallengeElements[3] = proof.shplonkQ.y_0; + shplonkZChallengeElements[4] = proof.shplonkQ.y_1; + + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkZChallengeElements))); + (shplonkZ,) = splitChallenge(nextPreviousChallenge); + } + + function loadProof(bytes calldata proof) internal pure returns (Honk.ZKProof memory p) { + // Metadata + p.circuitSize = uint256(bytes32(proof[0x00:0x20])); + p.publicInputsSize = uint256(bytes32(proof[0x20:0x40])); + p.publicInputsOffset = uint256(bytes32(proof[0x40:0x60])); + + // Commitments + p.w1 = bytesToG1ProofPoint(proof[0x60:0xe0]); + + p.w2 = bytesToG1ProofPoint(proof[0xe0:0x160]); + p.w3 = bytesToG1ProofPoint(proof[0x160:0x1e0]); + + // Lookup / Permutation Helper Commitments + p.lookupReadCounts = bytesToG1ProofPoint(proof[0x1e0:0x260]); + p.lookupReadTags = bytesToG1ProofPoint(proof[0x260:0x2e0]); + p.w4 = bytesToG1ProofPoint(proof[0x2e0:0x360]); + p.lookupInverses = bytesToG1ProofPoint(proof[0x360:0x3e0]); + p.zPerm = bytesToG1ProofPoint(proof[0x3e0:0x460]); + p.libraCommitments[0] = bytesToG1ProofPoint(proof[0x460:0x4e0]); + // TEMP the boundary of what has already been read + uint256 boundary = 0x4e0; + + p.libraSum = bytesToFr(proof[boundary:boundary + 0x20]); + boundary += 0x20; + // Sumcheck univariates + for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N; i++) { + for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { + p.sumcheckUnivariates[i][j] = bytesToFr(proof[boundary:boundary + 0x20]); + boundary += 0x20; + } + } + + // Sumcheck evaluations + for (uint256 i = 0; i < NUMBER_OF_ENTITIES; i++) { + p.sumcheckEvaluations[i] = bytesToFr(proof[boundary:boundary + 0x20]); + boundary += 0x20; + } + + p.libraEvaluation = bytesToFr(proof[boundary:boundary + 0x20]); + boundary += 0x20; + + p.libraCommitments[1] = bytesToG1ProofPoint(proof[boundary:boundary + 0x80]); + boundary = boundary + 0x80; + p.libraCommitments[2] = bytesToG1ProofPoint(proof[boundary:boundary + 0x80]); + boundary = boundary + 0x80; + p.geminiMaskingPoly = bytesToG1ProofPoint(proof[boundary:boundary + 0x80]); + boundary = boundary + 0x80; + p.geminiMaskingEval = bytesToFr(proof[boundary:boundary + 0x20]); + boundary += 0x20; + + // Gemini + // Read gemini fold univariates + for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N - 1; i++) { + p.geminiFoldComms[i] = bytesToG1ProofPoint(proof[boundary:boundary + 0x80]); + boundary += 0x80; + } + + // Read gemini a evaluations + for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N; i++) { + p.geminiAEvaluations[i] = bytesToFr(proof[boundary:boundary + 0x20]); + boundary += 0x20; + } + + for (uint256 i = 0; i < 4; i++) { + p.libraPolyEvals[i] = bytesToFr(proof[boundary:boundary + 0x20]); + boundary += 0x20; + } + + // Shplonk + p.shplonkQ = bytesToG1ProofPoint(proof[boundary:boundary + 0x80]); + boundary = boundary + 0x80; + // KZG + p.kzgQuotient = bytesToG1ProofPoint(proof[boundary:boundary + 0x80]); + } +} + +function bytesToFr(bytes calldata proofSection) pure returns (Fr scalar) { + scalar = FrLib.fromBytes32(bytes32(proofSection)); +} + +// EC Point utilities +function convertProofPoint(Honk.G1ProofPoint memory input) pure returns (Honk.G1Point memory) { + return Honk.G1Point({x: input.x_0 | (input.x_1 << 136), y: input.y_0 | (input.y_1 << 136)}); +} + +function bytesToG1ProofPoint(bytes calldata proofSection) pure returns (Honk.G1ProofPoint memory point) { + point = Honk.G1ProofPoint({ + x_0: uint256(bytes32(proofSection[0x00:0x20])), + x_1: uint256(bytes32(proofSection[0x20:0x40])), + y_0: uint256(bytes32(proofSection[0x40:0x60])), + y_1: uint256(bytes32(proofSection[0x60:0x80])) + }); +} + +function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point memory) { + point.y = (Q - point.y) % Q; + return point; +} + + function pairing(Honk.G1Point memory rhs, Honk.G1Point memory lhs) view returns (bool) { + bytes memory input = abi.encodePacked( + rhs.x, + rhs.y, + // Fixed G1 point + uint256(0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2), + uint256(0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed), + uint256(0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b), + uint256(0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa), + lhs.x, + lhs.y, + // G1 point from VK + uint256(0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1), + uint256(0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0), + uint256(0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4), + uint256(0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55) + ); + + (bool success, bytes memory result) = address(0x08).staticcall(input); + bool decodedResult = abi.decode(result, (bool)); + return success && decodedResult; + } + + +library RelationsLib { + + function accumulateRelationEvaluations( + Fr[NUMBER_OF_ENTITIES] memory purportedEvaluations, + Honk.RelationParameters memory rp, + Fr[NUMBER_OF_ALPHAS] memory alphas, + Fr powPartialEval + ) internal pure returns (Fr accumulator) { + Fr[NUMBER_OF_SUBRELATIONS] memory evaluations; + + // Accumulate all relations in Ultra Honk - each with varying number of subrelations + accumulateArithmeticRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePermutationRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateLogDerivativeLookupRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateDeltaRangeRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateEllipticRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateAuxillaryRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulatePoseidonExternalRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePoseidonInternalRelation(purportedEvaluations, evaluations, powPartialEval); + // batch the subrelations with the alpha challenges to obtain the full honk relation + accumulator = scaleAndBatchSubrelations(evaluations, alphas); + } + + /** + * Aesthetic helper function that is used to index by enum into proof.sumcheckEvaluations, it avoids + * the relation checking code being cluttered with uint256 type casting, which is often a different colour in code + * editors, and thus is noisy. + */ + function wire(Fr[NUMBER_OF_ENTITIES] memory p, WIRE _wire) internal pure returns (Fr) { + return p[uint256(_wire)]; + } + + /** + * Ultra Arithmetic Relation + * + */ + function accumulateArithmeticRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + // Relation 0 + Fr q_arith = wire(p, WIRE.Q_ARITH); + { + Fr neg_half = Fr.wrap(0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000); // neg half modulo P + + Fr accum = (q_arith - Fr.wrap(3)) * (wire(p, WIRE.Q_M) * wire(p, WIRE.W_R) * wire(p, WIRE.W_L)) * neg_half; + accum = accum + (wire(p, WIRE.Q_L) * wire(p, WIRE.W_L)) + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_R)) + + (wire(p, WIRE.Q_O) * wire(p, WIRE.W_O)) + (wire(p, WIRE.Q_4) * wire(p, WIRE.W_4)) + wire(p, WIRE.Q_C); + accum = accum + (q_arith - ONE) * wire(p, WIRE.W_4_SHIFT); + accum = accum * q_arith; + accum = accum * domainSep; + evals[0] = accum; + } + + // Relation 1 + { + Fr accum = wire(p, WIRE.W_L) + wire(p, WIRE.W_4) - wire(p, WIRE.W_L_SHIFT) + wire(p, WIRE.Q_M); + accum = accum * (q_arith - Fr.wrap(2)); + accum = accum * (q_arith - ONE); + accum = accum * q_arith; + accum = accum * domainSep; + evals[1] = accum; + } + } + + function accumulatePermutationRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Honk.RelationParameters memory rp, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + Fr grand_product_numerator; + Fr grand_product_denominator; + + { + Fr num = wire(p, WIRE.W_L) + wire(p, WIRE.ID_1) * rp.beta + rp.gamma; + num = num * (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); + num = num * (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); + num = num * (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); + + grand_product_numerator = num; + } + { + Fr den = wire(p, WIRE.W_L) + wire(p, WIRE.SIGMA_1) * rp.beta + rp.gamma; + den = den * (wire(p, WIRE.W_R) + wire(p, WIRE.SIGMA_2) * rp.beta + rp.gamma); + den = den * (wire(p, WIRE.W_O) + wire(p, WIRE.SIGMA_3) * rp.beta + rp.gamma); + den = den * (wire(p, WIRE.W_4) + wire(p, WIRE.SIGMA_4) * rp.beta + rp.gamma); + + grand_product_denominator = den; + } + + // Contribution 2 + { + Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * grand_product_numerator; + + acc = acc + - ( + (wire(p, WIRE.Z_PERM_SHIFT) + (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) + * grand_product_denominator + ); + acc = acc * domainSep; + evals[2] = acc; + } + + // Contribution 3 + { + Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; + evals[3] = acc; + } + } + + function accumulateLogDerivativeLookupRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Honk.RelationParameters memory rp, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + Fr write_term; + Fr read_term; + + // Calculate the write term (the table accumulation) + { + write_term = wire(p, WIRE.TABLE_1) + rp.gamma + (wire(p, WIRE.TABLE_2) * rp.eta) + + (wire(p, WIRE.TABLE_3) * rp.etaTwo) + (wire(p, WIRE.TABLE_4) * rp.etaThree); + } + + // Calculate the write term + { + Fr derived_entry_1 = wire(p, WIRE.W_L) + rp.gamma + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); + Fr derived_entry_2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_M) * wire(p, WIRE.W_R_SHIFT); + Fr derived_entry_3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_C) * wire(p, WIRE.W_O_SHIFT); + + read_term = derived_entry_1 + (derived_entry_2 * rp.eta) + (derived_entry_3 * rp.etaTwo) + + (wire(p, WIRE.Q_O) * rp.etaThree); + } + + Fr read_inverse = wire(p, WIRE.LOOKUP_INVERSES) * write_term; + Fr write_inverse = wire(p, WIRE.LOOKUP_INVERSES) * read_term; + + Fr inverse_exists_xor = wire(p, WIRE.LOOKUP_READ_TAGS) + wire(p, WIRE.Q_LOOKUP) + - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); + + // Inverse calculated correctly relation + Fr accumulatorNone = read_term * write_term * wire(p, WIRE.LOOKUP_INVERSES) - inverse_exists_xor; + accumulatorNone = accumulatorNone * domainSep; + + // Inverse + Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * read_inverse - wire(p, WIRE.LOOKUP_READ_COUNTS) * write_inverse; + + evals[4] = accumulatorNone; + evals[5] = accumulatorOne; + } + + function accumulateDeltaRangeRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + Fr minus_one = ZERO - ONE; + Fr minus_two = ZERO - Fr.wrap(2); + Fr minus_three = ZERO - Fr.wrap(3); + + // Compute wire differences + Fr delta_1 = wire(p, WIRE.W_R) - wire(p, WIRE.W_L); + Fr delta_2 = wire(p, WIRE.W_O) - wire(p, WIRE.W_R); + Fr delta_3 = wire(p, WIRE.W_4) - wire(p, WIRE.W_O); + Fr delta_4 = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_4); + + // Contribution 6 + { + Fr acc = delta_1; + acc = acc * (delta_1 + minus_one); + acc = acc * (delta_1 + minus_two); + acc = acc * (delta_1 + minus_three); + acc = acc * wire(p, WIRE.Q_RANGE); + acc = acc * domainSep; + evals[6] = acc; + } + + // Contribution 7 + { + Fr acc = delta_2; + acc = acc * (delta_2 + minus_one); + acc = acc * (delta_2 + minus_two); + acc = acc * (delta_2 + minus_three); + acc = acc * wire(p, WIRE.Q_RANGE); + acc = acc * domainSep; + evals[7] = acc; + } + + // Contribution 8 + { + Fr acc = delta_3; + acc = acc * (delta_3 + minus_one); + acc = acc * (delta_3 + minus_two); + acc = acc * (delta_3 + minus_three); + acc = acc * wire(p, WIRE.Q_RANGE); + acc = acc * domainSep; + evals[8] = acc; + } + + // Contribution 9 + { + Fr acc = delta_4; + acc = acc * (delta_4 + minus_one); + acc = acc * (delta_4 + minus_two); + acc = acc * (delta_4 + minus_three); + acc = acc * wire(p, WIRE.Q_RANGE); + acc = acc * domainSep; + evals[9] = acc; + } + } + + struct EllipticParams { + // Points + Fr x_1; + Fr y_1; + Fr x_2; + Fr y_2; + Fr y_3; + Fr x_3; + // push accumulators into memory + Fr x_double_identity; + } + + function accumulateEllipticRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + EllipticParams memory ep; + ep.x_1 = wire(p, WIRE.W_R); + ep.y_1 = wire(p, WIRE.W_O); + + ep.x_2 = wire(p, WIRE.W_L_SHIFT); + ep.y_2 = wire(p, WIRE.W_4_SHIFT); + ep.y_3 = wire(p, WIRE.W_O_SHIFT); + ep.x_3 = wire(p, WIRE.W_R_SHIFT); + + Fr q_sign = wire(p, WIRE.Q_L); + Fr q_is_double = wire(p, WIRE.Q_M); + + // Contribution 10 point addition, x-coordinate check + // q_elliptic * (x3 + x2 + x1)(x2 - x1)(x2 - x1) - y2^2 - y1^2 + 2(y2y1)*q_sign = 0 + Fr x_diff = (ep.x_2 - ep.x_1); + Fr y1_sqr = (ep.y_1 * ep.y_1); + { + // Move to top + Fr partialEval = domainSep; + + Fr y2_sqr = (ep.y_2 * ep.y_2); + Fr y1y2 = ep.y_1 * ep.y_2 * q_sign; + Fr x_add_identity = (ep.x_3 + ep.x_2 + ep.x_1); + x_add_identity = x_add_identity * x_diff * x_diff; + x_add_identity = x_add_identity - y2_sqr - y1_sqr + y1y2 + y1y2; + + evals[10] = x_add_identity * partialEval * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); + } + + // Contribution 11 point addition, x-coordinate check + // q_elliptic * (q_sign * y1 + y3)(x2 - x1) + (x3 - x1)(y2 - q_sign * y1) = 0 + { + Fr y1_plus_y3 = ep.y_1 + ep.y_3; + Fr y_diff = ep.y_2 * q_sign - ep.y_1; + Fr y_add_identity = y1_plus_y3 * x_diff + (ep.x_3 - ep.x_1) * y_diff; + evals[11] = y_add_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); + } + + // Contribution 10 point doubling, x-coordinate check + // (x3 + x1 + x1) (4y1*y1) - 9 * x1 * x1 * x1 * x1 = 0 + // N.B. we're using the equivalence x1*x1*x1 === y1*y1 - curve_b to reduce degree by 1 + { + Fr x_pow_4 = (y1_sqr + Fr.wrap(17)) * ep.x_1; + Fr y1_sqr_mul_4 = y1_sqr + y1_sqr; + y1_sqr_mul_4 = y1_sqr_mul_4 + y1_sqr_mul_4; + Fr x1_pow_4_mul_9 = x_pow_4 * Fr.wrap(9); + + // NOTE: pushed into memory (stack >:'( ) + ep.x_double_identity = (ep.x_3 + ep.x_1 + ep.x_1) * y1_sqr_mul_4 - x1_pow_4_mul_9; + + Fr acc = ep.x_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; + evals[10] = evals[10] + acc; + } + + // Contribution 11 point doubling, y-coordinate check + // (y1 + y1) (2y1) - (3 * x1 * x1)(x1 - x3) = 0 + { + Fr x1_sqr_mul_3 = (ep.x_1 + ep.x_1 + ep.x_1) * ep.x_1; + Fr y_double_identity = x1_sqr_mul_3 * (ep.x_1 - ep.x_3) - (ep.y_1 + ep.y_1) * (ep.y_1 + ep.y_3); + evals[11] = evals[11] + y_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; + } + } + + // Constants for the auxiliary relation + Fr constant LIMB_SIZE = Fr.wrap(uint256(1) << 68); + Fr constant SUBLIMB_SHIFT = Fr.wrap(uint256(1) << 14); + + // Parameters used within the Auxiliary Relation + // A struct is used to work around stack too deep. This relation has alot of variables + struct AuxParams { + Fr limb_subproduct; + Fr non_native_field_gate_1; + Fr non_native_field_gate_2; + Fr non_native_field_gate_3; + Fr limb_accumulator_1; + Fr limb_accumulator_2; + Fr memory_record_check; + Fr partial_record_check; + Fr next_gate_access_type; + Fr record_delta; + Fr index_delta; + Fr adjacent_values_match_if_adjacent_indices_match; + Fr adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation; + Fr access_check; + Fr next_gate_access_type_is_boolean; + Fr ROM_consistency_check_identity; + Fr RAM_consistency_check_identity; + Fr timestamp_delta; + Fr RAM_timestamp_check_identity; + Fr memory_identity; + Fr index_is_monotonically_increasing; + Fr auxiliary_identity; + } + + function accumulateAuxillaryRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Honk.RelationParameters memory rp, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + AuxParams memory ap; + + /** + * Contribution 12 + * Non native field arithmetic gate 2 + * deg 4 + * + * _ _ + * / _ _ _ 14 \ + * q_2 . q_4 | (w_1 . w_2) + (w_1 . w_2) + (w_1 . w_4 + w_2 . w_3 - w_3) . 2 - w_3 - w_4 | + * \_ _/ + * + * + */ + ap.limb_subproduct = wire(p, WIRE.W_L) * wire(p, WIRE.W_R_SHIFT) + wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R); + ap.non_native_field_gate_2 = + (wire(p, WIRE.W_L) * wire(p, WIRE.W_4) + wire(p, WIRE.W_R) * wire(p, WIRE.W_O) - wire(p, WIRE.W_O_SHIFT)); + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 - wire(p, WIRE.W_4_SHIFT); + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 + ap.limb_subproduct; + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * wire(p, WIRE.Q_4); + + ap.limb_subproduct = ap.limb_subproduct * LIMB_SIZE; + ap.limb_subproduct = ap.limb_subproduct + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); + ap.non_native_field_gate_1 = ap.limb_subproduct; + ap.non_native_field_gate_1 = ap.non_native_field_gate_1 - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); + ap.non_native_field_gate_1 = ap.non_native_field_gate_1 * wire(p, WIRE.Q_O); + + ap.non_native_field_gate_3 = ap.limb_subproduct; + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 + wire(p, WIRE.W_4); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 * wire(p, WIRE.Q_M); + + Fr non_native_field_identity = + ap.non_native_field_gate_1 + ap.non_native_field_gate_2 + ap.non_native_field_gate_3; + non_native_field_identity = non_native_field_identity * wire(p, WIRE.Q_R); + + // ((((w2' * 2^14 + w1') * 2^14 + w3) * 2^14 + w2) * 2^14 + w1 - w4) * qm + // deg 2 + ap.limb_accumulator_1 = wire(p, WIRE.W_R_SHIFT) * SUBLIMB_SHIFT; + ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_L_SHIFT); + ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT; + ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_O); + ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT; + ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_R); + ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT; + ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_L); + ap.limb_accumulator_1 = ap.limb_accumulator_1 - wire(p, WIRE.W_4); + ap.limb_accumulator_1 = ap.limb_accumulator_1 * wire(p, WIRE.Q_4); + + // ((((w3' * 2^14 + w2') * 2^14 + w1') * 2^14 + w4) * 2^14 + w3 - w4') * qm + // deg 2 + ap.limb_accumulator_2 = wire(p, WIRE.W_O_SHIFT) * SUBLIMB_SHIFT; + ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_R_SHIFT); + ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT; + ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_L_SHIFT); + ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT; + ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_4); + ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT; + ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_O); + ap.limb_accumulator_2 = ap.limb_accumulator_2 - wire(p, WIRE.W_4_SHIFT); + ap.limb_accumulator_2 = ap.limb_accumulator_2 * wire(p, WIRE.Q_M); + + Fr limb_accumulator_identity = ap.limb_accumulator_1 + ap.limb_accumulator_2; + limb_accumulator_identity = limb_accumulator_identity * wire(p, WIRE.Q_O); // deg 3 + + /** + * MEMORY + * + * A RAM memory record contains a tuple of the following fields: + * * i: `index` of memory cell being accessed + * * t: `timestamp` of memory cell being accessed (used for RAM, set to 0 for ROM) + * * v: `value` of memory cell being accessed + * * a: `access` type of record. read: 0 = read, 1 = write + * * r: `record` of memory cell. record = access + index * eta + timestamp * eta_two + value * eta_three + * + * A ROM memory record contains a tuple of the following fields: + * * i: `index` of memory cell being accessed + * * v: `value1` of memory cell being accessed (ROM tables can store up to 2 values per index) + * * v2:`value2` of memory cell being accessed (ROM tables can store up to 2 values per index) + * * r: `record` of memory cell. record = index * eta + value2 * eta_two + value1 * eta_three + * + * When performing a read/write access, the values of i, t, v, v2, a, r are stored in the following wires + + * selectors, depending on whether the gate is a RAM read/write or a ROM read + * + * | gate type | i | v2/t | v | a | r | + * | --------- | -- | ----- | -- | -- | -- | + * | ROM | w1 | w2 | w3 | -- | w4 | + * | RAM | w1 | w2 | w3 | qc | w4 | + * + * (for accesses where `index` is a circuit constant, it is assumed the circuit will apply a copy constraint on + * `w2` to fix its value) + * + * + */ + + /** + * Memory Record Check + * Partial degree: 1 + * Total degree: 4 + * + * A ROM/ROM access gate can be evaluated with the identity: + * + * qc + w1 \eta + w2 \eta_two + w3 \eta_three - w4 = 0 + * + * For ROM gates, qc = 0 + */ + ap.memory_record_check = wire(p, WIRE.W_O) * rp.etaThree; + ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_R) * rp.etaTwo); + ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_L) * rp.eta); + ap.memory_record_check = ap.memory_record_check + wire(p, WIRE.Q_C); + ap.partial_record_check = ap.memory_record_check; // used in RAM consistency check; deg 1 or 4 + ap.memory_record_check = ap.memory_record_check - wire(p, WIRE.W_4); + + /** + * Contribution 13 & 14 + * ROM Consistency Check + * Partial degree: 1 + * Total degree: 4 + * + * For every ROM read, a set equivalence check is applied between the record witnesses, and a second set of + * records that are sorted. + * + * We apply the following checks for the sorted records: + * + * 1. w1, w2, w3 correctly map to 'index', 'v1, 'v2' for a given record value at w4 + * 2. index values for adjacent records are monotonically increasing + * 3. if, at gate i, index_i == index_{i + 1}, then value1_i == value1_{i + 1} and value2_i == value2_{i + 1} + * + */ + ap.index_delta = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_L); + ap.record_delta = wire(p, WIRE.W_4_SHIFT) - wire(p, WIRE.W_4); + + ap.index_is_monotonically_increasing = ap.index_delta * ap.index_delta - ap.index_delta; // deg 2 + + ap.adjacent_values_match_if_adjacent_indices_match = (ap.index_delta * MINUS_ONE + ONE) * ap.record_delta; // deg 2 + + evals[13] = ap.adjacent_values_match_if_adjacent_indices_match * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) + * (wire(p, WIRE.Q_AUX) * domainSep); // deg 5 + evals[14] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) + * (wire(p, WIRE.Q_AUX) * domainSep); // deg 5 + + ap.ROM_consistency_check_identity = ap.memory_record_check * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)); // deg 3 or 7 + + /** + * Contributions 15,16,17 + * RAM Consistency Check + * + * The 'access' type of the record is extracted with the expression `w_4 - ap.partial_record_check` + * (i.e. for an honest Prover `w1 * eta + w2 * eta^2 + w3 * eta^3 - w4 = access`. + * This is validated by requiring `access` to be boolean + * + * For two adjacent entries in the sorted list if _both_ + * A) index values match + * B) adjacent access value is 0 (i.e. next gate is a READ) + * then + * C) both values must match. + * The gate boolean check is + * (A && B) => C === !(A && B) || C === !A || !B || C + * + * N.B. it is the responsibility of the circuit writer to ensure that every RAM cell is initialized + * with a WRITE operation. + */ + Fr access_type = (wire(p, WIRE.W_4) - ap.partial_record_check); // will be 0 or 1 for honest Prover; deg 1 or 4 + ap.access_check = access_type * access_type - access_type; // check value is 0 or 1; deg 2 or 8 + + ap.next_gate_access_type = wire(p, WIRE.W_O_SHIFT) * rp.etaThree; + ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); + ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_L_SHIFT) * rp.eta); + ap.next_gate_access_type = wire(p, WIRE.W_4_SHIFT) - ap.next_gate_access_type; + + Fr value_delta = wire(p, WIRE.W_O_SHIFT) - wire(p, WIRE.W_O); + ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = ( + ap.index_delta * MINUS_ONE + ONE + ) * value_delta * (ap.next_gate_access_type * MINUS_ONE + ONE); // deg 3 or 6 + + // We can't apply the RAM consistency check identity on the final entry in the sorted list (the wires in the + // next gate would make the identity fail). We need to validate that its 'access type' bool is correct. Can't + // do with an arithmetic gate because of the `eta` factors. We need to check that the *next* gate's access + // type is correct, to cover this edge case + // deg 2 or 4 + ap.next_gate_access_type_is_boolean = + ap.next_gate_access_type * ap.next_gate_access_type - ap.next_gate_access_type; + + // Putting it all together... + evals[15] = ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation + * (wire(p, WIRE.Q_ARITH)) * (wire(p, WIRE.Q_AUX) * domainSep); // deg 5 or 8 + evals[16] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_ARITH)) * (wire(p, WIRE.Q_AUX) * domainSep); // deg 4 + evals[17] = ap.next_gate_access_type_is_boolean * (wire(p, WIRE.Q_ARITH)) * (wire(p, WIRE.Q_AUX) * domainSep); // deg 4 or 6 + + ap.RAM_consistency_check_identity = ap.access_check * (wire(p, WIRE.Q_ARITH)); // deg 3 or 9 + + /** + * RAM Timestamp Consistency Check + * + * | w1 | w2 | w3 | w4 | + * | index | timestamp | timestamp_check | -- | + * + * Let delta_index = index_{i + 1} - index_{i} + * + * Iff delta_index == 0, timestamp_check = timestamp_{i + 1} - timestamp_i + * Else timestamp_check = 0 + */ + ap.timestamp_delta = wire(p, WIRE.W_R_SHIFT) - wire(p, WIRE.W_R); + ap.RAM_timestamp_check_identity = + (ap.index_delta * MINUS_ONE + ONE) * ap.timestamp_delta - wire(p, WIRE.W_O); // deg 3 + + /** + * Complete Contribution 12 + * The complete RAM/ROM memory identity + * Partial degree: + */ + ap.memory_identity = ap.ROM_consistency_check_identity; // deg 3 or 6 + ap.memory_identity = + ap.memory_identity + ap.RAM_timestamp_check_identity * (wire(p, WIRE.Q_4) * wire(p, WIRE.Q_L)); // deg 4 + ap.memory_identity = ap.memory_identity + ap.memory_record_check * (wire(p, WIRE.Q_M) * wire(p, WIRE.Q_L)); // deg 3 or 6 + ap.memory_identity = ap.memory_identity + ap.RAM_consistency_check_identity; // deg 3 or 9 + + // (deg 3 or 9) + (deg 4) + (deg 3) + ap.auxiliary_identity = ap.memory_identity + non_native_field_identity + limb_accumulator_identity; + ap.auxiliary_identity = ap.auxiliary_identity * (wire(p, WIRE.Q_AUX) * domainSep); // deg 4 or 10 + evals[12] = ap.auxiliary_identity; + } + + struct PoseidonExternalParams { + Fr s1; + Fr s2; + Fr s3; + Fr s4; + Fr u1; + Fr u2; + Fr u3; + Fr u4; + Fr t0; + Fr t1; + Fr t2; + Fr t3; + Fr v1; + Fr v2; + Fr v3; + Fr v4; + Fr q_pos_by_scaling; + } + + function accumulatePoseidonExternalRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + PoseidonExternalParams memory ep; + + ep.s1 = wire(p, WIRE.W_L) + wire(p, WIRE.Q_L); + ep.s2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_R); + ep.s3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_O); + ep.s4 = wire(p, WIRE.W_4) + wire(p, WIRE.Q_4); + + ep.u1 = ep.s1 * ep.s1 * ep.s1 * ep.s1 * ep.s1; + ep.u2 = ep.s2 * ep.s2 * ep.s2 * ep.s2 * ep.s2; + ep.u3 = ep.s3 * ep.s3 * ep.s3 * ep.s3 * ep.s3; + ep.u4 = ep.s4 * ep.s4 * ep.s4 * ep.s4 * ep.s4; + // matrix mul v = M_E * u with 14 additions + ep.t0 = ep.u1 + ep.u2; // u_1 + u_2 + ep.t1 = ep.u3 + ep.u4; // u_3 + u_4 + ep.t2 = ep.u2 + ep.u2 + ep.t1; // 2u_2 + // ep.t2 += ep.t1; // 2u_2 + u_3 + u_4 + ep.t3 = ep.u4 + ep.u4 + ep.t0; // 2u_4 + // ep.t3 += ep.t0; // u_1 + u_2 + 2u_4 + ep.v4 = ep.t1 + ep.t1; + ep.v4 = ep.v4 + ep.v4 + ep.t3; + // ep.v4 += ep.t3; // u_1 + u_2 + 4u_3 + 6u_4 + ep.v2 = ep.t0 + ep.t0; + ep.v2 = ep.v2 + ep.v2 + ep.t2; + // ep.v2 += ep.t2; // 4u_1 + 6u_2 + u_3 + u_4 + ep.v1 = ep.t3 + ep.v2; // 5u_1 + 7u_2 + u_3 + 3u_4 + ep.v3 = ep.t2 + ep.v4; // u_1 + 3u_2 + 5u_3 + 7u_4 + + ep.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_EXTERNAL) * domainSep; + evals[18] = evals[18] + ep.q_pos_by_scaling * (ep.v1 - wire(p, WIRE.W_L_SHIFT)); + + evals[19] = evals[19] + ep.q_pos_by_scaling * (ep.v2 - wire(p, WIRE.W_R_SHIFT)); + + evals[20] = evals[20] + ep.q_pos_by_scaling * (ep.v3 - wire(p, WIRE.W_O_SHIFT)); + + evals[21] = evals[21] + ep.q_pos_by_scaling * (ep.v4 - wire(p, WIRE.W_4_SHIFT)); + } + + struct PoseidonInternalParams { + Fr u1; + Fr u2; + Fr u3; + Fr u4; + Fr u_sum; + Fr v1; + Fr v2; + Fr v3; + Fr v4; + Fr s1; + Fr q_pos_by_scaling; + } + + function accumulatePoseidonInternalRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + PoseidonInternalParams memory ip; + + // add round constants + ip.s1 = wire(p, WIRE.W_L) + wire(p, WIRE.Q_L); + + // apply s-box round + ip.u1 = ip.s1 * ip.s1 * ip.s1 * ip.s1 * ip.s1; + ip.u2 = wire(p, WIRE.W_R); + ip.u3 = wire(p, WIRE.W_O); + ip.u4 = wire(p, WIRE.W_4); + + // matrix mul with v = M_I * u 4 muls and 7 additions + ip.u_sum = ip.u1 + ip.u2 + ip.u3 + ip.u4; + + ip.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_INTERNAL) * domainSep; + + ip.v1 = ip.u1 * FrLib.from(0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7) + ip.u_sum; + evals[22] = evals[22] + ip.q_pos_by_scaling * (ip.v1 - wire(p, WIRE.W_L_SHIFT)); + + ip.v2 = ip.u2 * FrLib.from(0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b) + ip.u_sum; + evals[23] = evals[23] + ip.q_pos_by_scaling * (ip.v2 - wire(p, WIRE.W_R_SHIFT)); + + ip.v3 = ip.u3 * FrLib.from(0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15) + ip.u_sum; + evals[24] = evals[24] + ip.q_pos_by_scaling * (ip.v3 - wire(p, WIRE.W_O_SHIFT)); + + ip.v4 = ip.u4 * FrLib.from(0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b) + ip.u_sum; + evals[25] = evals[25] + ip.q_pos_by_scaling * (ip.v4 - wire(p, WIRE.W_4_SHIFT)); + } + + function scaleAndBatchSubrelations( + Fr[NUMBER_OF_SUBRELATIONS] memory evaluations, + Fr[NUMBER_OF_ALPHAS] memory subrelationChallenges + ) internal pure returns (Fr accumulator) { + accumulator = accumulator + evaluations[0]; + + for (uint256 i = 1; i < NUMBER_OF_SUBRELATIONS; ++i) { + accumulator = accumulator + evaluations[i] * subrelationChallenges[i - 1]; + } + } +} + +struct ShpleminiIntermediates { + Fr unshiftedScalar; + Fr shiftedScalar; + // Scalar to be multiplied by [1]₁ + Fr constantTermAccumulator; + // Accumulator for powers of rho + Fr batchingChallenge; + // Linear combination of multilinear (sumcheck) evaluations and powers of rho + Fr batchedEvaluation; + Fr[4] denominators; + Fr[4] batchingScalars; + Fr[CONST_PROOF_SIZE_LOG_N + 1] inverse_vanishing_denominators; + +} + +library CommitmentSchemeLib { + using FrLib for Fr; + + function computeSquares(Fr r) internal pure returns (Fr[CONST_PROOF_SIZE_LOG_N] memory squares) { + squares[0] = r; + for (uint256 i = 1; i < CONST_PROOF_SIZE_LOG_N; ++i) { + squares[i] = squares[i - 1].sqr(); + } + } + + function computeInvertedGeminiDenominators( + Fr shplonkZ, + Fr[CONST_PROOF_SIZE_LOG_N] memory eval_challenge_powers, + uint256 logSize + ) internal view returns (Fr[CONST_PROOF_SIZE_LOG_N + 1] memory inverse_vanishing_evals) { + inverse_vanishing_evals[0] = (shplonkZ- eval_challenge_powers[0]).invert(); + + for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N; ++i) { + Fr round_inverted_denominator = ZERO; + if (i <= logSize + 1) { + round_inverted_denominator = (shplonkZ+ eval_challenge_powers[i]).invert(); + } + inverse_vanishing_evals[i + 1] = round_inverted_denominator; + } + } + + function computeGeminiBatchedUnivariateEvaluation( + Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckUChallenges, + Fr batchedEvalAccumulator, + Fr[CONST_PROOF_SIZE_LOG_N] memory geminiEvaluations, + Fr[CONST_PROOF_SIZE_LOG_N] memory geminiEvalChallengePowers + ) internal view returns (Fr a_0_pos) { + for (uint256 i = CONST_PROOF_SIZE_LOG_N; i > 0; --i) { + Fr challengePower = geminiEvalChallengePowers[i - 1]; + Fr u = sumcheckUChallenges[i - 1]; + + Fr batchedEvalRoundAcc = ( + (challengePower * batchedEvalAccumulator * Fr.wrap(2)) + - geminiEvaluations[i - 1] * (challengePower * (ONE - u) - u) + ); + // Divide by the denominator + batchedEvalRoundAcc = batchedEvalRoundAcc * (challengePower * (ONE - u) + u).invert(); + + if (i <= LOG_N) { + batchedEvalAccumulator = batchedEvalRoundAcc; + } + } + + a_0_pos = batchedEvalAccumulator; + } +} + +interface IVerifier { + function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool); +} + + contract HonkVerifier is IVerifier { + using FrLib for Fr; + + function loadVerificationKey() internal pure returns (Honk.VerificationKey memory vk) { + vk = HonkVerificationKey.loadVerificationKey(); + } + + + // Errors + error PublicInputsLengthWrong(); + error SumcheckFailed(); + error ShpleminiFailed(); + error GeminiChallengeInSubgroup(); + error ConsistencyCheckFailed(); + + function verify(bytes calldata proof, bytes32[] calldata publicInputs) public view override returns (bool verified) { + Honk.VerificationKey memory vk = loadVerificationKey(); + Honk.ZKProof memory p = ZKTranscriptLib.loadProof(proof); + + if (publicInputs.length != vk.publicInputsSize) { + revert PublicInputsLengthWrong(); + } + + // Generate the fiat shamir challenges for the whole protocol + ZKTranscript memory t = ZKTranscriptLib.generateTranscript(p, publicInputs, NUMBER_OF_PUBLIC_INPUTS); + + // Derive public input delta + t.relationParameters.publicInputsDelta = computePublicInputDelta( + publicInputs, t.relationParameters.beta, t.relationParameters.gamma, p.publicInputsOffset + ); + + // Sumcheck + if (!verifySumcheck(p, t)) revert SumcheckFailed(); + + if (!verifyShplemini(p, vk, t)) revert ShpleminiFailed(); + + verified = true; + + } + + function computePublicInputDelta(bytes32[] memory publicInputs, Fr beta, Fr gamma, uint256 offset) + internal + view + returns (Fr publicInputDelta) + { + Fr numerator = ONE; + Fr denominator = ONE; + + Fr numeratorAcc = gamma + (beta * FrLib.from(N + offset)); + Fr denominatorAcc = gamma - (beta * FrLib.from(offset + 1)); + + { + for (uint256 i = 0; i < NUMBER_OF_PUBLIC_INPUTS; i++) { + Fr pubInput = FrLib.fromBytes32(publicInputs[i]); + + numerator = numerator * (numeratorAcc + pubInput); + denominator = denominator * (denominatorAcc + pubInput); + + numeratorAcc = numeratorAcc + beta; + denominatorAcc = denominatorAcc - beta; + } + } + + publicInputDelta = FrLib.div(numerator, denominator); + } + + function verifySumcheck(Honk.ZKProof memory proof, ZKTranscript memory tp) internal view returns (bool verified) { + Fr roundTargetSum = tp.libraChallenge * proof.libraSum; // default 0 + Fr powPartialEvaluation = ONE; + + // We perform sumcheck reductions over log n rounds ( the multivariate degree ) + for (uint256 round; round < LOG_N; ++round) { + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariate = proof.sumcheckUnivariates[round]; + Fr totalSum = roundUnivariate[0] + roundUnivariate[1]; + if (totalSum != roundTargetSum) revert SumcheckFailed(); + + Fr roundChallenge = tp.sumCheckUChallenges[round]; + + // Update the round target for the next rounf + roundTargetSum = computeNextTargetSum(roundUnivariate, roundChallenge); + powPartialEvaluation = + powPartialEvaluation * (ONE + roundChallenge * (tp.gateChallenges[round] - ONE)); + } + + // Last round + Fr grandHonkRelationSum = RelationsLib.accumulateRelationEvaluations( + proof.sumcheckEvaluations, tp.relationParameters, tp.alphas, powPartialEvaluation + ); + + Fr evaluation = ONE; + for (uint256 i = 2; i < LOG_N; i++) { + evaluation = evaluation * tp.sumCheckUChallenges[i]; + } + + grandHonkRelationSum = grandHonkRelationSum * (ONE - evaluation) + proof.libraEvaluation * tp.libraChallenge; + verified = (grandHonkRelationSum == roundTargetSum); + } + + // Return the new target sum for the next sumcheck round + function computeNextTargetSum(Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, Fr roundChallenge) + internal + view + returns (Fr targetSum) + { + // TODO: inline + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), + Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000240), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), + Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80) + ]; + + // To compute the next target sum, we evaluate the given univariate at a point u (challenge). + + // TODO: opt: use same array mem for each iteratioon + // Performing Barycentric evaluations + // Compute B(x) + Fr numeratorValue = ONE; + for (uint256 i; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { + numeratorValue = numeratorValue * (roundChallenge - Fr.wrap(i)); + } + + // Calculate domain size N of inverses -- TODO: montgomery's trick + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory denominatorInverses; + for (uint256 i; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { + denominatorInverses[i] = FrLib.invert(BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * (roundChallenge - Fr.wrap(i))); + } + + for (uint256 i; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { + targetSum = targetSum + roundUnivariates[i]*denominatorInverses[i]; + } + + // Scale the sum by the value of B(x) + targetSum = targetSum * numeratorValue; + } + + function verifyShplemini(Honk.ZKProof memory proof, Honk.VerificationKey memory vk, ZKTranscript memory tp) + internal + view + returns (bool verified) + { + ShpleminiIntermediates memory mem; // stack + + // - Compute vector (r, r², ... , r²⁽ⁿ⁻¹⁾), where n = log_circuit_size + Fr[CONST_PROOF_SIZE_LOG_N] memory powers_of_evaluation_challenge = CommitmentSchemeLib.computeSquares(tp.geminiR); + // Arrays hold values that will be linearly combined for the gemini and shplonk batch openings + Fr[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 3 + 3] memory scalars; + Honk.G1Point[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 3 + 3] memory commitments; + + mem.inverse_vanishing_denominators = + CommitmentSchemeLib.computeInvertedGeminiDenominators(tp.shplonkZ, powers_of_evaluation_challenge, LOG_N); + + mem.unshiftedScalar = + mem.inverse_vanishing_denominators[0] + (tp.shplonkNu * mem.inverse_vanishing_denominators[1]); + mem.shiftedScalar = tp.geminiR.invert() + * (mem.inverse_vanishing_denominators[0] - (tp.shplonkNu * mem.inverse_vanishing_denominators[1])); + + scalars[0] = ONE; + commitments[0] = convertProofPoint(proof.shplonkQ); + + mem.batchedEvaluation = proof.geminiMaskingEval; + mem.batchingChallenge = tp.rho; + scalars[1] = mem.unshiftedScalar.neg(); + for (uint256 i = 0; i < NUMBER_UNSHIFTED; ++i) { + scalars[i + 2] = mem.unshiftedScalar.neg() * mem.batchingChallenge; + mem.batchedEvaluation = mem.batchedEvaluation + (proof.sumcheckEvaluations[i] * mem.batchingChallenge); + mem.batchingChallenge = mem.batchingChallenge * tp.rho; + } + + for (uint256 i = NUMBER_UNSHIFTED; i < NUMBER_OF_ENTITIES; ++i) { + scalars[i + 2] = mem.shiftedScalar.neg() * mem.batchingChallenge; + mem.batchedEvaluation = mem.batchedEvaluation + (proof.sumcheckEvaluations[i] * mem.batchingChallenge); + mem.batchingChallenge = mem.batchingChallenge * tp.rho; + } + + commitments[1] = convertProofPoint(proof.geminiMaskingPoly); + + commitments[2] = vk.qm; + commitments[3] = vk.qc; + commitments[4] = vk.ql; + commitments[5] = vk.qr; + commitments[6] = vk.qo; + commitments[7] = vk.q4; + commitments[8] = vk.qLookup; + commitments[9] = vk.qArith; + commitments[10] = vk.qDeltaRange; + commitments[11] = vk.qElliptic; + commitments[12] = vk.qAux; + commitments[13] = vk.qPoseidon2External; + commitments[14] = vk.qPoseidon2Internal; + commitments[15] = vk.s1; + commitments[16] = vk.s2; + commitments[17] = vk.s3; + commitments[18] = vk.s4; + commitments[19] = vk.id1; + commitments[20] = vk.id2; + commitments[21] = vk.id3; + commitments[22] = vk.id4; + commitments[23] = vk.t1; + commitments[24] = vk.t2; + commitments[25] = vk.t3; + commitments[26] = vk.t4; + commitments[27] = vk.lagrangeFirst; + commitments[28] = vk.lagrangeLast; + + // Accumulate proof points + commitments[29] = convertProofPoint(proof.w1); + commitments[30] = convertProofPoint(proof.w2); + commitments[31] = convertProofPoint(proof.w3); + commitments[32] = convertProofPoint(proof.w4); + commitments[33] = convertProofPoint(proof.zPerm); + commitments[34] = convertProofPoint(proof.lookupInverses); + commitments[35] = convertProofPoint(proof.lookupReadCounts); + commitments[36] = convertProofPoint(proof.lookupReadTags); + + // to be Shifted + commitments[37] = convertProofPoint(proof.w1); + commitments[38] = convertProofPoint(proof.w2); + commitments[39] = convertProofPoint(proof.w3); + commitments[40] = convertProofPoint(proof.w4); + commitments[41] = convertProofPoint(proof.zPerm); + + mem.constantTermAccumulator = ZERO; + mem.batchingChallenge = tp.shplonkNu.sqr(); + uint256 boundary = NUMBER_OF_ENTITIES + 2; + + for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N - 1; ++i) { + bool dummy_round = i >= (LOG_N - 1); + + Fr scalingFactor = ZERO; + if (!dummy_round) { + scalingFactor = mem.batchingChallenge * mem.inverse_vanishing_denominators[i + 2]; + scalars[boundary + i] = scalingFactor.neg(); + } + + mem.constantTermAccumulator = + mem.constantTermAccumulator + (scalingFactor * proof.geminiAEvaluations[i + 1]); + mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu; + + commitments[boundary + i] = convertProofPoint(proof.geminiFoldComms[i]); + } + + boundary += CONST_PROOF_SIZE_LOG_N - 1; + + // Add contributions from A₀(r) and A₀(-r) to constant_term_accumulator: + // Compute evaluation A₀(r) + Fr a_0_pos = CommitmentSchemeLib.computeGeminiBatchedUnivariateEvaluation( + tp.sumCheckUChallenges, + mem.batchedEvaluation, + proof.geminiAEvaluations, + powers_of_evaluation_challenge + ); + + mem.constantTermAccumulator = mem.constantTermAccumulator + (a_0_pos * mem.inverse_vanishing_denominators[0]); + mem.constantTermAccumulator = mem.constantTermAccumulator + + (proof.geminiAEvaluations[0] * tp.shplonkNu * mem.inverse_vanishing_denominators[1]); + + // Finalise the batch opening claim + mem.denominators[0] = ONE.div(tp.shplonkZ - tp.geminiR); + mem.denominators[1] = ONE.div(tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR); + mem.denominators[2] = mem.denominators[0]; + mem.denominators[3] = mem.denominators[0]; + + mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu; + for (uint256 i = 0; i < 4; i++) { + Fr scalingFactor = mem.denominators[i] * mem.batchingChallenge; + mem.batchingScalars[i] = scalingFactor.neg(); + mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu; + mem.constantTermAccumulator = mem.constantTermAccumulator + scalingFactor * proof.libraPolyEvals[i]; + } + scalars[boundary] = mem.batchingScalars[0]; + scalars[boundary + 1] = mem.batchingScalars[1] + mem.batchingScalars[2]; + scalars[boundary + 2] = mem.batchingScalars[3]; + + for (uint256 i = 0; i < 3; i++) { + commitments[boundary++] = convertProofPoint(proof.libraCommitments[i]); + } + + commitments[boundary] = Honk.G1Point({x: 1, y: 2}); + scalars[boundary++] = mem.constantTermAccumulator; + + if (! checkEvalsConsistency(proof.libraPolyEvals, tp.geminiR, tp.sumCheckUChallenges, proof.libraEvaluation)) { + revert ConsistencyCheckFailed(); + } + Honk.G1Point memory quotient_commitment = convertProofPoint(proof.kzgQuotient); + + commitments[boundary] = quotient_commitment; + scalars[boundary] = tp.shplonkZ; // evaluation challenge + + Honk.G1Point memory P_0 = batchMul(commitments, scalars); + Honk.G1Point memory P_1 = negateInplace(quotient_commitment); + + return pairing(P_0, P_1); + } + + struct SmallSubgroupIpaIntermediates { + Fr[SUBGROUP_SIZE] challengePolyLagrange; + Fr challengePolyEval; + Fr lagrangeFirst; + Fr lagrangeLast; + Fr rootPower; + Fr[SUBGROUP_SIZE] denominators; + Fr diff; + } + + function checkEvalsConsistency( + Fr[4] memory libraPolyEvals, + Fr geminiR, + Fr[CONST_PROOF_SIZE_LOG_N] memory uChallenges, + Fr libraEval + ) internal view returns (bool check) { + Fr vanishingPolyEval = geminiR.pow(SUBGROUP_SIZE) - ONE; + if (vanishingPolyEval == ZERO) { + revert GeminiChallengeInSubgroup(); + } + + SmallSubgroupIpaIntermediates memory mem; + mem.challengePolyLagrange[0] = ONE; + for (uint256 round = 0; round < CONST_PROOF_SIZE_LOG_N; round++) { + uint256 currIdx = 1 + 9 * round; + mem.challengePolyLagrange[currIdx] = ONE; + for (uint256 idx = currIdx + 1; idx < currIdx + 9; idx++) { + mem.challengePolyLagrange[idx] = mem.challengePolyLagrange[idx - 1] * uChallenges[round]; + } + } + + mem.rootPower = ONE; + mem.challengePolyEval = ZERO; + for (uint256 idx = 0; idx < SUBGROUP_SIZE; idx++) { + mem.denominators[idx] = mem.rootPower * geminiR - ONE; + mem.denominators[idx] = mem.denominators[idx].invert(); + mem.challengePolyEval = mem.challengePolyEval + mem.challengePolyLagrange[idx] * mem.denominators[idx]; + mem.rootPower = mem.rootPower * SUBGROUP_GENERATOR_INVERSE; + } + + Fr numerator = vanishingPolyEval * Fr.wrap(SUBGROUP_SIZE).invert(); + mem.challengePolyEval = mem.challengePolyEval * numerator; + mem.lagrangeFirst = mem.denominators[0] * numerator; + mem.lagrangeLast = mem.denominators[SUBGROUP_SIZE - 1] * numerator; + + mem.diff = mem.lagrangeFirst * libraPolyEvals[2]; + + mem.diff = mem.diff + + (geminiR - SUBGROUP_GENERATOR_INVERSE) + * (libraPolyEvals[1] - libraPolyEvals[2] - libraPolyEvals[0] * mem.challengePolyEval); + mem.diff = mem.diff + mem.lagrangeLast * (libraPolyEvals[2] - libraEval) - vanishingPolyEval * libraPolyEvals[3]; + + check = mem.diff == ZERO; + } + + function batchMul( + Honk.G1Point[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 3 + 3] memory base, + Fr[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 3 + 3] memory scalars + ) internal view returns (Honk.G1Point memory result) { + uint256 limit = NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 3 + 3; + assembly { + let success := 0x01 + let free := mload(0x40) + + // Write the original into the accumulator + // Load into memory for ecMUL, leave offset for eccAdd result + // base is an array of pointers, so we have to dereference them + mstore(add(free, 0x40), mload(mload(base))) + mstore(add(free, 0x60), mload(add(0x20, mload(base)))) + // Add scalar + mstore(add(free, 0x80), mload(scalars)) + success := and(success, staticcall(gas(), 7, add(free, 0x40), 0x60, free, 0x40)) + + let count := 0x01 + for {} lt(count, limit) { count := add(count, 1) } { + // Get loop offsets + let base_base := add(base, mul(count, 0x20)) + let scalar_base := add(scalars, mul(count, 0x20)) + + mstore(add(free, 0x40), mload(mload(base_base))) + mstore(add(free, 0x60), mload(add(0x20, mload(base_base)))) + // Add scalar + mstore(add(free, 0x80), mload(scalar_base)) + + success := and(success, staticcall(gas(), 7, add(free, 0x40), 0x60, add(free, 0x40), 0x40)) + // accumulator = accumulator + accumulator_2 + success := and(success, staticcall(gas(), 6, free, 0x80, free, 0x40)) + } + + // Return the result - i hate this + mstore(result, mload(free)) + mstore(add(result, 0x20), mload(add(free, 0x20))) + } + } +} + +)"; + +inline std::string get_honk_zk_solidity_verifier(auto const& verification_key) +{ + std::ostringstream stream; + output_vk_sol_ultra_honk(stream, verification_key, "HonkVerificationKey"); + return stream.str() + HONK_ZK_CONTRACT_SOURCE; +} diff --git a/barretenberg/cpp/src/barretenberg/nodejs_module/lmdb_store/lmdb_store_wrapper.cpp b/barretenberg/cpp/src/barretenberg/nodejs_module/lmdb_store/lmdb_store_wrapper.cpp index e93b5902f816..3538da8caa8f 100644 --- a/barretenberg/cpp/src/barretenberg/nodejs_module/lmdb_store/lmdb_store_wrapper.cpp +++ b/barretenberg/cpp/src/barretenberg/nodejs_module/lmdb_store/lmdb_store_wrapper.cpp @@ -248,7 +248,18 @@ StatsResponse LMDBStoreWrapper::get_stats() BoolResponse LMDBStoreWrapper::close() { + // prevent this store from receiving further messages + _msg_processor.close(); + + { + // close all of the open read cursors + std::lock_guard cursors(_cursor_mutex); + _cursors.clear(); + } + + // and finally close the database handle _store.reset(nullptr); + return { true }; } diff --git a/barretenberg/cpp/src/barretenberg/nodejs_module/util/message_processor.hpp b/barretenberg/cpp/src/barretenberg/nodejs_module/util/message_processor.hpp index d6dd84c2846e..3a646c71c7e1 100644 --- a/barretenberg/cpp/src/barretenberg/nodejs_module/util/message_processor.hpp +++ b/barretenberg/cpp/src/barretenberg/nodejs_module/util/message_processor.hpp @@ -56,7 +56,9 @@ class AsyncMessageProcessor { // complete on an separate thread auto deferred = std::make_shared(env); - if (info.Length() < 1) { + if (!open) { + deferred->Reject(Napi::TypeError::New(env, "Message processor is closed").Value()); + } else if (info.Length() < 1) { deferred->Reject(Napi::TypeError::New(env, "Wrong number of arguments").Value()); } else if (!info[0].IsBuffer()) { deferred->Reject(Napi::TypeError::New(env, "Argument must be a buffer").Value()); @@ -82,8 +84,11 @@ class AsyncMessageProcessor { return deferred->Promise(); } + void close() { open = false; } + private: bb::messaging::MessageDispatcher dispatcher; + bool open = true; template void _register_handler(uint32_t msgType, const std::function& fn) diff --git a/barretenberg/cpp/src/barretenberg/plonk/proof_system/verification_key/sol_gen.hpp b/barretenberg/cpp/src/barretenberg/plonk/proof_system/verification_key/sol_gen.hpp index e270a46fe755..21f3c7d3b641 100644 --- a/barretenberg/cpp/src/barretenberg/plonk/proof_system/verification_key/sol_gen.hpp +++ b/barretenberg/cpp/src/barretenberg/plonk/proof_system/verification_key/sol_gen.hpp @@ -145,10 +145,6 @@ inline void output_vk_sol(std::ostream& os, std::shared_ptr(key->circuit_type); switch (circuit_type) { - case CircuitType::STANDARD: { - return output_vk_sol_standard(os, key, class_name); - break; - } case CircuitType::ULTRA: { return output_vk_sol_ultra(os, key, class_name); break; diff --git a/barretenberg/cpp/src/barretenberg/plonk_honk_shared/composer/composer_lib.test.cpp b/barretenberg/cpp/src/barretenberg/plonk_honk_shared/composer/composer_lib.test.cpp index 1d2729bc725d..fa41d4493c12 100644 --- a/barretenberg/cpp/src/barretenberg/plonk_honk_shared/composer/composer_lib.test.cpp +++ b/barretenberg/cpp/src/barretenberg/plonk_honk_shared/composer/composer_lib.test.cpp @@ -43,8 +43,9 @@ TEST_F(ComposerLibTests, LookupReadCounts) auto accumulators = plookup::get_lookup_accumulators(UINT32_XOR, left, right, /*is_2_to_1_lookup*/ true); builder.create_gates_from_plookup_accumulators(UINT32_XOR, accumulators, left_idx, right_idx); - EXPECT_EQ(builder.lookup_tables.size(), 1); // we only used a single table - EXPECT_EQ(builder.lookup_tables[0].size(), 4096); // table has size 64*64 (6 bit operands) + EXPECT_EQ(builder.lookup_tables.size(), 2); // we only used two tables, first for 6 bits, second for 2 bits + EXPECT_EQ(builder.lookup_tables[0].size(), 4096); // first table has size 64*64 (6 bit operands) + EXPECT_EQ(builder.lookup_tables[1].size(), 16); // first table has size 4*4 (2 bit operands) size_t circuit_size = 8192; @@ -59,15 +60,19 @@ TEST_F(ComposerLibTests, LookupReadCounts) // The uint32 XOR lookup table is constructed for 6 bit operands via double for loop that iterates through the left // operand externally (0 to 63) then the right operand internally (0 to 63). Computing (1 XOR 5) will thus result in - // 1 lookup from the (1*64 + 5)th index in the table and 5 lookups from the (0*64 + 0)th index (for the remaining 5 - // limbs that are all 0). The counts and tags at all other indices should be zero. + // 1 lookup from the (1*64 + 5)th index in the table and 4 lookups from the (0*64 + 0)th index (for the remaining 4 + // 6-bits limbs that are all 0) and one lookup from second table from the (64 * 64 + 0) index (for last 2 bits). + // The counts and tags at all other indices should be zero. for (auto [idx, count, tag] : zip_polys(read_counts, read_tags)) { if (idx == (0 + offset)) { - EXPECT_EQ(count, 5); + EXPECT_EQ(count, 4); EXPECT_EQ(tag, 1); } else if (idx == (69 + offset)) { EXPECT_EQ(count, 1); EXPECT_EQ(tag, 1); + } else if (idx == (64 * 64 + offset)) { + EXPECT_EQ(count, 1); + EXPECT_EQ(tag, 1); } else { EXPECT_EQ(count, 0); EXPECT_EQ(tag, 0); diff --git a/barretenberg/cpp/src/barretenberg/solidity_helpers/circuits/add_2_circuit.hpp b/barretenberg/cpp/src/barretenberg/solidity_helpers/circuits/add_2_circuit.hpp index eb3d8bb59f8a..822623d5dc95 100644 --- a/barretenberg/cpp/src/barretenberg/solidity_helpers/circuits/add_2_circuit.hpp +++ b/barretenberg/cpp/src/barretenberg/solidity_helpers/circuits/add_2_circuit.hpp @@ -2,10 +2,11 @@ #include "barretenberg/stdlib/primitives/field/field.hpp" #include "barretenberg/stdlib/primitives/witness/witness.hpp" -template class Add2Circuit { +class Add2Circuit { public: - typedef bb::stdlib::public_witness_t public_witness_ct; - typedef bb::stdlib::field_t field_ct; + using Builder = bb::UltraCircuitBuilder; + using public_witness_ct = bb::stdlib::public_witness_t; + using field_ct = bb::stdlib::field_t; // Three public inputs static Builder generate(uint256_t inputs[]) diff --git a/barretenberg/cpp/src/barretenberg/solidity_helpers/circuits/blake_circuit.hpp b/barretenberg/cpp/src/barretenberg/solidity_helpers/circuits/blake_circuit.hpp index ff31d816301d..9646c8bbcf48 100644 --- a/barretenberg/cpp/src/barretenberg/solidity_helpers/circuits/blake_circuit.hpp +++ b/barretenberg/cpp/src/barretenberg/solidity_helpers/circuits/blake_circuit.hpp @@ -3,8 +3,9 @@ #include "barretenberg/stdlib/primitives/field/field.hpp" #include "barretenberg/stdlib/primitives/witness/witness.hpp" -template class BlakeCircuit { +class BlakeCircuit { public: + using Builder = bb::UltraCircuitBuilder; using field_ct = bb::stdlib::field_t; using public_witness_ct = bb::stdlib::public_witness_t; using byte_array_ct = bb::stdlib::byte_array; diff --git a/barretenberg/cpp/src/barretenberg/solidity_helpers/circuits/ecdsa_circuit.hpp b/barretenberg/cpp/src/barretenberg/solidity_helpers/circuits/ecdsa_circuit.hpp index 92bbd07f99f3..1c24902107af 100644 --- a/barretenberg/cpp/src/barretenberg/solidity_helpers/circuits/ecdsa_circuit.hpp +++ b/barretenberg/cpp/src/barretenberg/solidity_helpers/circuits/ecdsa_circuit.hpp @@ -13,9 +13,9 @@ #include "barretenberg/stdlib/primitives/witness/witness.hpp" namespace bb { - -template class EcdsaCircuit { +class EcdsaCircuit { public: + using Builder = bb::UltraCircuitBuilder; using field_ct = stdlib::field_t; using bool_ct = stdlib::bool_t; using public_witness_ct = stdlib::public_witness_t; @@ -86,7 +86,7 @@ template class EcdsaCircuit { sig); // Assert the signature is true, we hash the message inside the verify sig stdlib call - bool_ct is_true = bool_ct(1); + bool_ct is_true = bool_ct(true); signature_result.must_imply(is_true, "signature verification failed"); return builder; diff --git a/barretenberg/cpp/src/barretenberg/solidity_helpers/circuits/recursive_circuit.hpp b/barretenberg/cpp/src/barretenberg/solidity_helpers/circuits/recursive_circuit.hpp index 66a429be92f0..58f750fdf6d8 100644 --- a/barretenberg/cpp/src/barretenberg/solidity_helpers/circuits/recursive_circuit.hpp +++ b/barretenberg/cpp/src/barretenberg/solidity_helpers/circuits/recursive_circuit.hpp @@ -14,12 +14,12 @@ using namespace bb::plonk; using namespace stdlib; using numeric::uint256_t; -template class RecursiveCircuit { +class RecursiveCircuit { using InnerComposer = UltraComposer; - using InnerBuilder = typename InnerComposer::CircuitBuilder; + using Builder = UltraCircuitBuilder; - using inner_curve = bn254; - using outer_curve = bn254; + using inner_curve = bn254; + using outer_curve = bn254; using verification_key_pt = recursion::verification_key; using recursive_settings = recursion::recursive_ultra_verifier_settings; @@ -34,20 +34,15 @@ template class RecursiveCircuit { using inner_scalar_field = typename inner_curve::ScalarFieldNative; using outer_scalar_field = typename outer_curve::BaseFieldNative; using pairing_target_field = bb::fq12; - static constexpr bool is_ultra_to_ultra = std::is_same_v; - using ProverOfInnerCircuit = - std::conditional_t; - using VerifierOfInnerProof = - std::conditional_t; - using RecursiveSettings = - std::conditional_t; + using ProverOfInnerCircuit = plonk::UltraProver; + using VerifierOfInnerProof = plonk::UltraVerifier; struct circuit_outputs { stdlib::recursion::aggregation_state aggregation_state; std::shared_ptr verification_key; }; - static void create_inner_circuit_no_tables(InnerBuilder& builder, uint256_t public_inputs[]) + static void create_inner_circuit_no_tables(Builder& builder, uint256_t public_inputs[]) { // A nice Pythagorean triples circuit example: "I know a & b s.t. a^2 + b^2 = c^2". inner_scalar_field_ct a(witness_ct(&builder, public_inputs[0])); @@ -61,15 +56,11 @@ template class RecursiveCircuit { c_sq.set_public(); }; - static circuit_outputs create_outer_circuit(InnerBuilder& inner_circuit, OuterBuilder& outer_builder) + static circuit_outputs create_outer_circuit(Builder& inner_circuit, Builder& outer_builder) { ProverOfInnerCircuit prover; InnerComposer inner_composer; - if constexpr (is_ultra_to_ultra) { - prover = inner_composer.create_prover(inner_circuit); - } else { - prover = inner_composer.create_ultra_to_standard_prover(inner_circuit); - } + prover = inner_composer.create_prover(inner_circuit); const auto verification_key_native = inner_composer.compute_verification_key(inner_circuit); // Convert the verification key's elements into _circuit_ types, using the OUTER composer. @@ -82,27 +73,23 @@ template class RecursiveCircuit { // Native check is mainly for comparison vs circuit version of the verifier. VerifierOfInnerProof native_verifier; - if constexpr (is_ultra_to_ultra) { - native_verifier = inner_composer.create_verifier(inner_circuit); - } else { - native_verifier = inner_composer.create_ultra_to_standard_verifier(inner_circuit); - } + native_verifier = inner_composer.create_verifier(inner_circuit); - auto native_result = native_verifier.verify_proof(proof_to_recursively_verify); - if (native_result == false) { + bool native_result = native_verifier.verify_proof(proof_to_recursively_verify); + if (!native_result) { throw_or_abort("Native verification failed"); } } transcript::Manifest recursive_manifest = InnerComposer::create_manifest(prover.key->num_public_inputs); - auto output = recursion::verify_proof( + auto output = recursion::verify_proof( &outer_builder, verification_key, recursive_manifest, proof_to_recursively_verify); return { output, verification_key }; }; - static bool check_pairing_point_accum_public_inputs(OuterBuilder& builder, const bb::pairing::miller_lines* lines) + static bool check_pairing_point_accum_public_inputs(Builder& builder, const bb::pairing::miller_lines* lines) { if (builder.contains_pairing_point_accumulator && builder.pairing_point_accumulator_public_input_indices.size() == 16) { @@ -166,10 +153,10 @@ template class RecursiveCircuit { } public: - static OuterBuilder generate(uint256_t inputs[]) + static Builder generate(uint256_t inputs[]) { - InnerBuilder inner_circuit; - OuterBuilder outer_circuit; + Builder inner_circuit; + Builder outer_circuit; create_inner_circuit_no_tables(inner_circuit, inputs); diff --git a/barretenberg/cpp/src/barretenberg/solidity_helpers/honk_key_gen.cpp b/barretenberg/cpp/src/barretenberg/solidity_helpers/honk_key_gen.cpp index 2eaf9bdc9906..a04e876e9bcb 100644 --- a/barretenberg/cpp/src/barretenberg/solidity_helpers/honk_key_gen.cpp +++ b/barretenberg/cpp/src/barretenberg/solidity_helpers/honk_key_gen.cpp @@ -16,11 +16,10 @@ using namespace bb; using DeciderProvingKey = DeciderProvingKey_; using VerificationKey = UltraKeccakFlavor::VerificationKey; -template