diff --git a/barretenberg/.dockerignore b/barretenberg/.dockerignore new file mode 100644 index 000000000000..b38d06b33567 --- /dev/null +++ b/barretenberg/.dockerignore @@ -0,0 +1,8 @@ +sol/broadcast +sol/cache +sol/out +sol/Dockerfile +sol/lib +cpp/build +cpp/srs_db/ignition +.gitmodules \ No newline at end of file diff --git a/barretenberg/.github/workflows/solidity_verifier_test.yml b/barretenberg/.github/workflows/solidity_verifier_test.yml new file mode 100644 index 000000000000..8203b494ba97 --- /dev/null +++ b/barretenberg/.github/workflows/solidity_verifier_test.yml @@ -0,0 +1,23 @@ +name: solidity-verifier-test + +on: + - workflow_dispatch + - push + +env: + FOUNDRY_PROFILE: ci + +jobs: + check: + strategy: + fail-fast: true + + name: Solidity Verifier Tests + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v3 + with: + submodules: recursive + + - name: Build docker image + run: docker build . -f sol/Dockerfile \ No newline at end of file diff --git a/barretenberg/.gitmodules b/barretenberg/.gitmodules index 29296995e26c..d07f11dee899 100644 --- a/barretenberg/.gitmodules +++ b/barretenberg/.gitmodules @@ -1,3 +1,12 @@ [submodule "foundation"] path = foundation url = git@github.com:AztecProtocol/foundation.git +[submodule "sol/lib/forge-std"] + path = sol/lib/forge-std + url = https://github.com/foundry-rs/forge-std +[submodule "sol/lib/solidity-stringutils"] + path = sol/lib/solidity-stringutils + url = https://github.com/Arachnid/solidity-stringutils +[submodule "sol/lib/openzeppelin-contracts"] + path = sol/lib/openzeppelin-contracts + url = https://github.com/OpenZeppelin/openzeppelin-contracts diff --git a/barretenberg/cpp/dockerfiles/Dockerfile.x86_64-linux-clang b/barretenberg/cpp/dockerfiles/Dockerfile.x86_64-linux-clang index 6e0b707b5250..f05aa87a94c8 100644 --- a/barretenberg/cpp/dockerfiles/Dockerfile.x86_64-linux-clang +++ b/barretenberg/cpp/dockerfiles/Dockerfile.x86_64-linux-clang @@ -19,4 +19,4 @@ RUN cmake --preset default && cmake --build --preset default FROM alpine:3.17 RUN apk update && apk add openmp -COPY --from=builder /usr/src/barretenberg/cpp/srs_db /usr/src/barretenberg/cpp/srs_db +COPY --from=builder /usr/src/barretenberg/cpp/srs_db /usr/src/barretenberg/cpp/srs_db \ No newline at end of file diff --git a/barretenberg/cpp/src/CMakeLists.txt b/barretenberg/cpp/src/CMakeLists.txt index a90789f9c644..350392c1877e 100644 --- a/barretenberg/cpp/src/CMakeLists.txt +++ b/barretenberg/cpp/src/CMakeLists.txt @@ -52,6 +52,7 @@ add_subdirectory(barretenberg/plonk) add_subdirectory(barretenberg/stdlib) add_subdirectory(barretenberg/join_split_example) add_subdirectory(barretenberg/dsl) +add_subdirectory(barretenberg/solidity_helpers) if(BENCHMARKS) add_subdirectory(barretenberg/benchmark) diff --git a/barretenberg/cpp/src/barretenberg/solidity_helpers/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/solidity_helpers/CMakeLists.txt new file mode 100644 index 000000000000..085b5297e5fe --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/solidity_helpers/CMakeLists.txt @@ -0,0 +1,17 @@ +barretenberg_module(stdlib_solidity_helpers plonk proof_system transcript crypto_pedersen_commitment polynomials crypto_sha256 ecc crypto_blake3s stdlib_primitives stdlib_pedersen_commitment stdlib_blake3s stdlib_blake2s) + +add_executable(solidity_key_gen key_gen.cpp) + +add_executable(solidity_proof_gen proof_gen.cpp) + +target_link_libraries( + solidity_key_gen + stdlib_solidity_helpers + env +) + +target_link_libraries( + solidity_proof_gen + stdlib_solidity_helpers + env +) 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 new file mode 100644 index 000000000000..99f443faf4f6 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/solidity_helpers/circuits/add_2_circuit.hpp @@ -0,0 +1,25 @@ +#include "barretenberg/stdlib/primitives/field/field.hpp" +#include "barretenberg/stdlib/primitives/witness/witness.hpp" +#include "barretenberg/stdlib/primitives/uint/uint.hpp" +#include "barretenberg/stdlib/primitives/bool/bool.hpp" + +template class Add2Circuit { + public: + typedef stdlib::field_t field_ct; + typedef stdlib::witness_t witness_ct; + typedef stdlib::public_witness_t public_witness_ct; + + // Three public inputs + static Composer generate(std::string srs_path, uint256_t inputs[]) + { + + Composer composer(srs_path); + + field_ct a(public_witness_ct(&composer, inputs[0])); + field_ct b(public_witness_ct(&composer, inputs[1])); + field_ct c(public_witness_ct(&composer, inputs[2])); + c.assert_equal(a + b); + + return composer; + } +}; \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/solidity_helpers/circuits/blake_circuit.hpp b/barretenberg/cpp/src/barretenberg/solidity_helpers/circuits/blake_circuit.hpp new file mode 100644 index 000000000000..1b19bd8b3b09 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/solidity_helpers/circuits/blake_circuit.hpp @@ -0,0 +1,31 @@ +#include "barretenberg/stdlib/primitives/field/field.hpp" +#include "barretenberg/stdlib/primitives/witness/witness.hpp" +#include "barretenberg/stdlib/hash/blake2s/blake2s.hpp" + +using namespace proof_system::plonk; +using namespace proof_system::plonk::stdlib; + +using numeric::uint256_t; + +template class BlakeCircuit { + public: + typedef stdlib::field_t field_ct; + typedef stdlib::public_witness_t public_witness_ct; + typedef stdlib::byte_array byte_array_ct; + + static constexpr size_t NUM_PUBLIC_INPUTS = 4; + + static Composer generate(std::string srs_path, uint256_t public_inputs[]) + { + Composer composer(srs_path); + + byte_array_ct input_buffer(&composer); + for (size_t i = 0; i < NUM_PUBLIC_INPUTS; ++i) { + input_buffer.write(byte_array_ct(field_ct(public_witness_ct(&composer, public_inputs[i])))); + } + + stdlib::blake2s(input_buffer); + + return composer; + } +}; \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/solidity_helpers/circuits/recursive_circuit.hpp b/barretenberg/cpp/src/barretenberg/solidity_helpers/circuits/recursive_circuit.hpp new file mode 100644 index 000000000000..077427af511f --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/solidity_helpers/circuits/recursive_circuit.hpp @@ -0,0 +1,136 @@ +#include "barretenberg/transcript/transcript.hpp" +#include "barretenberg/plonk/proof_system/proving_key/serialize.hpp" +#include "barretenberg/stdlib/primitives/curves/bn254.hpp" +#include "barretenberg/stdlib/recursion/verifier/verifier.hpp" +#include "barretenberg/stdlib/recursion/verifier/program_settings.hpp" +#include "barretenberg/ecc/curves/bn254/fq12.hpp" +#include "barretenberg/ecc/curves/bn254/pairing.hpp" + +using namespace proof_system::plonk; + +using numeric::uint256_t; + +template class RecursiveCircuit { + using InnerComposer = UltraComposer; + + typedef stdlib::field_t field_ct; + typedef stdlib::bn254 inner_curve; + typedef stdlib::bn254 outer_curve; + typedef stdlib::recursion::verification_key verification_key_pt; + typedef stdlib::recursion::recursive_ultra_verifier_settings recursive_settings; + typedef stdlib::recursion::recursive_ultra_to_standard_verifier_settings + ultra_to_standard_recursive_settings; + typedef inner_curve::fr_ct fr_ct; + typedef inner_curve::public_witness_ct public_witness_ct; + typedef inner_curve::witness_ct witness_ct; + + struct circuit_outputs { + stdlib::recursion::aggregation_state aggregation_state; + std::shared_ptr verification_key; + }; + + static void create_inner_circuit_no_tables(InnerComposer& composer, uint256_t inputs[]) + { + field_ct a(public_witness_ct(&composer, inputs[0])); + field_ct b(public_witness_ct(&composer, inputs[1])); + field_ct c(public_witness_ct(&composer, inputs[2])); + + // @note For some reason, if we don't have this line, the circuit fails to verify. + auto c_sq = c * c; + + c.assert_equal(a + b); + c_sq.assert_equal(c * c); + }; + + static circuit_outputs create_outer_circuit(InnerComposer& inner_composer, OuterComposer& outer_composer) + { + // These constexpr definitions are to allow for the following: + // An Ultra Pedersen hash evaluates to a different value from the Turbo/Standard versions of the Pedersen hash. + // Therefore, the fiat-shamir challenges generated by the prover and verifier _could_ accidentally be different + // if an ultra proof is generated using ultra-pedersen challenges, but is being verified within a non-ultra + // circuit which uses non-ultra-pedersen challenges. We need the prover and verifier hashes to be the same. The + // solution is to select the relevant prover and verifier types (whose settings use the same hash for + // fiat-shamir), depending on the Inner-Outer combo. It's a bit clunky, but the alternative is to have a + // template argument for the hashtype, and that would pervade the entire UltraComposer, which would be + // horrendous. + constexpr bool is_ultra_to_ultra = std::is_same::value; + typedef + typename std::conditional::type ProverOfInnerCircuit; + typedef typename std::conditional::type + VerifierOfInnerProof; + typedef + typename std::conditional::type + RecursiveSettings; + + ProverOfInnerCircuit prover; + if constexpr (is_ultra_to_ultra) { + prover = inner_composer.create_prover(); + } else { + prover = inner_composer.create_ultra_to_standard_prover(); + } + + const auto verification_key_native = inner_composer.compute_verification_key(); + + // Convert the verification key's elements into _circuit_ types, using the OUTER composer. + std::shared_ptr verification_key = + verification_key_pt::from_witness(&outer_composer, verification_key_native); + + proof recursive_proof = prover.construct_proof(); + + { + // 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(); + } else { + native_verifier = inner_composer.create_ultra_to_standard_verifier(); + } + auto native_result = native_verifier.verify_proof(recursive_proof); + if (native_result == false) { + throw std::runtime_error("Native verification failed"); + } + } + + transcript::Manifest recursive_manifest = InnerComposer::create_manifest(prover.key->num_public_inputs); + + // Verifying the ultra (inner) proof with CIRCUIT TYPES (i.e. within a standard or ultra plonk arithmetic + // circuit) + stdlib::recursion::aggregation_state output = + stdlib::recursion::verify_proof( + &outer_composer, verification_key, recursive_manifest, recursive_proof); + + return { output, verification_key }; + }; + + public: + static OuterComposer generate(std::string srs_path, uint256_t inputs[]) + { + InnerComposer inner_composer = InnerComposer(srs_path); + OuterComposer outer_composer = OuterComposer(srs_path); + + create_inner_circuit_no_tables(inner_composer, inputs); + auto circuit_output = create_outer_circuit(inner_composer, outer_composer); + + g1::affine_element P[2]; + P[0].x = barretenberg::fq(circuit_output.aggregation_state.P0.x.get_value().lo); + P[0].y = barretenberg::fq(circuit_output.aggregation_state.P0.y.get_value().lo); + P[1].x = barretenberg::fq(circuit_output.aggregation_state.P1.x.get_value().lo); + P[1].y = barretenberg::fq(circuit_output.aggregation_state.P1.y.get_value().lo); + + barretenberg::fq12 inner_proof_result = barretenberg::pairing::reduced_ate_pairing_batch_precomputed( + P, circuit_output.verification_key->reference_string->get_precomputed_g2_lines(), 2); + + if (inner_proof_result != barretenberg::fq12::one()) { + throw std::runtime_error("inner proof result != 1"); + } + + circuit_output.aggregation_state.add_proof_outputs_as_public_inputs(); + + if (outer_composer.failed()) { + throw std::runtime_error("outer composer failed"); + } + + return outer_composer; + } +}; \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/solidity_helpers/key_gen.cpp b/barretenberg/cpp/src/barretenberg/solidity_helpers/key_gen.cpp new file mode 100644 index 000000000000..86913a0d9228 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/solidity_helpers/key_gen.cpp @@ -0,0 +1,87 @@ +#include + +#include "barretenberg/plonk/composer/standard_composer.hpp" +#include "barretenberg/plonk/composer/ultra_composer.hpp" +#include "barretenberg/plonk/proof_system/verification_key/sol_gen.hpp" + +#include "circuits/blake_circuit.hpp" +#include "circuits/add_2_circuit.hpp" +#include "circuits/recursive_circuit.hpp" + +#include "utils/utils.hpp" +#include "utils/instance_sol_gen.hpp" + +template +void generate_keys(std::string output_path, std::string srs_path, std::string flavour_prefix, std::string circuit_name) +{ + uint256_t public_inputs[4] = { 0, 0, 0, 0 }; + Composer composer = Circuit::generate(srs_path, public_inputs); + + std::shared_ptr vkey = composer.compute_verification_key(); + + // Make verification key file upper case + circuit_name.at(0) = static_cast(std::toupper(static_cast(circuit_name.at(0)))); + flavour_prefix.at(0) = static_cast(std::toupper(static_cast(flavour_prefix.at(0)))); + + std::string vk_class_name = circuit_name + flavour_prefix + "VerificationKey"; + std::string base_class_name = "Base" + flavour_prefix + "Verifier"; + std::string instance_class_name = circuit_name + flavour_prefix + "Verifier"; + + { + auto vk_filename = output_path + "/keys/" + vk_class_name + ".sol"; + std::ofstream os(vk_filename); + proof_system::output_vk_sol(os, vkey, vk_class_name); + info("VK contract written to: ", vk_filename); + } + + { + auto instance_filename = output_path + "/instance/" + instance_class_name + ".sol"; + std::ofstream os(instance_filename); + output_instance(os, vk_class_name, base_class_name, instance_class_name); + info("Verifier instance written to: ", instance_filename); + } +} + +/* + * @brief Main entry point for the verification key generator + * + * 1. project_root_path: path to the solidity project root + * 2. srs_path: path to the srs db + */ +int main(int argc, char** argv) +{ + std::vector args(argv, argv + argc); + + if (args.size() < 5) { + info("usage: ", args[0], "[plonk flavour] [circuit flavour] [output path] [srs path]"); + return 1; + } + + const std::string plonk_flavour = args[1]; + const std::string circuit_flavour = args[2]; + const std::string output_path = args[3]; + const std::string srs_path = args[4]; + + // @todo - Add support for unrolled standard verifier. Needs a new solidity verifier contract. + + if (plonk_flavour != "ultra") { + info("Only ultra plonk flavour is supported at the moment"); + return 1; + } else { + info("Generating ultra plonk keys for ", circuit_flavour, " circuit"); + + if (circuit_flavour == "blake") { + generate_keys>( + output_path, srs_path, plonk_flavour, circuit_flavour); + } else if (circuit_flavour == "add2") { + generate_keys>( + output_path, srs_path, plonk_flavour, circuit_flavour); + } else if (circuit_flavour == "recursive") { + generate_keys>( + output_path, srs_path, plonk_flavour, circuit_flavour); + } else { + info("Only blake, add2 and recursive circuits are supported at the moment"); + return 1; + } + } +} \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/solidity_helpers/proof_gen.cpp b/barretenberg/cpp/src/barretenberg/solidity_helpers/proof_gen.cpp new file mode 100644 index 000000000000..1448597c321e --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/solidity_helpers/proof_gen.cpp @@ -0,0 +1,93 @@ +#include +#include +#include + +#include "barretenberg/plonk/composer/standard_composer.hpp" +#include "barretenberg/plonk/composer/ultra_composer.hpp" + +#include "circuits/blake_circuit.hpp" +#include "circuits/add_2_circuit.hpp" +#include "circuits/recursive_circuit.hpp" +#include "utils/utils.hpp" + +using namespace numeric; +using numeric::uint256_t; + +template void generate_proof(std::string srs_path, uint256_t inputs[]) +{ + Composer composer = Circuit::generate(srs_path, inputs); + + // @todo this only works for ultra! Why is ultra part of function name on ultra composer? + auto prover = composer.create_ultra_with_keccak_prover(); + auto proof = prover.construct_proof(); + { + auto verifier = composer.create_ultra_with_keccak_verifier(); + + if (!verifier.verify_proof(proof)) { + throw std::runtime_error("Verification failed"); + } + + std::string proof_bytes = bytes_to_hex_string(proof.proof_data); + std::cout << proof_bytes; + } +} + +std::string pad_left(std::string input, size_t length) +{ + return std::string(length - std::min(length, input.length()), '0') + input; +} + +/** + * @brief Main entry point for the proof generator. + * Expected inputs: + * 1. plonk_flavour: ultra + * 2. circuit_flavour: blake, add2, recursive + * 3. public_inputs: comma separated list of public inputs + * 4. project_root_path: path to the solidity project root + * 5. srs_path: path to the srs db + */ +int main(int argc, char** argv) +{ + std::vector args(argv, argv + argc); + + if (args.size() < 5) { + info("usage: ", args[0], "[plonk flavour] [circuit flavour] [srs path] [public inputs]"); + return 1; + } + + const std::string plonk_flavour = args[1]; + const std::string circuit_flavour = args[2]; + const std::string srs_path = args[3]; + const std::string string_input = args[4]; + + // @todo dynamically allocate this + uint256_t inputs[] = { 0, 0, 0, 0, 0 }; + + size_t count = 0; + std::stringstream s_stream(string_input); + while (s_stream.good()) { + std::string sub; + getline(s_stream, sub, ','); + if (sub.substr(0, 2) == "0x") { + sub = sub.substr(2); + } + std::string padded = pad_left(sub, 64); + inputs[count++] = uint256_t(padded); + } + + if (plonk_flavour != "ultra") { + info("Only ultra plonk flavour is supported at the moment"); + return 1; + } else { + if (circuit_flavour == "blake") { + generate_proof>(srs_path, inputs); + } else if (circuit_flavour == "add2") { + generate_proof>(srs_path, inputs); + } else if (circuit_flavour == "recursive") { + generate_proof>(srs_path, inputs); + } else { + info("Invalid circuit flavour: " + circuit_flavour); + return 1; + } + } +} \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/solidity_helpers/utils/instance_sol_gen.hpp b/barretenberg/cpp/src/barretenberg/solidity_helpers/utils/instance_sol_gen.hpp new file mode 100644 index 000000000000..0bbcdb126ed0 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/solidity_helpers/utils/instance_sol_gen.hpp @@ -0,0 +1,30 @@ +/** + * Write a solidity file containing the instance importing vk params to the given stream. + **/ +inline void output_instance(std::ostream& os, + std::string const& vk_class_name, + std::string const& base_class_name, + std::string const& class_name) +{ + + std::string vk_filename = "../keys/" + vk_class_name + ".sol"; + std::string base_filename = "../" + base_class_name + ".sol"; + + // clang-format off + os << + "// SPDX-License-Identifier: Apache-2.0\n" + "// Copyright 2023 Aztec\n" + "pragma solidity >=0.8.4;\n\n" + "import {" << vk_class_name << " as VK} from \"" << vk_filename << "\";\n" + "import {" << base_class_name << " as BASE} from \"" << base_filename << "\";\n\n" + "contract " << class_name << " is BASE {\n" + " function getVerificationKeyHash() public pure override(BASE) returns (bytes32) {\n" + " return VK.verificationKeyHash();\n" + " }\n\n" + " function loadVerificationKey(uint256 vk, uint256 _omegaInverseLoc) internal pure virtual override(BASE) {\n" + " VK.loadVerificationKey(vk, _omegaInverseLoc);\n" + " }\n" + "}\n"; + + os << std::flush; +} \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/solidity_helpers/utils/utils.hpp b/barretenberg/cpp/src/barretenberg/solidity_helpers/utils/utils.hpp new file mode 100644 index 000000000000..0567e7373939 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/solidity_helpers/utils/utils.hpp @@ -0,0 +1,19 @@ +#include +#include + +std::string bytes_to_hex_string(const std::vector& input) +{ + static const char characters[] = "0123456789ABCDEF"; + + // Zeroes out the buffer unnecessarily, can't be avoided for std::string. + std::string ret(input.size() * 2, 0); + + // Hack... Against the rules but avoids copying the whole buffer. + auto buf = const_cast(ret.data()); + + for (const auto& oneInputByte : input) { + *buf++ = characters[oneInputByte >> 4]; + *buf++ = characters[oneInputByte & 0x0F]; + } + return ret; +} diff --git a/barretenberg/sol/.gitignore b/barretenberg/sol/.gitignore new file mode 100644 index 000000000000..434dd3d2ff48 --- /dev/null +++ b/barretenberg/sol/.gitignore @@ -0,0 +1,15 @@ +# Compiler files +cache/ +out/ + +# Ignores development broadcast logs +!/broadcast +/broadcast/*/31337/ +/broadcast/**/dry-run/ + +# Dotenv file +.env +.vscode/settings.json +.foundry + +build/ \ No newline at end of file diff --git a/barretenberg/sol/Dockerfile b/barretenberg/sol/Dockerfile new file mode 100644 index 000000000000..8284044d0efe --- /dev/null +++ b/barretenberg/sol/Dockerfile @@ -0,0 +1,41 @@ +FROM alpine:3.17 +RUN apk update \ + && apk upgrade \ + && apk add --no-cache \ + build-base \ + clang15 \ + openmp-dev \ + cmake \ + ninja \ + git \ + curl \ + perl + +WORKDIR /usr/src/barretenberg/cpp + +COPY ./cpp . +# Build everything to ensure everything builds. All tests will be run from the result of this build. +RUN cmake --preset default && cmake --build --preset default --target solidity_key_gen solidity_proof_gen + +FROM docker.io/frolvlad/alpine-glibc:alpine-3.16_glibc-2.34 as builder +RUN apk update && apk add git curl build-base openmp-dev bash + +COPY --from=0 /usr/src/barretenberg/cpp/build/bin /usr/src/barretenberg/cpp/build/bin +COPY --from=0 /usr/src/barretenberg/cpp/srs_db /usr/src/barretenberg/cpp/srs_db +WORKDIR /usr/src/barretenberg/sol +RUN git init +COPY ./sol . + +# Copy forge binary directly from foundry +COPY --from=ghcr.io/foundry-rs/foundry:latest /usr/local/bin/forge /usr/local/bin/forge + +RUN forge install --no-commit \ + https://github.com/foundry-rs/forge-std \ + https://github.com/openzeppelin/openzeppelin-contracts \ + https://github.com/Arachnid/solidity-stringutils + +RUN cd ../cpp/srs_db && ./download_ignition.sh 3 && cd ../../sol + +RUN ./scripts/init.sh + +RUN forge test --no-match-contract TestBase \ No newline at end of file diff --git a/barretenberg/sol/README.md b/barretenberg/sol/README.md new file mode 100644 index 000000000000..d0ae9ffa1b50 --- /dev/null +++ b/barretenberg/sol/README.md @@ -0,0 +1,80 @@ +# Solidity Verifier contracts and tests + +**Note:** ONLY Ultra verifier as rolled versions were removed. [aztec-verifier-contracts](https://github.com/AztecProtocol/aztec-verifier-contracts) contains old code with rolled standard verifier and tests. + + +This folder contains verifier contracts and testing harnesses that are used by [Noir, our Zero-Knowledge Programming Language](https://github.com/noir-lang/noir). + +The implementations maintain the same interface, regardless of the verifier flavour (Standard, Turbo, Ultra), this should enable upstream implementations to be "plug-and-play". + +The verifier will follow an overall architecture below, consisting of 3 contracts/libraries. Namely, the verifier algorithm (stable across circuits), the verification key (circuit dependent) and then the "verifier instance", a base that reads from the verification key and uses the key's values in the verification algorithm. The main advantage of this design is that we can generate verification key's per circuit and plug them into a general verification algorithm. + +![Verifier architecture](./figures/verifier.png) + +The verification key is currently generated via [Barretenberg](https://github.com/AztecProtocol/barretenberg/blob/master/cpp/src/aztec/proof_system/verification_key/sol_gen.hpp), Aztec's backend for generating proofs. + +## Current implementations + +- UltraPlonK (see `src/ultra`) + +## Generating Verification Keys and Proofs + +Run `bootstrap.sh` to clone git submodules, download SRS and generate verification keys, relies on barretenberg already being compiled (run `./bootstrap` in `cpp`). The bootstrap will also install foundry to `./.foundry` so you can use `./.foundry/bin/forge` if you don't already have foundry installed. + +# Tests + +Test are performed with a `TestBase` harness, it provides helpers for reading files and printing proofs. The tests also require proofs and verification keys, those are build as part of the `bootstrap.sh`. + +Note that foundry is set to just run 1 fuzz run, this is mainly to limit the duty for CI, and then it can be hammered more heavily when needed. + +## How To Run the Tests? + +To run all tests, run the following scripts at the root of the repo: + +```bash +forge test --no-match-contract TestBase # add -(v, vv, vvv, vvvv) for verbosity of logs, no logs emitted as default +``` + +To run test for a specific Contract test, + +```bash +forge test --match-contract # e.g., StandardTest +``` + +To run a specific test + +```bash +forge test --match-test # e.g., testValidProof +``` + +Example to run only `testValidProof` for the Standard verifier with logs: + +```bash +forge test --match-contract StandardTest --match-test testValidProof -vvvv +``` + +## Debugging in assembly + +Debugging from inside the assembly can be pretty inconvenient. The quickest way to get going is to add a custom error: +```solidity +bytes4 internal constant ERR_S = 0xf7b44074; +error ERR(bytes32,bytes32,bytes32); +``` +Where `ERR_S` is the selector (first 4 bytes of keccak256(function signature)). + +To revert the contract, and print values, you can then do as +```solidity +mstore(0x00, ERR_S) // put the selector in memory +mstore(0x04, val_1) // add first value after selector +mstore(0x24, val_2) // add second value after first +mstore(0x44, val_3) // add third value after second +revert(0x00, 0x64) // revert with a message containing 0x64 bytes defined above +``` +When running a test, you will then see the three values `val_1, val_2, val_3` in the console. + +# Docker +To run the docker image, which will build keygen and proofgen and then run tests, run the following from root: +```bash +# From root +docker build . -f sol/Dockerfile +``` diff --git a/barretenberg/sol/bootstrap.sh b/barretenberg/sol/bootstrap.sh new file mode 100755 index 000000000000..fa9a85bbf06f --- /dev/null +++ b/barretenberg/sol/bootstrap.sh @@ -0,0 +1,32 @@ + +echo "Installing foundry..." +rm -rf broadcast cache out +. ./scripts/install_foundry.sh +forge install --no-commit +# Ensure libraries are at the correct version +git submodule update --init --recursive ./lib + +echo "Installing barretenberg..." +git submodule init +git submodule update + +echo "Downloading srs..." +cd ../cpp/srs_db +./download_ignition.sh 3 +#./download_ignition_lagrange.sh 12 +cd ../../sol + +echo "Building c++ binaries..." +cd ../cpp +cmake --preset clang15 +cmake --build --preset clang15 --target solidity_key_gen solidity_proof_gen +cd ../sol + +echo "Generating keys..." +./scripts/init.sh + +echo "Formatting code..." +forge fmt +forge build + +echo "Targets built, you are good to go!" \ No newline at end of file diff --git a/barretenberg/sol/figures/verifier.png b/barretenberg/sol/figures/verifier.png new file mode 100644 index 000000000000..b099f9c8af75 Binary files /dev/null and b/barretenberg/sol/figures/verifier.png differ diff --git a/barretenberg/sol/foundry.toml b/barretenberg/sol/foundry.toml new file mode 100644 index 000000000000..15f71ad424b7 --- /dev/null +++ b/barretenberg/sol/foundry.toml @@ -0,0 +1,12 @@ +[profile.default] +src = 'src' +out = 'out' +libs = ['lib'] +ffi = true + +[fuzz] +runs = 1 + +solc = "0.8.18" + +# See more config options https://github.com/foundry-rs/foundry/tree/master/config \ No newline at end of file diff --git a/barretenberg/sol/lib/forge-std b/barretenberg/sol/lib/forge-std new file mode 160000 index 000000000000..f06ad8944e43 --- /dev/null +++ b/barretenberg/sol/lib/forge-std @@ -0,0 +1 @@ +Subproject commit f06ad8944e436e639bce09cfccc55466d99e79d8 diff --git a/barretenberg/sol/lib/openzeppelin-contracts b/barretenberg/sol/lib/openzeppelin-contracts new file mode 160000 index 000000000000..8d633cb7d169 --- /dev/null +++ b/barretenberg/sol/lib/openzeppelin-contracts @@ -0,0 +1 @@ +Subproject commit 8d633cb7d169f2f8595b273660b00b69e845c2fe diff --git a/barretenberg/sol/lib/solidity-stringutils b/barretenberg/sol/lib/solidity-stringutils new file mode 160000 index 000000000000..46983c6d9462 --- /dev/null +++ b/barretenberg/sol/lib/solidity-stringutils @@ -0,0 +1 @@ +Subproject commit 46983c6d9462a80229cf0d5bab8ea3b3ee31066c diff --git a/barretenberg/sol/remappings.txt b/barretenberg/sol/remappings.txt new file mode 100644 index 000000000000..da51dc14af88 --- /dev/null +++ b/barretenberg/sol/remappings.txt @@ -0,0 +1,2 @@ +stringutils/=lib/solidity-stringutils/ +@openzeppelin/=lib/openzeppelin-contracts/ \ No newline at end of file diff --git a/barretenberg/sol/scripts/init.sh b/barretenberg/sol/scripts/init.sh new file mode 100755 index 000000000000..fa00f288bd2f --- /dev/null +++ b/barretenberg/sol/scripts/init.sh @@ -0,0 +1,10 @@ +#!/bin/bash + + +PLONK_FLAVOUR="ultra" +SRS_PATH="../cpp/srs_db/ignition" +OUTPUT_PATH="./src/ultra" + +../cpp/build/bin/solidity_key_gen $PLONK_FLAVOUR add2 $OUTPUT_PATH $SRS_PATH +../cpp/build/bin/solidity_key_gen $PLONK_FLAVOUR blake $OUTPUT_PATH $SRS_PATH +../cpp/build/bin/solidity_key_gen $PLONK_FLAVOUR recursive $OUTPUT_PATH $SRS_PATH \ No newline at end of file diff --git a/barretenberg/sol/scripts/install_foundry.sh b/barretenberg/sol/scripts/install_foundry.sh new file mode 100755 index 000000000000..3842f6a4318b --- /dev/null +++ b/barretenberg/sol/scripts/install_foundry.sh @@ -0,0 +1,21 @@ +#!/bin/sh +set -eu + +export FOUNDRY_DIR="$PWD/.foundry" +FOUNDRY_BIN_DIR="$FOUNDRY_DIR/bin" +BIN_URL="https://raw.githubusercontent.com/foundry-rs/foundry/master/foundryup/foundryup" +BIN_PATH="$FOUNDRY_BIN_DIR/foundryup" +FOUNDRY_MAN_DIR="$FOUNDRY_DIR/share/man/man1" + +# Clean +rm -rf $FOUNDRY_DIR + +# Install foundryup. +mkdir -p $FOUNDRY_BIN_DIR +mkdir -p $FOUNDRY_MAN_DIR +curl -# -L $BIN_URL -o $BIN_PATH +chmod +x $BIN_PATH +export PATH=$FOUNDRY_BIN_DIR:$PATH + +# Use version. +foundryup \ No newline at end of file diff --git a/barretenberg/sol/scripts/run_fuzzer.sh b/barretenberg/sol/scripts/run_fuzzer.sh new file mode 100755 index 000000000000..e6e235a85e51 --- /dev/null +++ b/barretenberg/sol/scripts/run_fuzzer.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +PLONK_FLAVOUR=${1:-"ultra"} +CIRCUIT_FLAVOUR=${2:-"blake"} +INPUTS=${3:-"1,2,3,4"} + +INPUTS="$( sed 's/\\n//g' <<<"$INPUTS" )" + +SRS_PATH="../cpp/srs_db/ignition" + +# @note This needs to be updated to point to the generator +../cpp/build/bin/solidity_proof_gen $PLONK_FLAVOUR $CIRCUIT_FLAVOUR $SRS_PATH $INPUTS \ No newline at end of file diff --git a/barretenberg/sol/src/interfaces/IVerifier.sol b/barretenberg/sol/src/interfaces/IVerifier.sol new file mode 100644 index 000000000000..f1b841b7d78a --- /dev/null +++ b/barretenberg/sol/src/interfaces/IVerifier.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2022 Aztec +pragma solidity >=0.8.4; + +interface IVerifier { + function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool); +} diff --git a/barretenberg/sol/src/ultra/BaseUltraVerifier.sol b/barretenberg/sol/src/ultra/BaseUltraVerifier.sol new file mode 100644 index 000000000000..1d2842c35e01 --- /dev/null +++ b/barretenberg/sol/src/ultra/BaseUltraVerifier.sol @@ -0,0 +1,2532 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2022 Aztec +pragma solidity >=0.8.4; + +/** + * @title Ultra Plonk proof verification contract + * @dev Top level Plonk proof verification contract, which allows Plonk proof to be verified + */ +abstract contract BaseUltraVerifier { + // VERIFICATION KEY MEMORY LOCATIONS + uint256 internal constant N_LOC = 0x380; + uint256 internal constant NUM_INPUTS_LOC = 0x3a0; + uint256 internal constant OMEGA_LOC = 0x3c0; + uint256 internal constant DOMAIN_INVERSE_LOC = 0x3e0; + uint256 internal constant Q1_X_LOC = 0x400; + uint256 internal constant Q1_Y_LOC = 0x420; + uint256 internal constant Q2_X_LOC = 0x440; + uint256 internal constant Q2_Y_LOC = 0x460; + uint256 internal constant Q3_X_LOC = 0x480; + uint256 internal constant Q3_Y_LOC = 0x4a0; + uint256 internal constant Q4_X_LOC = 0x4c0; + uint256 internal constant Q4_Y_LOC = 0x4e0; + uint256 internal constant QM_X_LOC = 0x500; + uint256 internal constant QM_Y_LOC = 0x520; + uint256 internal constant QC_X_LOC = 0x540; + uint256 internal constant QC_Y_LOC = 0x560; + uint256 internal constant QARITH_X_LOC = 0x580; + uint256 internal constant QARITH_Y_LOC = 0x5a0; + uint256 internal constant QSORT_X_LOC = 0x5c0; + uint256 internal constant QSORT_Y_LOC = 0x5e0; + uint256 internal constant QELLIPTIC_X_LOC = 0x600; + uint256 internal constant QELLIPTIC_Y_LOC = 0x620; + uint256 internal constant QAUX_X_LOC = 0x640; + uint256 internal constant QAUX_Y_LOC = 0x660; + uint256 internal constant SIGMA1_X_LOC = 0x680; + uint256 internal constant SIGMA1_Y_LOC = 0x6a0; + uint256 internal constant SIGMA2_X_LOC = 0x6c0; + uint256 internal constant SIGMA2_Y_LOC = 0x6e0; + uint256 internal constant SIGMA3_X_LOC = 0x700; + uint256 internal constant SIGMA3_Y_LOC = 0x720; + uint256 internal constant SIGMA4_X_LOC = 0x740; + uint256 internal constant SIGMA4_Y_LOC = 0x760; + uint256 internal constant TABLE1_X_LOC = 0x780; + uint256 internal constant TABLE1_Y_LOC = 0x7a0; + uint256 internal constant TABLE2_X_LOC = 0x7c0; + uint256 internal constant TABLE2_Y_LOC = 0x7e0; + uint256 internal constant TABLE3_X_LOC = 0x800; + uint256 internal constant TABLE3_Y_LOC = 0x820; + uint256 internal constant TABLE4_X_LOC = 0x840; + uint256 internal constant TABLE4_Y_LOC = 0x860; + uint256 internal constant TABLE_TYPE_X_LOC = 0x880; + uint256 internal constant TABLE_TYPE_Y_LOC = 0x8a0; + uint256 internal constant ID1_X_LOC = 0x8c0; + uint256 internal constant ID1_Y_LOC = 0x8e0; + uint256 internal constant ID2_X_LOC = 0x900; + uint256 internal constant ID2_Y_LOC = 0x920; + uint256 internal constant ID3_X_LOC = 0x940; + uint256 internal constant ID3_Y_LOC = 0x960; + uint256 internal constant ID4_X_LOC = 0x980; + uint256 internal constant ID4_Y_LOC = 0x9a0; + uint256 internal constant CONTAINS_RECURSIVE_PROOF_LOC = 0x9c0; + uint256 internal constant RECURSIVE_PROOF_PUBLIC_INPUT_INDICES_LOC = 0x9e0; + uint256 internal constant G2X_X0_LOC = 0xa00; + uint256 internal constant G2X_X1_LOC = 0xa20; + uint256 internal constant G2X_Y0_LOC = 0xa40; + uint256 internal constant G2X_Y1_LOC = 0xa60; + + // ### PROOF DATA MEMORY LOCATIONS + uint256 internal constant W1_X_LOC = 0x1200; + uint256 internal constant W1_Y_LOC = 0x1220; + uint256 internal constant W2_X_LOC = 0x1240; + uint256 internal constant W2_Y_LOC = 0x1260; + uint256 internal constant W3_X_LOC = 0x1280; + uint256 internal constant W3_Y_LOC = 0x12a0; + uint256 internal constant W4_X_LOC = 0x12c0; + uint256 internal constant W4_Y_LOC = 0x12e0; + uint256 internal constant S_X_LOC = 0x1300; + uint256 internal constant S_Y_LOC = 0x1320; + uint256 internal constant Z_X_LOC = 0x1340; + uint256 internal constant Z_Y_LOC = 0x1360; + uint256 internal constant Z_LOOKUP_X_LOC = 0x1380; + uint256 internal constant Z_LOOKUP_Y_LOC = 0x13a0; + uint256 internal constant T1_X_LOC = 0x13c0; + uint256 internal constant T1_Y_LOC = 0x13e0; + uint256 internal constant T2_X_LOC = 0x1400; + uint256 internal constant T2_Y_LOC = 0x1420; + uint256 internal constant T3_X_LOC = 0x1440; + uint256 internal constant T3_Y_LOC = 0x1460; + uint256 internal constant T4_X_LOC = 0x1480; + uint256 internal constant T4_Y_LOC = 0x14a0; + + uint256 internal constant W1_EVAL_LOC = 0x1600; + uint256 internal constant W2_EVAL_LOC = 0x1620; + uint256 internal constant W3_EVAL_LOC = 0x1640; + uint256 internal constant W4_EVAL_LOC = 0x1660; + uint256 internal constant S_EVAL_LOC = 0x1680; + uint256 internal constant Z_EVAL_LOC = 0x16a0; + uint256 internal constant Z_LOOKUP_EVAL_LOC = 0x16c0; + uint256 internal constant Q1_EVAL_LOC = 0x16e0; + uint256 internal constant Q2_EVAL_LOC = 0x1700; + uint256 internal constant Q3_EVAL_LOC = 0x1720; + uint256 internal constant Q4_EVAL_LOC = 0x1740; + uint256 internal constant QM_EVAL_LOC = 0x1760; + uint256 internal constant QC_EVAL_LOC = 0x1780; + uint256 internal constant QARITH_EVAL_LOC = 0x17a0; + uint256 internal constant QSORT_EVAL_LOC = 0x17c0; + uint256 internal constant QELLIPTIC_EVAL_LOC = 0x17e0; + uint256 internal constant QAUX_EVAL_LOC = 0x1800; + uint256 internal constant TABLE1_EVAL_LOC = 0x1840; + uint256 internal constant TABLE2_EVAL_LOC = 0x1860; + uint256 internal constant TABLE3_EVAL_LOC = 0x1880; + uint256 internal constant TABLE4_EVAL_LOC = 0x18a0; + uint256 internal constant TABLE_TYPE_EVAL_LOC = 0x18c0; + uint256 internal constant ID1_EVAL_LOC = 0x18e0; + uint256 internal constant ID2_EVAL_LOC = 0x1900; + uint256 internal constant ID3_EVAL_LOC = 0x1920; + uint256 internal constant ID4_EVAL_LOC = 0x1940; + uint256 internal constant SIGMA1_EVAL_LOC = 0x1960; + uint256 internal constant SIGMA2_EVAL_LOC = 0x1980; + uint256 internal constant SIGMA3_EVAL_LOC = 0x19a0; + uint256 internal constant SIGMA4_EVAL_LOC = 0x19c0; + uint256 internal constant W1_OMEGA_EVAL_LOC = 0x19e0; + uint256 internal constant W2_OMEGA_EVAL_LOC = 0x2000; + uint256 internal constant W3_OMEGA_EVAL_LOC = 0x2020; + uint256 internal constant W4_OMEGA_EVAL_LOC = 0x2040; + uint256 internal constant S_OMEGA_EVAL_LOC = 0x2060; + uint256 internal constant Z_OMEGA_EVAL_LOC = 0x2080; + uint256 internal constant Z_LOOKUP_OMEGA_EVAL_LOC = 0x20a0; + uint256 internal constant TABLE1_OMEGA_EVAL_LOC = 0x20c0; + uint256 internal constant TABLE2_OMEGA_EVAL_LOC = 0x20e0; + uint256 internal constant TABLE3_OMEGA_EVAL_LOC = 0x2100; + uint256 internal constant TABLE4_OMEGA_EVAL_LOC = 0x2120; + + uint256 internal constant PI_Z_X_LOC = 0x2300; + uint256 internal constant PI_Z_Y_LOC = 0x2320; + uint256 internal constant PI_Z_OMEGA_X_LOC = 0x2340; + uint256 internal constant PI_Z_OMEGA_Y_LOC = 0x2360; + + // Used for elliptic widget. These are alias names for wire + shifted wire evaluations + uint256 internal constant X1_EVAL_LOC = W2_EVAL_LOC; + uint256 internal constant X2_EVAL_LOC = W1_OMEGA_EVAL_LOC; + uint256 internal constant X3_EVAL_LOC = W2_OMEGA_EVAL_LOC; + uint256 internal constant Y1_EVAL_LOC = W3_EVAL_LOC; + uint256 internal constant Y2_EVAL_LOC = W4_OMEGA_EVAL_LOC; + uint256 internal constant Y3_EVAL_LOC = W3_OMEGA_EVAL_LOC; + uint256 internal constant QBETA_LOC = Q3_EVAL_LOC; + uint256 internal constant QBETA_SQR_LOC = Q4_EVAL_LOC; + uint256 internal constant QSIGN_LOC = Q1_EVAL_LOC; + + // ### CHALLENGES MEMORY OFFSETS + + uint256 internal constant C_BETA_LOC = 0x2600; + uint256 internal constant C_GAMMA_LOC = 0x2620; + uint256 internal constant C_ALPHA_LOC = 0x2640; + uint256 internal constant C_ETA_LOC = 0x2660; + uint256 internal constant C_ETA_SQR_LOC = 0x2680; + uint256 internal constant C_ETA_CUBE_LOC = 0x26a0; + + uint256 internal constant C_ZETA_LOC = 0x26c0; + uint256 internal constant C_CURRENT_LOC = 0x26e0; + uint256 internal constant C_V0_LOC = 0x2700; + uint256 internal constant C_V1_LOC = 0x2720; + uint256 internal constant C_V2_LOC = 0x2740; + uint256 internal constant C_V3_LOC = 0x2760; + uint256 internal constant C_V4_LOC = 0x2780; + uint256 internal constant C_V5_LOC = 0x27a0; + uint256 internal constant C_V6_LOC = 0x27c0; + uint256 internal constant C_V7_LOC = 0x27e0; + uint256 internal constant C_V8_LOC = 0x2800; + uint256 internal constant C_V9_LOC = 0x2820; + uint256 internal constant C_V10_LOC = 0x2840; + uint256 internal constant C_V11_LOC = 0x2860; + uint256 internal constant C_V12_LOC = 0x2880; + uint256 internal constant C_V13_LOC = 0x28a0; + uint256 internal constant C_V14_LOC = 0x28c0; + uint256 internal constant C_V15_LOC = 0x28e0; + uint256 internal constant C_V16_LOC = 0x2900; + uint256 internal constant C_V17_LOC = 0x2920; + uint256 internal constant C_V18_LOC = 0x2940; + uint256 internal constant C_V19_LOC = 0x2960; + uint256 internal constant C_V20_LOC = 0x2980; + uint256 internal constant C_V21_LOC = 0x29a0; + uint256 internal constant C_V22_LOC = 0x29c0; + uint256 internal constant C_V23_LOC = 0x29e0; + uint256 internal constant C_V24_LOC = 0x2a00; + uint256 internal constant C_V25_LOC = 0x2a20; + uint256 internal constant C_V26_LOC = 0x2a40; + uint256 internal constant C_V27_LOC = 0x2a60; + uint256 internal constant C_V28_LOC = 0x2a80; + uint256 internal constant C_V29_LOC = 0x2aa0; + uint256 internal constant C_V30_LOC = 0x2ac0; + + uint256 internal constant C_U_LOC = 0x2b00; + + // ### LOCAL VARIABLES MEMORY OFFSETS + uint256 internal constant DELTA_NUMERATOR_LOC = 0x3000; + uint256 internal constant DELTA_DENOMINATOR_LOC = 0x3020; + uint256 internal constant ZETA_POW_N_LOC = 0x3040; + uint256 internal constant PUBLIC_INPUT_DELTA_LOC = 0x3060; + uint256 internal constant ZERO_POLY_LOC = 0x3080; + uint256 internal constant L_START_LOC = 0x30a0; + uint256 internal constant L_END_LOC = 0x30c0; + uint256 internal constant R_ZERO_EVAL_LOC = 0x30e0; + + uint256 internal constant PLOOKUP_DELTA_NUMERATOR_LOC = 0x3100; + uint256 internal constant PLOOKUP_DELTA_DENOMINATOR_LOC = 0x3120; + uint256 internal constant PLOOKUP_DELTA_LOC = 0x3140; + + uint256 internal constant ACCUMULATOR_X_LOC = 0x3160; + uint256 internal constant ACCUMULATOR_Y_LOC = 0x3180; + uint256 internal constant ACCUMULATOR2_X_LOC = 0x31a0; + uint256 internal constant ACCUMULATOR2_Y_LOC = 0x31c0; + uint256 internal constant PAIRING_LHS_X_LOC = 0x31e0; + uint256 internal constant PAIRING_LHS_Y_LOC = 0x3200; + uint256 internal constant PAIRING_RHS_X_LOC = 0x3220; + uint256 internal constant PAIRING_RHS_Y_LOC = 0x3240; + + // ### SUCCESS FLAG MEMORY LOCATIONS + uint256 internal constant GRAND_PRODUCT_SUCCESS_FLAG = 0x3300; + uint256 internal constant ARITHMETIC_TERM_SUCCESS_FLAG = 0x3020; + uint256 internal constant BATCH_OPENING_SUCCESS_FLAG = 0x3340; + uint256 internal constant OPENING_COMMITMENT_SUCCESS_FLAG = 0x3360; + uint256 internal constant PAIRING_PREAMBLE_SUCCESS_FLAG = 0x3380; + uint256 internal constant PAIRING_SUCCESS_FLAG = 0x33a0; + uint256 internal constant RESULT_FLAG = 0x33c0; + + // misc stuff + uint256 internal constant OMEGA_INVERSE_LOC = 0x3400; + uint256 internal constant C_ALPHA_SQR_LOC = 0x3420; + uint256 internal constant C_ALPHA_CUBE_LOC = 0x3440; + uint256 internal constant C_ALPHA_QUAD_LOC = 0x3460; + uint256 internal constant C_ALPHA_BASE_LOC = 0x3480; + + // ### RECURSION VARIABLE MEMORY LOCATIONS + uint256 internal constant RECURSIVE_P1_X_LOC = 0x3500; + uint256 internal constant RECURSIVE_P1_Y_LOC = 0x3520; + uint256 internal constant RECURSIVE_P2_X_LOC = 0x3540; + uint256 internal constant RECURSIVE_P2_Y_LOC = 0x3560; + + uint256 internal constant PUBLIC_INPUTS_HASH_LOCATION = 0x3580; + + // sub-identity storage + uint256 internal constant PERMUTATION_IDENTITY = 0x3600; + uint256 internal constant PLOOKUP_IDENTITY = 0x3620; + uint256 internal constant ARITHMETIC_IDENTITY = 0x3640; + uint256 internal constant SORT_IDENTITY = 0x3660; + uint256 internal constant ELLIPTIC_IDENTITY = 0x3680; + uint256 internal constant AUX_IDENTITY = 0x36a0; + uint256 internal constant AUX_NON_NATIVE_FIELD_EVALUATION = 0x36c0; + uint256 internal constant AUX_LIMB_ACCUMULATOR_EVALUATION = 0x36e0; + uint256 internal constant AUX_RAM_CONSISTENCY_EVALUATION = 0x3700; + uint256 internal constant AUX_ROM_CONSISTENCY_EVALUATION = 0x3720; + uint256 internal constant AUX_MEMORY_EVALUATION = 0x3740; + + uint256 internal constant QUOTIENT_EVAL_LOC = 0x3760; + uint256 internal constant ZERO_POLY_INVERSE_LOC = 0x3780; + + // when hashing public inputs we use memory at NU_CHALLENGE_INPUT_LOC_A, as the hash input size is unknown at compile time + uint256 internal constant NU_CHALLENGE_INPUT_LOC_A = 0x37a0; + uint256 internal constant NU_CHALLENGE_INPUT_LOC_B = 0x37c0; + uint256 internal constant NU_CHALLENGE_INPUT_LOC_C = 0x37e0; + + bytes4 internal constant PUBLIC_INPUT_INVALID_BN128_G1_POINT_SELECTOR = 0xeba9f4a6; + bytes4 internal constant PUBLIC_INPUT_GE_P_SELECTOR = 0x374a972f; + bytes4 internal constant MOD_EXP_FAILURE_SELECTOR = 0xf894a7bc; + bytes4 internal constant EC_SCALAR_MUL_FAILURE_SELECTOR = 0xf755f369; + bytes4 internal constant PROOF_FAILURE_SELECTOR = 0x0711fcec; + + uint256 internal constant ETA_INPUT_LENGTH = 0xc0; // W1, W2, W3 = 6 * 0x20 bytes + + // We need to hash 41 field elements when generating the NU challenge + // w1, w2, w3, w4, s, z, z_lookup, q1, q2, q3, q4, qm, qc, qarith (14) + // qsort, qelliptic, qaux, sigma1, sigma2, sigma, sigma4, (7) + // table1, table2, table3, table4, tabletype, id1, id2, id3, id4, (9) + // w1_omega, w2_omega, w3_omega, w4_omega, s_omega, z_omega, z_lookup_omega, (7) + // table1_omega, table2_omega, table3_omega, table4_omega (4) + uint256 internal constant NU_INPUT_LENGTH = 0x520; // 0x520 = 41 * 0x20 + + // There are ELEVEN G1 group elements added into the transcript in the `beta` round, that we need to skip over + // W1, W2, W3, W4, S, Z, Z_LOOKUP, T1, T2, T3, T4 + uint256 internal constant NU_CALLDATA_SKIP_LENGTH = 0x2c0; // 11 * 0x40 = 0x2c0 + + uint256 internal constant NEGATIVE_INVERSE_OF_2_MODULO_P = + 0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000; + uint256 internal constant LIMB_SIZE = 0x100000000000000000; // 2<<68 + uint256 internal constant SUBLIMB_SHIFT = 0x4000; // 2<<14 + + error PUBLIC_INPUT_COUNT_INVALID(uint256 expected, uint256 actual); + error PUBLIC_INPUT_INVALID_BN128_G1_POINT(); + error PUBLIC_INPUT_GE_P(); + error MOD_EXP_FAILURE(); + error EC_SCALAR_MUL_FAILURE(); + error PROOF_FAILURE(); + + function getVerificationKeyHash() public pure virtual returns (bytes32); + + function loadVerificationKey(uint256 _vk, uint256 _omegaInverseLoc) internal pure virtual; + + /** + * @notice Verify a Ultra Plonk proof + * @param _proof - The serialized proof + * @param _publicInputs - An array of the public inputs + * @return True if proof is valid, reverts otherwise + */ + function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool) { + loadVerificationKey(N_LOC, OMEGA_INVERSE_LOC); + + uint256 requiredPublicInputCount; + assembly { + requiredPublicInputCount := mload(NUM_INPUTS_LOC) + } + if (requiredPublicInputCount != _publicInputs.length) { + revert PUBLIC_INPUT_COUNT_INVALID(requiredPublicInputCount, _publicInputs.length); + } + + assembly { + let q := 21888242871839275222246405745257275088696311157297823662689037894645226208583 // EC group order + let p := 21888242871839275222246405745257275088548364400416034343698204186575808495617 // Prime field order + + /** + * LOAD PROOF FROM CALLDATA + */ + { + let data_ptr := add(calldataload(0x04), 0x24) + + mstore(W1_Y_LOC, mod(calldataload(data_ptr), q)) + mstore(W1_X_LOC, mod(calldataload(add(data_ptr, 0x20)), q)) + + mstore(W2_Y_LOC, mod(calldataload(add(data_ptr, 0x40)), q)) + mstore(W2_X_LOC, mod(calldataload(add(data_ptr, 0x60)), q)) + + mstore(W3_Y_LOC, mod(calldataload(add(data_ptr, 0x80)), q)) + mstore(W3_X_LOC, mod(calldataload(add(data_ptr, 0xa0)), q)) + + mstore(W4_Y_LOC, mod(calldataload(add(data_ptr, 0xc0)), q)) + mstore(W4_X_LOC, mod(calldataload(add(data_ptr, 0xe0)), q)) + + mstore(S_Y_LOC, mod(calldataload(add(data_ptr, 0x100)), q)) + mstore(S_X_LOC, mod(calldataload(add(data_ptr, 0x120)), q)) + mstore(Z_Y_LOC, mod(calldataload(add(data_ptr, 0x140)), q)) + mstore(Z_X_LOC, mod(calldataload(add(data_ptr, 0x160)), q)) + mstore(Z_LOOKUP_Y_LOC, mod(calldataload(add(data_ptr, 0x180)), q)) + mstore(Z_LOOKUP_X_LOC, mod(calldataload(add(data_ptr, 0x1a0)), q)) + mstore(T1_Y_LOC, mod(calldataload(add(data_ptr, 0x1c0)), q)) + mstore(T1_X_LOC, mod(calldataload(add(data_ptr, 0x1e0)), q)) + + mstore(T2_Y_LOC, mod(calldataload(add(data_ptr, 0x200)), q)) + mstore(T2_X_LOC, mod(calldataload(add(data_ptr, 0x220)), q)) + + mstore(T3_Y_LOC, mod(calldataload(add(data_ptr, 0x240)), q)) + mstore(T3_X_LOC, mod(calldataload(add(data_ptr, 0x260)), q)) + + mstore(T4_Y_LOC, mod(calldataload(add(data_ptr, 0x280)), q)) + mstore(T4_X_LOC, mod(calldataload(add(data_ptr, 0x2a0)), q)) + + mstore(W1_EVAL_LOC, mod(calldataload(add(data_ptr, 0x2c0)), p)) + mstore(W2_EVAL_LOC, mod(calldataload(add(data_ptr, 0x2e0)), p)) + mstore(W3_EVAL_LOC, mod(calldataload(add(data_ptr, 0x300)), p)) + mstore(W4_EVAL_LOC, mod(calldataload(add(data_ptr, 0x320)), p)) + mstore(S_EVAL_LOC, mod(calldataload(add(data_ptr, 0x340)), p)) + mstore(Z_EVAL_LOC, mod(calldataload(add(data_ptr, 0x360)), p)) + mstore(Z_LOOKUP_EVAL_LOC, mod(calldataload(add(data_ptr, 0x380)), p)) + mstore(Q1_EVAL_LOC, mod(calldataload(add(data_ptr, 0x3a0)), p)) + mstore(Q2_EVAL_LOC, mod(calldataload(add(data_ptr, 0x3c0)), p)) + mstore(Q3_EVAL_LOC, mod(calldataload(add(data_ptr, 0x3e0)), p)) + mstore(Q4_EVAL_LOC, mod(calldataload(add(data_ptr, 0x400)), p)) + mstore(QM_EVAL_LOC, mod(calldataload(add(data_ptr, 0x420)), p)) + mstore(QC_EVAL_LOC, mod(calldataload(add(data_ptr, 0x440)), p)) + mstore(QARITH_EVAL_LOC, mod(calldataload(add(data_ptr, 0x460)), p)) + mstore(QSORT_EVAL_LOC, mod(calldataload(add(data_ptr, 0x480)), p)) + mstore(QELLIPTIC_EVAL_LOC, mod(calldataload(add(data_ptr, 0x4a0)), p)) + mstore(QAUX_EVAL_LOC, mod(calldataload(add(data_ptr, 0x4c0)), p)) + + mstore(SIGMA1_EVAL_LOC, mod(calldataload(add(data_ptr, 0x4e0)), p)) + mstore(SIGMA2_EVAL_LOC, mod(calldataload(add(data_ptr, 0x500)), p)) + + mstore(SIGMA3_EVAL_LOC, mod(calldataload(add(data_ptr, 0x520)), p)) + mstore(SIGMA4_EVAL_LOC, mod(calldataload(add(data_ptr, 0x540)), p)) + + mstore(TABLE1_EVAL_LOC, mod(calldataload(add(data_ptr, 0x560)), p)) + mstore(TABLE2_EVAL_LOC, mod(calldataload(add(data_ptr, 0x580)), p)) + mstore(TABLE3_EVAL_LOC, mod(calldataload(add(data_ptr, 0x5a0)), p)) + mstore(TABLE4_EVAL_LOC, mod(calldataload(add(data_ptr, 0x5c0)), p)) + mstore(TABLE_TYPE_EVAL_LOC, mod(calldataload(add(data_ptr, 0x5e0)), p)) + + mstore(ID1_EVAL_LOC, mod(calldataload(add(data_ptr, 0x600)), p)) + mstore(ID2_EVAL_LOC, mod(calldataload(add(data_ptr, 0x620)), p)) + mstore(ID3_EVAL_LOC, mod(calldataload(add(data_ptr, 0x640)), p)) + mstore(ID4_EVAL_LOC, mod(calldataload(add(data_ptr, 0x660)), p)) + + mstore(W1_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x680)), p)) + mstore(W2_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x6a0)), p)) + mstore(W3_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x6c0)), p)) + mstore(W4_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x6e0)), p)) + mstore(S_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x700)), p)) + + mstore(Z_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x720)), p)) + + mstore(Z_LOOKUP_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x740)), p)) + mstore(TABLE1_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x760)), p)) + mstore(TABLE2_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x780)), p)) + mstore(TABLE3_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x7a0)), p)) + mstore(TABLE4_OMEGA_EVAL_LOC, mod(calldataload(add(data_ptr, 0x7c0)), p)) + + mstore(PI_Z_Y_LOC, mod(calldataload(add(data_ptr, 0x7e0)), q)) + mstore(PI_Z_X_LOC, mod(calldataload(add(data_ptr, 0x800)), q)) + + mstore(PI_Z_OMEGA_Y_LOC, mod(calldataload(add(data_ptr, 0x820)), q)) + mstore(PI_Z_OMEGA_X_LOC, mod(calldataload(add(data_ptr, 0x840)), q)) + } + + /** + * LOAD RECURSIVE PROOF INTO MEMORY + */ + { + if mload(CONTAINS_RECURSIVE_PROOF_LOC) { + let public_inputs_ptr := add(calldataload(0x24), 0x24) + let index_counter := add(shl(5, mload(RECURSIVE_PROOF_PUBLIC_INPUT_INDICES_LOC)), public_inputs_ptr) + + let x0 := calldataload(index_counter) + x0 := add(x0, shl(68, calldataload(add(index_counter, 0x20)))) + x0 := add(x0, shl(136, calldataload(add(index_counter, 0x40)))) + x0 := add(x0, shl(204, calldataload(add(index_counter, 0x60)))) + let y0 := calldataload(add(index_counter, 0x80)) + y0 := add(y0, shl(68, calldataload(add(index_counter, 0xa0)))) + y0 := add(y0, shl(136, calldataload(add(index_counter, 0xc0)))) + y0 := add(y0, shl(204, calldataload(add(index_counter, 0xe0)))) + let x1 := calldataload(add(index_counter, 0x100)) + x1 := add(x1, shl(68, calldataload(add(index_counter, 0x120)))) + x1 := add(x1, shl(136, calldataload(add(index_counter, 0x140)))) + x1 := add(x1, shl(204, calldataload(add(index_counter, 0x160)))) + let y1 := calldataload(add(index_counter, 0x180)) + y1 := add(y1, shl(68, calldataload(add(index_counter, 0x1a0)))) + y1 := add(y1, shl(136, calldataload(add(index_counter, 0x1c0)))) + y1 := add(y1, shl(204, calldataload(add(index_counter, 0x1e0)))) + mstore(RECURSIVE_P1_X_LOC, x0) + mstore(RECURSIVE_P1_Y_LOC, y0) + mstore(RECURSIVE_P2_X_LOC, x1) + mstore(RECURSIVE_P2_Y_LOC, y1) + + // validate these are valid bn128 G1 points + if iszero(and(and(lt(x0, q), lt(x1, q)), and(lt(y0, q), lt(y1, q)))) { + mstore(0x00, PUBLIC_INPUT_INVALID_BN128_G1_POINT_SELECTOR) + revert(0x00, 0x04) + } + } + } + + { + /** + * Generate initial challenge + */ + mstore(0x00, shl(224, mload(N_LOC))) + mstore(0x04, shl(224, mload(NUM_INPUTS_LOC))) + let challenge := keccak256(0x00, 0x08) + + /** + * Generate eta challenge + */ + mstore(PUBLIC_INPUTS_HASH_LOCATION, challenge) + // The public input location is stored at 0x24, we then add 0x24 to skip selector and the length of public inputs + let public_inputs_start := add(calldataload(0x24), 0x24) + // copy the public inputs over + let public_input_size := mul(mload(NUM_INPUTS_LOC), 0x20) + calldatacopy(add(PUBLIC_INPUTS_HASH_LOCATION, 0x20), public_inputs_start, public_input_size) + + // copy W1, W2, W3 into challenge. Each point is 0x40 bytes, so load 0xc0 = 3 * 0x40 bytes (ETA input length) + let w_start := add(calldataload(0x04), 0x24) + calldatacopy(add(add(PUBLIC_INPUTS_HASH_LOCATION, 0x20), public_input_size), w_start, ETA_INPUT_LENGTH) + + // Challenge is the old challenge + public inputs + W1, W2, W3 (0x20 + public_input_size + 0xc0) + let challenge_bytes_size := add(0x20, add(public_input_size, ETA_INPUT_LENGTH)) + + challenge := keccak256(PUBLIC_INPUTS_HASH_LOCATION, challenge_bytes_size) + { + let eta := mod(challenge, p) + mstore(C_ETA_LOC, eta) + mstore(C_ETA_SQR_LOC, mulmod(eta, eta, p)) + mstore(C_ETA_CUBE_LOC, mulmod(mload(C_ETA_SQR_LOC), eta, p)) + } + + /** + * Generate beta challenge + */ + mstore(0x00, challenge) + mstore(0x20, mload(W4_Y_LOC)) + mstore(0x40, mload(W4_X_LOC)) + mstore(0x60, mload(S_Y_LOC)) + mstore(0x80, mload(S_X_LOC)) + challenge := keccak256(0x00, 0xa0) + mstore(C_BETA_LOC, mod(challenge, p)) + + /** + * Generate gamma challenge + */ + mstore(0x00, challenge) + mstore8(0x20, 0x01) + challenge := keccak256(0x00, 0x21) + mstore(C_GAMMA_LOC, mod(challenge, p)) + + /** + * Generate alpha challenge + */ + mstore(0x00, challenge) + mstore(0x20, mload(Z_Y_LOC)) + mstore(0x40, mload(Z_X_LOC)) + mstore(0x60, mload(Z_LOOKUP_Y_LOC)) + mstore(0x80, mload(Z_LOOKUP_X_LOC)) + challenge := keccak256(0x00, 0xa0) + mstore(C_ALPHA_LOC, mod(challenge, p)) + + /** + * Compute and store some powers of alpha for future computations + */ + let alpha := mload(C_ALPHA_LOC) + mstore(C_ALPHA_SQR_LOC, mulmod(alpha, alpha, p)) + mstore(C_ALPHA_CUBE_LOC, mulmod(mload(C_ALPHA_SQR_LOC), alpha, p)) + mstore(C_ALPHA_QUAD_LOC, mulmod(mload(C_ALPHA_CUBE_LOC), alpha, p)) + mstore(C_ALPHA_BASE_LOC, alpha) + + /** + * Generate zeta challenge + */ + mstore(0x00, challenge) + mstore(0x20, mload(T1_Y_LOC)) + mstore(0x40, mload(T1_X_LOC)) + mstore(0x60, mload(T2_Y_LOC)) + mstore(0x80, mload(T2_X_LOC)) + mstore(0xa0, mload(T3_Y_LOC)) + mstore(0xc0, mload(T3_X_LOC)) + mstore(0xe0, mload(T4_Y_LOC)) + mstore(0x100, mload(T4_X_LOC)) + + challenge := keccak256(0x00, 0x120) + + mstore(C_ZETA_LOC, mod(challenge, p)) + mstore(C_CURRENT_LOC, challenge) + } + + /** + * EVALUATE FIELD OPERATIONS + */ + + /** + * COMPUTE PUBLIC INPUT DELTA + * ΔPI = ∏ᵢ∈ℓ(wᵢ + β σ(i) + γ) / ∏ᵢ∈ℓ(wᵢ + β σ'(i) + γ) + */ + { + let beta := mload(C_BETA_LOC) // β + let gamma := mload(C_GAMMA_LOC) // γ + let work_root := mload(OMEGA_LOC) // ω + let numerator_value := 1 + let denominator_value := 1 + + let p_clone := p // move p to the front of the stack + let valid_inputs := true + + // Load the starting point of the public inputs (jump over the selector and the length of public inputs [0x24]) + let public_inputs_ptr := add(calldataload(0x24), 0x24) + + // endpoint_ptr = public_inputs_ptr + num_inputs * 0x20. // every public input is 0x20 bytes + let endpoint_ptr := add(public_inputs_ptr, mul(mload(NUM_INPUTS_LOC), 0x20)) + + // root_1 = β * 0x05 + let root_1 := mulmod(beta, 0x05, p_clone) // k1.β + // root_2 = β * 0x0c + let root_2 := mulmod(beta, 0x0c, p_clone) + // @note 0x05 + 0x07 == 0x0c == external coset generator + + for {} lt(public_inputs_ptr, endpoint_ptr) { public_inputs_ptr := add(public_inputs_ptr, 0x20) } { + /** + * input = public_input[i] + * valid_inputs &= input < p + * temp = input + gamma + * numerator_value *= (β.σ(i) + wᵢ + γ) // σ(i) = 0x05.ωⁱ + * denominator_value *= (β.σ'(i) + wᵢ + γ) // σ'(i) = 0x0c.ωⁱ + * root_1 *= ω + * root_2 *= ω + */ + + let input := calldataload(public_inputs_ptr) + valid_inputs := and(valid_inputs, lt(input, p_clone)) + let temp := addmod(input, gamma, p_clone) + + numerator_value := mulmod(numerator_value, add(root_1, temp), p_clone) + denominator_value := mulmod(denominator_value, add(root_2, temp), p_clone) + + root_1 := mulmod(root_1, work_root, p_clone) + root_2 := mulmod(root_2, work_root, p_clone) + } + + // Revert if not all public inputs are field elements (i.e. < p) + if iszero(valid_inputs) { + mstore(0x00, PUBLIC_INPUT_GE_P_SELECTOR) + revert(0x00, 0x04) + } + + mstore(DELTA_NUMERATOR_LOC, numerator_value) + mstore(DELTA_DENOMINATOR_LOC, denominator_value) + } + + /** + * Compute Plookup delta factor [γ(1 + β)]^{n-k} + * k = num roots cut out of Z_H = 4 + */ + { + let delta_base := mulmod(mload(C_GAMMA_LOC), addmod(mload(C_BETA_LOC), 1, p), p) + let delta_numerator := delta_base + { + let exponent := mload(N_LOC) + let count := 1 + for {} lt(count, exponent) { count := add(count, count) } { + delta_numerator := mulmod(delta_numerator, delta_numerator, p) + } + } + mstore(PLOOKUP_DELTA_NUMERATOR_LOC, delta_numerator) + + let delta_denominator := mulmod(delta_base, delta_base, p) + delta_denominator := mulmod(delta_denominator, delta_denominator, p) + mstore(PLOOKUP_DELTA_DENOMINATOR_LOC, delta_denominator) + } + /** + * Compute lagrange poly and vanishing poly fractions + */ + { + /** + * vanishing_numerator = zeta + * ZETA_POW_N = zeta^n + * vanishing_numerator -= 1 + * accumulating_root = omega_inverse + * work_root = p - accumulating_root + * domain_inverse = domain_inverse + * vanishing_denominator = zeta + work_root + * work_root *= accumulating_root + * vanishing_denominator *= (zeta + work_root) + * work_root *= accumulating_root + * vanishing_denominator *= (zeta + work_root) + * vanishing_denominator *= (zeta + (zeta + accumulating_root)) + * work_root = omega + * lagrange_numerator = vanishing_numerator * domain_inverse + * l_start_denominator = zeta - 1 + * accumulating_root = work_root^2 + * l_end_denominator = accumulating_root^2 * work_root * zeta - 1 + * Note: l_end_denominator term contains a term \omega^5 to cut out 5 roots of unity from vanishing poly + */ + + let zeta := mload(C_ZETA_LOC) + + // compute zeta^n, where n is a power of 2 + let vanishing_numerator := zeta + { + // pow_small + let exponent := mload(N_LOC) + let count := 1 + for {} lt(count, exponent) { count := add(count, count) } { + vanishing_numerator := mulmod(vanishing_numerator, vanishing_numerator, p) + } + } + mstore(ZETA_POW_N_LOC, vanishing_numerator) + vanishing_numerator := addmod(vanishing_numerator, sub(p, 1), p) + + let accumulating_root := mload(OMEGA_INVERSE_LOC) + let work_root := sub(p, accumulating_root) + let domain_inverse := mload(DOMAIN_INVERSE_LOC) + + let vanishing_denominator := addmod(zeta, work_root, p) + work_root := mulmod(work_root, accumulating_root, p) + vanishing_denominator := mulmod(vanishing_denominator, addmod(zeta, work_root, p), p) + work_root := mulmod(work_root, accumulating_root, p) + vanishing_denominator := mulmod(vanishing_denominator, addmod(zeta, work_root, p), p) + vanishing_denominator := + mulmod(vanishing_denominator, addmod(zeta, mulmod(work_root, accumulating_root, p), p), p) + + work_root := mload(OMEGA_LOC) + + let lagrange_numerator := mulmod(vanishing_numerator, domain_inverse, p) + let l_start_denominator := addmod(zeta, sub(p, 1), p) + + accumulating_root := mulmod(work_root, work_root, p) + + let l_end_denominator := + addmod( + mulmod(mulmod(mulmod(accumulating_root, accumulating_root, p), work_root, p), zeta, p), sub(p, 1), p + ) + + /** + * Compute inversions using Montgomery's batch inversion trick + */ + let accumulator := mload(DELTA_DENOMINATOR_LOC) + let t0 := accumulator + accumulator := mulmod(accumulator, vanishing_denominator, p) + let t1 := accumulator + accumulator := mulmod(accumulator, vanishing_numerator, p) + let t2 := accumulator + accumulator := mulmod(accumulator, l_start_denominator, p) + let t3 := accumulator + accumulator := mulmod(accumulator, mload(PLOOKUP_DELTA_DENOMINATOR_LOC), p) + let t4 := accumulator + { + mstore(0, 0x20) + mstore(0x20, 0x20) + mstore(0x40, 0x20) + mstore(0x60, mulmod(accumulator, l_end_denominator, p)) + mstore(0x80, sub(p, 2)) + mstore(0xa0, p) + if iszero(staticcall(gas(), 0x05, 0x00, 0xc0, 0x00, 0x20)) { + mstore(0x0, MOD_EXP_FAILURE_SELECTOR) + revert(0x00, 0x04) + } + accumulator := mload(0x00) + } + + t4 := mulmod(accumulator, t4, p) + accumulator := mulmod(accumulator, l_end_denominator, p) + + t3 := mulmod(accumulator, t3, p) + accumulator := mulmod(accumulator, mload(PLOOKUP_DELTA_DENOMINATOR_LOC), p) + + t2 := mulmod(accumulator, t2, p) + accumulator := mulmod(accumulator, l_start_denominator, p) + + t1 := mulmod(accumulator, t1, p) + accumulator := mulmod(accumulator, vanishing_numerator, p) + + t0 := mulmod(accumulator, t0, p) + accumulator := mulmod(accumulator, vanishing_denominator, p) + + accumulator := mulmod(mulmod(accumulator, accumulator, p), mload(DELTA_DENOMINATOR_LOC), p) + + mstore(PUBLIC_INPUT_DELTA_LOC, mulmod(mload(DELTA_NUMERATOR_LOC), accumulator, p)) + mstore(ZERO_POLY_LOC, mulmod(vanishing_numerator, t0, p)) + mstore(ZERO_POLY_INVERSE_LOC, mulmod(vanishing_denominator, t1, p)) + mstore(L_START_LOC, mulmod(lagrange_numerator, t2, p)) + mstore(PLOOKUP_DELTA_LOC, mulmod(mload(PLOOKUP_DELTA_NUMERATOR_LOC), t3, p)) + mstore(L_END_LOC, mulmod(lagrange_numerator, t4, p)) + } + + /** + * UltraPlonk Widget Ordering: + * + * 1. Permutation widget + * 2. Plookup widget + * 3. Arithmetic widget + * 4. Fixed base widget (?) + * 5. GenPermSort widget + * 6. Elliptic widget + * 7. Auxiliary widget + */ + + /** + * COMPUTE PERMUTATION WIDGET EVALUATION + */ + { + let alpha := mload(C_ALPHA_LOC) + let beta := mload(C_BETA_LOC) + let gamma := mload(C_GAMMA_LOC) + + /** + * t1 = (W1 + gamma + beta * ID1) * (W2 + gamma + beta * ID2) + * t2 = (W3 + gamma + beta * ID3) * (W4 + gamma + beta * ID4) + * result = alpha_base * z_eval * t1 * t2 + * t1 = (W1 + gamma + beta * sigma_1_eval) * (W2 + gamma + beta * sigma_2_eval) + * t2 = (W2 + gamma + beta * sigma_3_eval) * (W3 + gamma + beta * sigma_4_eval) + * result -= (alpha_base * z_omega_eval * t1 * t2) + */ + let t1 := + mulmod( + add(add(mload(W1_EVAL_LOC), gamma), mulmod(beta, mload(ID1_EVAL_LOC), p)), + add(add(mload(W2_EVAL_LOC), gamma), mulmod(beta, mload(ID2_EVAL_LOC), p)), + p + ) + let t2 := + mulmod( + add(add(mload(W3_EVAL_LOC), gamma), mulmod(beta, mload(ID3_EVAL_LOC), p)), + add(add(mload(W4_EVAL_LOC), gamma), mulmod(beta, mload(ID4_EVAL_LOC), p)), + p + ) + let result := mulmod(mload(C_ALPHA_BASE_LOC), mulmod(mload(Z_EVAL_LOC), mulmod(t1, t2, p), p), p) + t1 := + mulmod( + add(add(mload(W1_EVAL_LOC), gamma), mulmod(beta, mload(SIGMA1_EVAL_LOC), p)), + add(add(mload(W2_EVAL_LOC), gamma), mulmod(beta, mload(SIGMA2_EVAL_LOC), p)), + p + ) + t2 := + mulmod( + add(add(mload(W3_EVAL_LOC), gamma), mulmod(beta, mload(SIGMA3_EVAL_LOC), p)), + add(add(mload(W4_EVAL_LOC), gamma), mulmod(beta, mload(SIGMA4_EVAL_LOC), p)), + p + ) + result := + addmod( + result, + sub(p, mulmod(mload(C_ALPHA_BASE_LOC), mulmod(mload(Z_OMEGA_EVAL_LOC), mulmod(t1, t2, p), p), p)), + p + ) + + /** + * alpha_base *= alpha + * result += alpha_base . (L_{n-k}(ʓ) . (z(ʓ.ω) - ∆_{PI})) + * alpha_base *= alpha + * result += alpha_base . (L_1(ʓ)(Z(ʓ) - 1)) + * alpha_Base *= alpha + */ + mstore(C_ALPHA_BASE_LOC, mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_LOC), p)) + result := + addmod( + result, + mulmod( + mload(C_ALPHA_BASE_LOC), + mulmod( + mload(L_END_LOC), + addmod(mload(Z_OMEGA_EVAL_LOC), sub(p, mload(PUBLIC_INPUT_DELTA_LOC)), p), + p + ), + p + ), + p + ) + mstore(C_ALPHA_BASE_LOC, mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_LOC), p)) + mstore( + PERMUTATION_IDENTITY, + addmod( + result, + mulmod( + mload(C_ALPHA_BASE_LOC), + mulmod(mload(L_START_LOC), addmod(mload(Z_EVAL_LOC), sub(p, 1), p), p), + p + ), + p + ) + ) + mstore(C_ALPHA_BASE_LOC, mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_LOC), p)) + } + + /** + * COMPUTE PLOOKUP WIDGET EVALUATION + */ + { + /** + * Goal: f = (w1(z) + q2.w1(zω)) + η(w2(z) + qm.w2(zω)) + η²(w3(z) + qc.w_3(zω)) + q3(z).η³ + * f = η.q3(z) + * f += (w3(z) + qc.w_3(zω)) + * f *= η + * f += (w2(z) + qm.w2(zω)) + * f *= η + * f += (w1(z) + q2.w1(zω)) + */ + let f := mulmod(mload(C_ETA_LOC), mload(Q3_EVAL_LOC), p) + f := + addmod(f, addmod(mload(W3_EVAL_LOC), mulmod(mload(QC_EVAL_LOC), mload(W3_OMEGA_EVAL_LOC), p), p), p) + f := mulmod(f, mload(C_ETA_LOC), p) + f := + addmod(f, addmod(mload(W2_EVAL_LOC), mulmod(mload(QM_EVAL_LOC), mload(W2_OMEGA_EVAL_LOC), p), p), p) + f := mulmod(f, mload(C_ETA_LOC), p) + f := + addmod(f, addmod(mload(W1_EVAL_LOC), mulmod(mload(Q2_EVAL_LOC), mload(W1_OMEGA_EVAL_LOC), p), p), p) + + // t(z) = table4(z).η³ + table3(z).η² + table2(z).η + table1(z) + let t := + addmod( + addmod( + addmod( + mulmod(mload(TABLE4_EVAL_LOC), mload(C_ETA_CUBE_LOC), p), + mulmod(mload(TABLE3_EVAL_LOC), mload(C_ETA_SQR_LOC), p), + p + ), + mulmod(mload(TABLE2_EVAL_LOC), mload(C_ETA_LOC), p), + p + ), + mload(TABLE1_EVAL_LOC), + p + ) + + // t(zw) = table4(zw).η³ + table3(zw).η² + table2(zw).η + table1(zw) + let t_omega := + addmod( + addmod( + addmod( + mulmod(mload(TABLE4_OMEGA_EVAL_LOC), mload(C_ETA_CUBE_LOC), p), + mulmod(mload(TABLE3_OMEGA_EVAL_LOC), mload(C_ETA_SQR_LOC), p), + p + ), + mulmod(mload(TABLE2_OMEGA_EVAL_LOC), mload(C_ETA_LOC), p), + p + ), + mload(TABLE1_OMEGA_EVAL_LOC), + p + ) + + /** + * Goal: numerator = (TABLE_TYPE_EVAL * f(z) + γ) * (t(z) + βt(zω) + γ(β + 1)) * (β + 1) + * gamma_beta_constant = γ(β + 1) + * numerator = f * TABLE_TYPE_EVAL + gamma + * temp0 = t(z) + t(zω) * β + gamma_beta_constant + * numerator *= temp0 + * numerator *= (β + 1) + * temp0 = alpha * l_1 + * numerator += temp0 + * numerator *= z_lookup(z) + * numerator -= temp0 + */ + let gamma_beta_constant := mulmod(mload(C_GAMMA_LOC), addmod(mload(C_BETA_LOC), 1, p), p) + let numerator := addmod(mulmod(f, mload(TABLE_TYPE_EVAL_LOC), p), mload(C_GAMMA_LOC), p) + let temp0 := addmod(addmod(t, mulmod(t_omega, mload(C_BETA_LOC), p), p), gamma_beta_constant, p) + numerator := mulmod(numerator, temp0, p) + numerator := mulmod(numerator, addmod(mload(C_BETA_LOC), 1, p), p) + temp0 := mulmod(mload(C_ALPHA_LOC), mload(L_START_LOC), p) + numerator := addmod(numerator, temp0, p) + numerator := mulmod(numerator, mload(Z_LOOKUP_EVAL_LOC), p) + numerator := addmod(numerator, sub(p, temp0), p) + + /** + * Goal: denominator = z_lookup(zω)*[s(z) + βs(zω) + γ(1 + β)] - [z_lookup(zω) - [γ(1 + β)]^{n-k}]*α²L_end(z) + * note: delta_factor = [γ(1 + β)]^{n-k} + * denominator = s(z) + βs(zω) + γ(β + 1) + * temp1 = α²L_end(z) + * denominator -= temp1 + * denominator *= z_lookup(zω) + * denominator += temp1 * delta_factor + * PLOOKUP_IDENTITY = (numerator - denominator).alpha_base + * alpha_base *= alpha^3 + */ + let denominator := + addmod( + addmod(mload(S_EVAL_LOC), mulmod(mload(S_OMEGA_EVAL_LOC), mload(C_BETA_LOC), p), p), + gamma_beta_constant, + p + ) + let temp1 := mulmod(mload(C_ALPHA_SQR_LOC), mload(L_END_LOC), p) + denominator := addmod(denominator, sub(p, temp1), p) + denominator := mulmod(denominator, mload(Z_LOOKUP_OMEGA_EVAL_LOC), p) + denominator := addmod(denominator, mulmod(temp1, mload(PLOOKUP_DELTA_LOC), p), p) + + mstore(PLOOKUP_IDENTITY, mulmod(addmod(numerator, sub(p, denominator), p), mload(C_ALPHA_BASE_LOC), p)) + + // update alpha + mstore(C_ALPHA_BASE_LOC, mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_CUBE_LOC), p)) + } + + /** + * COMPUTE ARITHMETIC WIDGET EVALUATION + */ + { + /** + * The basic arithmetic gate identity in standard plonk is as follows. + * (w_1 . w_2 . q_m) + (w_1 . q_1) + (w_2 . q_2) + (w_3 . q_3) + (w_4 . q_4) + q_c = 0 + * However, for Ultraplonk, we extend this to support "passing" wires between rows (shown without alpha scaling below): + * q_arith * ( ( (-1/2) * (q_arith - 3) * q_m * w_1 * w_2 + q_1 * w_1 + q_2 * w_2 + q_3 * w_3 + q_4 * w_4 + q_c ) + + * (q_arith - 1)*( α * (q_arith - 2) * (w_1 + w_4 - w_1_omega + q_m) + w_4_omega) ) = 0 + * + * This formula results in several cases depending on q_arith: + * 1. q_arith == 0: Arithmetic gate is completely disabled + * + * 2. q_arith == 1: Everything in the minigate on the right is disabled. The equation is just a standard plonk equation + * with extra wires: q_m * w_1 * w_2 + q_1 * w_1 + q_2 * w_2 + q_3 * w_3 + q_4 * w_4 + q_c = 0 + * + * 3. q_arith == 2: The (w_1 + w_4 - ...) term is disabled. THe equation is: + * (1/2) * q_m * w_1 * w_2 + q_1 * w_1 + q_2 * w_2 + q_3 * w_3 + q_4 * w_4 + q_c + w_4_omega = 0 + * It allows defining w_4 at next index (w_4_omega) in terms of current wire values + * + * 4. q_arith == 3: The product of w_1 and w_2 is disabled, but a mini addition gate is enabled. α allows us to split + * the equation into two: + * + * q_1 * w_1 + q_2 * w_2 + q_3 * w_3 + q_4 * w_4 + q_c + 2 * w_4_omega = 0 + * and + * w_1 + w_4 - w_1_omega + q_m = 0 (we are reusing q_m here) + * + * 5. q_arith > 3: The product of w_1 and w_2 is scaled by (q_arith - 3), while the w_4_omega term is scaled by (q_arith - 1). + * The equation can be split into two: + * + * (q_arith - 3)* q_m * w_1 * w_ 2 + q_1 * w_1 + q_2 * w_2 + q_3 * w_3 + q_4 * w_4 + q_c + (q_arith - 1) * w_4_omega = 0 + * and + * w_1 + w_4 - w_1_omega + q_m = 0 + * + * The problem that q_m is used both in both equations can be dealt with by appropriately changing selector values at + * the next gate. Then we can treat (q_arith - 1) as a simulated q_6 selector and scale q_m to handle (q_arith - 3) at + * product. + */ + + let w1q1 := mulmod(mload(W1_EVAL_LOC), mload(Q1_EVAL_LOC), p) + let w2q2 := mulmod(mload(W2_EVAL_LOC), mload(Q2_EVAL_LOC), p) + let w3q3 := mulmod(mload(W3_EVAL_LOC), mload(Q3_EVAL_LOC), p) + let w4q3 := mulmod(mload(W4_EVAL_LOC), mload(Q4_EVAL_LOC), p) + + // @todo - Add a explicit test that hits QARITH == 3 + // w1w2qm := (w_1 . w_2 . q_m . (QARITH_EVAL_LOC - 3)) / 2 + let w1w2qm := + mulmod( + mulmod( + mulmod(mulmod(mload(W1_EVAL_LOC), mload(W2_EVAL_LOC), p), mload(QM_EVAL_LOC), p), + addmod(mload(QARITH_EVAL_LOC), sub(p, 3), p), + p + ), + NEGATIVE_INVERSE_OF_2_MODULO_P, + p + ) + + // (w_1 . w_2 . q_m . (q_arith - 3)) / -2) + (w_1 . q_1) + (w_2 . q_2) + (w_3 . q_3) + (w_4 . q_4) + q_c + let identity := + addmod( + mload(QC_EVAL_LOC), addmod(w4q3, addmod(w3q3, addmod(w2q2, addmod(w1q1, w1w2qm, p), p), p), p), p + ) + + // if q_arith == 3 we evaluate an additional mini addition gate (on top of the regular one), where: + // w_1 + w_4 - w_1_omega + q_m = 0 + // we use this gate to save an addition gate when adding or subtracting non-native field elements + // α * (q_arith - 2) * (w_1 + w_4 - w_1_omega + q_m) + let extra_small_addition_gate_identity := + mulmod( + mload(C_ALPHA_LOC), + mulmod( + addmod(mload(QARITH_EVAL_LOC), sub(p, 2), p), + addmod( + mload(QM_EVAL_LOC), + addmod( + sub(p, mload(W1_OMEGA_EVAL_LOC)), addmod(mload(W1_EVAL_LOC), mload(W4_EVAL_LOC), p), p + ), + p + ), + p + ), + p + ) + + // if q_arith == 2 OR q_arith == 3 we add the 4th wire of the NEXT gate into the arithmetic identity + // N.B. if q_arith > 2, this wire value will be scaled by (q_arith - 1) relative to the other gate wires! + // alpha_base * q_arith * (identity + (q_arith - 1) * (w_4_omega + extra_small_addition_gate_identity)) + mstore( + ARITHMETIC_IDENTITY, + mulmod( + mload(C_ALPHA_BASE_LOC), + mulmod( + mload(QARITH_EVAL_LOC), + addmod( + identity, + mulmod( + addmod(mload(QARITH_EVAL_LOC), sub(p, 1), p), + addmod(mload(W4_OMEGA_EVAL_LOC), extra_small_addition_gate_identity, p), + p + ), + p + ), + p + ), + p + ) + ) + + // update alpha + mstore(C_ALPHA_BASE_LOC, mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_SQR_LOC), p)) + } + + /** + * COMPUTE GENPERMSORT WIDGET EVALUATION + */ + { + /** + * D1 = (w2 - w1) + * D2 = (w3 - w2) + * D3 = (w4 - w3) + * D4 = (w1_omega - w4) + * + * α_a = alpha_base + * α_b = alpha_base * α + * α_c = alpha_base * α^2 + * α_d = alpha_base * α^3 + * + * range_accumulator = ( + * D1(D1 - 1)(D1 - 2)(D1 - 3).α_a + + * D2(D2 - 1)(D2 - 2)(D2 - 3).α_b + + * D3(D3 - 1)(D3 - 2)(D3 - 3).α_c + + * D4(D4 - 1)(D4 - 2)(D4 - 3).α_d + + * ) . q_sort + */ + let minus_two := sub(p, 2) + let minus_three := sub(p, 3) + let d1 := addmod(mload(W2_EVAL_LOC), sub(p, mload(W1_EVAL_LOC)), p) + let d2 := addmod(mload(W3_EVAL_LOC), sub(p, mload(W2_EVAL_LOC)), p) + let d3 := addmod(mload(W4_EVAL_LOC), sub(p, mload(W3_EVAL_LOC)), p) + let d4 := addmod(mload(W1_OMEGA_EVAL_LOC), sub(p, mload(W4_EVAL_LOC)), p) + + let range_accumulator := + mulmod( + mulmod( + mulmod(addmod(mulmod(d1, d1, p), sub(p, d1), p), addmod(d1, minus_two, p), p), + addmod(d1, minus_three, p), + p + ), + mload(C_ALPHA_BASE_LOC), + p + ) + range_accumulator := + addmod( + range_accumulator, + mulmod( + mulmod( + mulmod(addmod(mulmod(d2, d2, p), sub(p, d2), p), addmod(d2, minus_two, p), p), + addmod(d2, minus_three, p), + p + ), + mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_LOC), p), + p + ), + p + ) + range_accumulator := + addmod( + range_accumulator, + mulmod( + mulmod( + mulmod(addmod(mulmod(d3, d3, p), sub(p, d3), p), addmod(d3, minus_two, p), p), + addmod(d3, minus_three, p), + p + ), + mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_SQR_LOC), p), + p + ), + p + ) + range_accumulator := + addmod( + range_accumulator, + mulmod( + mulmod( + mulmod(addmod(mulmod(d4, d4, p), sub(p, d4), p), addmod(d4, minus_two, p), p), + addmod(d4, minus_three, p), + p + ), + mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_CUBE_LOC), p), + p + ), + p + ) + range_accumulator := mulmod(range_accumulator, mload(QSORT_EVAL_LOC), p) + + mstore(SORT_IDENTITY, range_accumulator) + + // update alpha + mstore(C_ALPHA_BASE_LOC, mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_QUAD_LOC), p)) + } + + /** + * COMPUTE ELLIPTIC WIDGET EVALUATION + */ + { + /** + * endo_term = (-x_2) * x_1 * (x_3 * 2 + x_1) * q_beta + * endo_sqr_term = x_2^2 + * endo_sqr_term *= (x_3 - x_1) + * endo_sqr_term *= q_beta^2 + * leftovers = x_2^2 + * leftovers *= x_2 + * leftovers += x_1^2 * (x_3 + x_1) @follow-up Invalid comment in BB widget + * leftovers -= (y_2^2 + y_1^2) + * sign_term = y_2 * y_1 + * sign_term += sign_term + * sign_term *= q_sign + */ + + let endo_term := + mulmod( + mulmod( + mulmod(sub(p, mload(X2_EVAL_LOC)), mload(X1_EVAL_LOC), p), + addmod(addmod(mload(X3_EVAL_LOC), mload(X3_EVAL_LOC), p), mload(X1_EVAL_LOC), p), + p + ), + mload(QBETA_LOC), + p + ) + + let endo_sqr_term := mulmod(mload(X2_EVAL_LOC), mload(X2_EVAL_LOC), p) + endo_sqr_term := mulmod(endo_sqr_term, addmod(mload(X3_EVAL_LOC), sub(p, mload(X1_EVAL_LOC)), p), p) + endo_sqr_term := mulmod(endo_sqr_term, mload(QBETA_SQR_LOC), p) + + let leftovers := mulmod(mload(X2_EVAL_LOC), mload(X2_EVAL_LOC), p) + leftovers := mulmod(leftovers, mload(X2_EVAL_LOC), p) + leftovers := + addmod( + leftovers, + mulmod( + mulmod(mload(X1_EVAL_LOC), mload(X1_EVAL_LOC), p), + addmod(mload(X3_EVAL_LOC), mload(X1_EVAL_LOC), p), + p + ), + p + ) + leftovers := + addmod( + leftovers, + sub( + p, + addmod( + mulmod(mload(Y2_EVAL_LOC), mload(Y2_EVAL_LOC), p), + mulmod(mload(Y1_EVAL_LOC), mload(Y1_EVAL_LOC), p), + p + ) + ), + p + ) + + let sign_term := mulmod(mload(Y2_EVAL_LOC), mload(Y1_EVAL_LOC), p) + sign_term := addmod(sign_term, sign_term, p) + sign_term := mulmod(sign_term, mload(QSIGN_LOC), p) + + /** + * x_identity = endo_term + endo_sqr_term + sign_term + leftovers + * x_identity *= alpha_base + * endo_term = (x_2 * q_beta) * (y_3 + y_1) + * sign_term = -((y2 * q_sign) * (x_1 + x_3)) + * leftovers = - x1 * (y_3 + y_1) + y_1 * (x_1 - x_3) + * y_identity = (endo_term + sign_term + leftovers) * (alpha_base * α) + */ + + let x_identity := addmod(addmod(endo_term, endo_sqr_term, p), addmod(sign_term, leftovers, p), p) + x_identity := mulmod(x_identity, mload(C_ALPHA_BASE_LOC), p) + endo_term := + mulmod( + mulmod(mload(X2_EVAL_LOC), mload(QBETA_LOC), p), + addmod(mload(Y3_EVAL_LOC), mload(Y1_EVAL_LOC), p), + p + ) + sign_term := + sub( + p, + mulmod( + mulmod(mload(Y2_EVAL_LOC), mload(QSIGN_LOC), p), + addmod(mload(X1_EVAL_LOC), sub(p, mload(X3_EVAL_LOC)), p), + p + ) + ) + leftovers := + addmod( + sub(p, mulmod(mload(X1_EVAL_LOC), addmod(mload(Y3_EVAL_LOC), mload(Y1_EVAL_LOC), p), p)), + mulmod(mload(Y1_EVAL_LOC), addmod(mload(X1_EVAL_LOC), sub(p, mload(X3_EVAL_LOC)), p), p), + p + ) + let y_identity := + mulmod( + addmod(addmod(endo_term, sign_term, p), leftovers, p), + mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_LOC), p), + p + ) + + // ELLIPTIC_IDENTITY = (x_identity + y_identity) * Q_ELLIPTIC_EVAL + mstore(ELLIPTIC_IDENTITY, mulmod(addmod(x_identity, y_identity, p), mload(QELLIPTIC_EVAL_LOC), p)) + + // update alpha + // The paper says to use ALPHA^2, we use ALPHA^4 this is a small oversight in the prover protocol + mstore(C_ALPHA_BASE_LOC, mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_QUAD_LOC), p)) + } + + /** + * COMPUTE AUXILIARY WIDGET EVALUATION + */ + { + { + /** + * Non native field arithmetic gate 2 + * _ _ + * / _ _ _ 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 | + * \_ _/ + * + * limb_subproduct = w_1 . w_2_omega + w_1_omega . w_2 + * non_native_field_gate_2 = w_1 * w_4 + w_4 * w_3 - w_3_omega + * non_native_field_gate_2 = non_native_field_gate_2 * limb_size + * non_native_field_gate_2 -= w_4_omega + * non_native_field_gate_2 += limb_subproduct + * non_native_field_gate_2 *= q_4 + * limb_subproduct *= limb_size + * limb_subproduct += w_1_omega * w_2_omega + * non_native_field_gate_1 = (limb_subproduct + w_3 + w_4) * q_3 + * non_native_field_gate_3 = (limb_subproduct + w_4 - (w_3_omega + w_4_omega)) * q_m + * non_native_field_identity = (non_native_field_gate_1 + non_native_field_gate_2 + non_native_field_gate_3) * q_2 + */ + + let limb_subproduct := + addmod( + mulmod(mload(W1_EVAL_LOC), mload(W2_OMEGA_EVAL_LOC), p), + mulmod(mload(W1_OMEGA_EVAL_LOC), mload(W2_EVAL_LOC), p), + p + ) + + let non_native_field_gate_2 := + addmod( + addmod( + mulmod(mload(W1_EVAL_LOC), mload(W4_EVAL_LOC), p), + mulmod(mload(W2_EVAL_LOC), mload(W3_EVAL_LOC), p), + p + ), + sub(p, mload(W3_OMEGA_EVAL_LOC)), + p + ) + non_native_field_gate_2 := mulmod(non_native_field_gate_2, LIMB_SIZE, p) + non_native_field_gate_2 := addmod(non_native_field_gate_2, sub(p, mload(W4_OMEGA_EVAL_LOC)), p) + non_native_field_gate_2 := addmod(non_native_field_gate_2, limb_subproduct, p) + non_native_field_gate_2 := mulmod(non_native_field_gate_2, mload(Q4_EVAL_LOC), p) + limb_subproduct := mulmod(limb_subproduct, LIMB_SIZE, p) + limb_subproduct := + addmod(limb_subproduct, mulmod(mload(W1_OMEGA_EVAL_LOC), mload(W2_OMEGA_EVAL_LOC), p), p) + let non_native_field_gate_1 := + mulmod( + addmod(limb_subproduct, sub(p, addmod(mload(W3_EVAL_LOC), mload(W4_EVAL_LOC), p)), p), + mload(Q3_EVAL_LOC), + p + ) + let non_native_field_gate_3 := + mulmod( + addmod( + addmod(limb_subproduct, mload(W4_EVAL_LOC), p), + sub(p, addmod(mload(W3_OMEGA_EVAL_LOC), mload(W4_OMEGA_EVAL_LOC), p)), + p + ), + mload(QM_EVAL_LOC), + p + ) + let non_native_field_identity := + mulmod( + addmod(addmod(non_native_field_gate_1, non_native_field_gate_2, p), non_native_field_gate_3, p), + mload(Q2_EVAL_LOC), + p + ) + + mstore(AUX_NON_NATIVE_FIELD_EVALUATION, non_native_field_identity) + } + + { + /** + * limb_accumulator_1 = w_2_omega; + * limb_accumulator_1 *= SUBLIMB_SHIFT; + * limb_accumulator_1 += w_1_omega; + * limb_accumulator_1 *= SUBLIMB_SHIFT; + * limb_accumulator_1 += w_3; + * limb_accumulator_1 *= SUBLIMB_SHIFT; + * limb_accumulator_1 += w_2; + * limb_accumulator_1 *= SUBLIMB_SHIFT; + * limb_accumulator_1 += w_1; + * limb_accumulator_1 -= w_4; + * limb_accumulator_1 *= q_4; + */ + let limb_accumulator_1 := mulmod(mload(W2_OMEGA_EVAL_LOC), SUBLIMB_SHIFT, p) + limb_accumulator_1 := addmod(limb_accumulator_1, mload(W1_OMEGA_EVAL_LOC), p) + limb_accumulator_1 := mulmod(limb_accumulator_1, SUBLIMB_SHIFT, p) + limb_accumulator_1 := addmod(limb_accumulator_1, mload(W3_EVAL_LOC), p) + limb_accumulator_1 := mulmod(limb_accumulator_1, SUBLIMB_SHIFT, p) + limb_accumulator_1 := addmod(limb_accumulator_1, mload(W2_EVAL_LOC), p) + limb_accumulator_1 := mulmod(limb_accumulator_1, SUBLIMB_SHIFT, p) + limb_accumulator_1 := addmod(limb_accumulator_1, mload(W1_EVAL_LOC), p) + limb_accumulator_1 := addmod(limb_accumulator_1, sub(p, mload(W4_EVAL_LOC)), p) + limb_accumulator_1 := mulmod(limb_accumulator_1, mload(Q4_EVAL_LOC), p) + + /** + * limb_accumulator_2 = w_3_omega; + * limb_accumulator_2 *= SUBLIMB_SHIFT; + * limb_accumulator_2 += w_2_omega; + * limb_accumulator_2 *= SUBLIMB_SHIFT; + * limb_accumulator_2 += w_1_omega; + * limb_accumulator_2 *= SUBLIMB_SHIFT; + * limb_accumulator_2 += w_4; + * limb_accumulator_2 *= SUBLIMB_SHIFT; + * limb_accumulator_2 += w_3; + * limb_accumulator_2 -= w_4_omega; + * limb_accumulator_2 *= q_m; + */ + let limb_accumulator_2 := mulmod(mload(W3_OMEGA_EVAL_LOC), SUBLIMB_SHIFT, p) + limb_accumulator_2 := addmod(limb_accumulator_2, mload(W2_OMEGA_EVAL_LOC), p) + limb_accumulator_2 := mulmod(limb_accumulator_2, SUBLIMB_SHIFT, p) + limb_accumulator_2 := addmod(limb_accumulator_2, mload(W1_OMEGA_EVAL_LOC), p) + limb_accumulator_2 := mulmod(limb_accumulator_2, SUBLIMB_SHIFT, p) + limb_accumulator_2 := addmod(limb_accumulator_2, mload(W4_EVAL_LOC), p) + limb_accumulator_2 := mulmod(limb_accumulator_2, SUBLIMB_SHIFT, p) + limb_accumulator_2 := addmod(limb_accumulator_2, mload(W3_EVAL_LOC), p) + limb_accumulator_2 := addmod(limb_accumulator_2, sub(p, mload(W4_OMEGA_EVAL_LOC)), p) + limb_accumulator_2 := mulmod(limb_accumulator_2, mload(QM_EVAL_LOC), p) + + mstore( + AUX_LIMB_ACCUMULATOR_EVALUATION, + mulmod(addmod(limb_accumulator_1, limb_accumulator_2, p), mload(Q3_EVAL_LOC), p) + ) + } + + { + /** + * memory_record_check = w_3; + * memory_record_check *= eta; + * memory_record_check += w_2; + * memory_record_check *= eta; + * memory_record_check += w_1; + * memory_record_check *= eta; + * memory_record_check += q_c; + * + * partial_record_check = memory_record_check; + * + * memory_record_check -= w_4; + */ + + let memory_record_check := mulmod(mload(W3_EVAL_LOC), mload(C_ETA_LOC), p) + memory_record_check := addmod(memory_record_check, mload(W2_EVAL_LOC), p) + memory_record_check := mulmod(memory_record_check, mload(C_ETA_LOC), p) + memory_record_check := addmod(memory_record_check, mload(W1_EVAL_LOC), p) + memory_record_check := mulmod(memory_record_check, mload(C_ETA_LOC), p) + memory_record_check := addmod(memory_record_check, mload(QC_EVAL_LOC), p) + + let partial_record_check := memory_record_check + memory_record_check := addmod(memory_record_check, sub(p, mload(W4_EVAL_LOC)), p) + + mstore(AUX_MEMORY_EVALUATION, memory_record_check) + + // index_delta = w_1_omega - w_1 + let index_delta := addmod(mload(W1_OMEGA_EVAL_LOC), sub(p, mload(W1_EVAL_LOC)), p) + // record_delta = w_4_omega - w_4 + let record_delta := addmod(mload(W4_OMEGA_EVAL_LOC), sub(p, mload(W4_EVAL_LOC)), p) + // index_is_monotonically_increasing = index_delta * (index_delta - 1) + let index_is_monotonically_increasing := mulmod(index_delta, addmod(index_delta, sub(p, 1), p), p) + + // adjacent_values_match_if_adjacent_indices_match = record_delta * (1 - index_delta) + let adjacent_values_match_if_adjacent_indices_match := + mulmod(record_delta, addmod(1, sub(p, index_delta), p), p) + + // AUX_ROM_CONSISTENCY_EVALUATION = ((adjacent_values_match_if_adjacent_indices_match * alpha) + index_is_monotonically_increasing) * alpha + partial_record_check + mstore( + AUX_ROM_CONSISTENCY_EVALUATION, + addmod( + mulmod( + addmod( + mulmod(adjacent_values_match_if_adjacent_indices_match, mload(C_ALPHA_LOC), p), + index_is_monotonically_increasing, + p + ), + mload(C_ALPHA_LOC), + p + ), + memory_record_check, + p + ) + ) + + { + /** + * next_gate_access_type = w_3_omega; + * next_gate_access_type *= eta; + * next_gate_access_type += w_2_omega; + * next_gate_access_type *= eta; + * next_gate_access_type += w_1_omega; + * next_gate_access_type *= eta; + * next_gate_access_type = w_4_omega - next_gate_access_type; + */ + let next_gate_access_type := mulmod(mload(W3_OMEGA_EVAL_LOC), mload(C_ETA_LOC), p) + next_gate_access_type := addmod(next_gate_access_type, mload(W2_OMEGA_EVAL_LOC), p) + next_gate_access_type := mulmod(next_gate_access_type, mload(C_ETA_LOC), p) + next_gate_access_type := addmod(next_gate_access_type, mload(W1_OMEGA_EVAL_LOC), p) + next_gate_access_type := mulmod(next_gate_access_type, mload(C_ETA_LOC), p) + next_gate_access_type := addmod(mload(W4_OMEGA_EVAL_LOC), sub(p, next_gate_access_type), p) + + // value_delta = w_3_omega - w_3 + let value_delta := addmod(mload(W3_OMEGA_EVAL_LOC), sub(p, mload(W3_EVAL_LOC)), p) + // adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = (1 - index_delta) * value_delta * (1 - next_gate_access_type); + + let adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation := + mulmod( + addmod(1, sub(p, index_delta), p), + mulmod(value_delta, addmod(1, sub(p, next_gate_access_type), p), p), + p + ) + + // AUX_RAM_CONSISTENCY_EVALUATION + + /** + * access_type = w_4 - partial_record_check + * access_check = access_type^2 - access_type + * next_gate_access_type_is_boolean = next_gate_access_type^2 - next_gate_access_type + * RAM_consistency_check_identity = adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation; + * RAM_consistency_check_identity *= alpha; + * RAM_consistency_check_identity += index_is_monotonically_increasing; + * RAM_consistency_check_identity *= alpha; + * RAM_consistency_check_identity += next_gate_access_type_is_boolean; + * RAM_consistency_check_identity *= alpha; + * RAM_consistency_check_identity += access_check; + */ + + let access_type := addmod(mload(W4_EVAL_LOC), sub(p, partial_record_check), p) + let access_check := mulmod(access_type, addmod(access_type, sub(p, 1), p), p) + let next_gate_access_type_is_boolean := + mulmod(next_gate_access_type, addmod(next_gate_access_type, sub(p, 1), p), p) + let RAM_cci := + mulmod( + adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation, + mload(C_ALPHA_LOC), + p + ) + RAM_cci := addmod(RAM_cci, index_is_monotonically_increasing, p) + RAM_cci := mulmod(RAM_cci, mload(C_ALPHA_LOC), p) + RAM_cci := addmod(RAM_cci, next_gate_access_type_is_boolean, p) + RAM_cci := mulmod(RAM_cci, mload(C_ALPHA_LOC), p) + RAM_cci := addmod(RAM_cci, access_check, p) + + mstore(AUX_RAM_CONSISTENCY_EVALUATION, RAM_cci) + } + + { + // timestamp_delta = w_2_omega - w_2 + let timestamp_delta := addmod(mload(W2_OMEGA_EVAL_LOC), sub(p, mload(W2_EVAL_LOC)), p) + + // RAM_timestamp_check_identity = (1 - index_delta) * timestamp_delta - w_3 + let RAM_timestamp_check_identity := + addmod( + mulmod(timestamp_delta, addmod(1, sub(p, index_delta), p), p), sub(p, mload(W3_EVAL_LOC)), p + ) + + /** + * memory_identity = ROM_consistency_check_identity * q_2; + * memory_identity += RAM_timestamp_check_identity * q_4; + * memory_identity += memory_record_check * q_m; + * memory_identity *= q_1; + * memory_identity += (RAM_consistency_check_identity * q_arith); + * + * auxiliary_identity = memory_identity + non_native_field_identity + limb_accumulator_identity; + * auxiliary_identity *= q_aux; + * auxiliary_identity *= alpha_base; + */ + let memory_identity := mulmod(mload(AUX_ROM_CONSISTENCY_EVALUATION), mload(Q2_EVAL_LOC), p) + memory_identity := + addmod(memory_identity, mulmod(RAM_timestamp_check_identity, mload(Q4_EVAL_LOC), p), p) + memory_identity := + addmod(memory_identity, mulmod(mload(AUX_MEMORY_EVALUATION), mload(QM_EVAL_LOC), p), p) + memory_identity := mulmod(memory_identity, mload(Q1_EVAL_LOC), p) + memory_identity := + addmod( + memory_identity, mulmod(mload(AUX_RAM_CONSISTENCY_EVALUATION), mload(QARITH_EVAL_LOC), p), p + ) + + let auxiliary_identity := addmod(memory_identity, mload(AUX_NON_NATIVE_FIELD_EVALUATION), p) + auxiliary_identity := addmod(auxiliary_identity, mload(AUX_LIMB_ACCUMULATOR_EVALUATION), p) + auxiliary_identity := mulmod(auxiliary_identity, mload(QAUX_EVAL_LOC), p) + auxiliary_identity := mulmod(auxiliary_identity, mload(C_ALPHA_BASE_LOC), p) + + mstore(AUX_IDENTITY, auxiliary_identity) + + // update alpha + mstore(C_ALPHA_BASE_LOC, mulmod(mload(C_ALPHA_BASE_LOC), mload(C_ALPHA_CUBE_LOC), p)) + } + } + } + + { + /** + * quotient = ARITHMETIC_IDENTITY + * quotient += PERMUTATION_IDENTITY + * quotient += PLOOKUP_IDENTITY + * quotient += SORT_IDENTITY + * quotient += ELLIPTIC_IDENTITY + * quotient += AUX_IDENTITY + * quotient *= ZERO_POLY_INVERSE + */ + mstore( + QUOTIENT_EVAL_LOC, + mulmod( + addmod( + addmod( + addmod( + addmod( + addmod(mload(PERMUTATION_IDENTITY), mload(PLOOKUP_IDENTITY), p), + mload(ARITHMETIC_IDENTITY), + p + ), + mload(SORT_IDENTITY), + p + ), + mload(ELLIPTIC_IDENTITY), + p + ), + mload(AUX_IDENTITY), + p + ), + mload(ZERO_POLY_INVERSE_LOC), + p + ) + ) + } + + /** + * GENERATE NU AND SEPARATOR CHALLENGES + */ + { + let current_challenge := mload(C_CURRENT_LOC) + // get a calldata pointer that points to the start of the data we want to copy + let calldata_ptr := add(calldataload(0x04), 0x24) + + calldata_ptr := add(calldata_ptr, NU_CALLDATA_SKIP_LENGTH) + + mstore(NU_CHALLENGE_INPUT_LOC_A, current_challenge) + mstore(NU_CHALLENGE_INPUT_LOC_B, mload(QUOTIENT_EVAL_LOC)) + calldatacopy(NU_CHALLENGE_INPUT_LOC_C, calldata_ptr, NU_INPUT_LENGTH) + + // hash length = (0x20 + num field elements), we include the previous challenge in the hash + let challenge := keccak256(NU_CHALLENGE_INPUT_LOC_A, add(NU_INPUT_LENGTH, 0x40)) + + mstore(C_V0_LOC, mod(challenge, p)) + // We need THIRTY-ONE independent nu challenges! + mstore(0x00, challenge) + mstore8(0x20, 0x01) + mstore(C_V1_LOC, mod(keccak256(0x00, 0x21), p)) + mstore8(0x20, 0x02) + mstore(C_V2_LOC, mod(keccak256(0x00, 0x21), p)) + mstore8(0x20, 0x03) + mstore(C_V3_LOC, mod(keccak256(0x00, 0x21), p)) + mstore8(0x20, 0x04) + mstore(C_V4_LOC, mod(keccak256(0x00, 0x21), p)) + mstore8(0x20, 0x05) + mstore(C_V5_LOC, mod(keccak256(0x00, 0x21), p)) + mstore8(0x20, 0x06) + mstore(C_V6_LOC, mod(keccak256(0x00, 0x21), p)) + mstore8(0x20, 0x07) + mstore(C_V7_LOC, mod(keccak256(0x00, 0x21), p)) + mstore8(0x20, 0x08) + mstore(C_V8_LOC, mod(keccak256(0x00, 0x21), p)) + mstore8(0x20, 0x09) + mstore(C_V9_LOC, mod(keccak256(0x00, 0x21), p)) + mstore8(0x20, 0x0a) + mstore(C_V10_LOC, mod(keccak256(0x00, 0x21), p)) + mstore8(0x20, 0x0b) + mstore(C_V11_LOC, mod(keccak256(0x00, 0x21), p)) + mstore8(0x20, 0x0c) + mstore(C_V12_LOC, mod(keccak256(0x00, 0x21), p)) + mstore8(0x20, 0x0d) + mstore(C_V13_LOC, mod(keccak256(0x00, 0x21), p)) + mstore8(0x20, 0x0e) + mstore(C_V14_LOC, mod(keccak256(0x00, 0x21), p)) + mstore8(0x20, 0x0f) + mstore(C_V15_LOC, mod(keccak256(0x00, 0x21), p)) + mstore8(0x20, 0x10) + mstore(C_V16_LOC, mod(keccak256(0x00, 0x21), p)) + mstore8(0x20, 0x11) + mstore(C_V17_LOC, mod(keccak256(0x00, 0x21), p)) + mstore8(0x20, 0x12) + mstore(C_V18_LOC, mod(keccak256(0x00, 0x21), p)) + mstore8(0x20, 0x13) + mstore(C_V19_LOC, mod(keccak256(0x00, 0x21), p)) + mstore8(0x20, 0x14) + mstore(C_V20_LOC, mod(keccak256(0x00, 0x21), p)) + mstore8(0x20, 0x15) + mstore(C_V21_LOC, mod(keccak256(0x00, 0x21), p)) + mstore8(0x20, 0x16) + mstore(C_V22_LOC, mod(keccak256(0x00, 0x21), p)) + mstore8(0x20, 0x17) + mstore(C_V23_LOC, mod(keccak256(0x00, 0x21), p)) + mstore8(0x20, 0x18) + mstore(C_V24_LOC, mod(keccak256(0x00, 0x21), p)) + mstore8(0x20, 0x19) + mstore(C_V25_LOC, mod(keccak256(0x00, 0x21), p)) + mstore8(0x20, 0x1a) + mstore(C_V26_LOC, mod(keccak256(0x00, 0x21), p)) + mstore8(0x20, 0x1b) + mstore(C_V27_LOC, mod(keccak256(0x00, 0x21), p)) + mstore8(0x20, 0x1c) + mstore(C_V28_LOC, mod(keccak256(0x00, 0x21), p)) + mstore8(0x20, 0x1d) + mstore(C_V29_LOC, mod(keccak256(0x00, 0x21), p)) + + // @follow-up - Why are both v29 and v30 using appending 0x1d to the prior challenge and hashing, should it not change? + mstore8(0x20, 0x1d) + challenge := keccak256(0x00, 0x21) + mstore(C_V30_LOC, mod(challenge, p)) + + // separator + mstore(0x00, challenge) + mstore(0x20, mload(PI_Z_Y_LOC)) + mstore(0x40, mload(PI_Z_X_LOC)) + mstore(0x60, mload(PI_Z_OMEGA_Y_LOC)) + mstore(0x80, mload(PI_Z_OMEGA_X_LOC)) + + mstore(C_U_LOC, mod(keccak256(0x00, 0xa0), p)) + } + + let success := 0 + // VALIDATE T1 + { + let x := mload(T1_X_LOC) + let y := mload(T1_Y_LOC) + let xx := mulmod(x, x, q) + // validate on curve + success := eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)) + mstore(ACCUMULATOR_X_LOC, x) + mstore(add(ACCUMULATOR_X_LOC, 0x20), y) + } + // VALIDATE T2 + { + let x := mload(T2_X_LOC) // 0x1400 + let y := mload(T2_Y_LOC) // 0x1420 + let xx := mulmod(x, x, q) + // validate on curve + success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) + mstore(0x00, x) + mstore(0x20, y) + } + mstore(0x40, mload(ZETA_POW_N_LOC)) + // accumulator_2 = [T2].zeta^n + success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) + // accumulator = [T1] + accumulator_2 + success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) + + // VALIDATE T3 + { + let x := mload(T3_X_LOC) + let y := mload(T3_Y_LOC) + let xx := mulmod(x, x, q) + // validate on curve + success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) + mstore(0x00, x) + mstore(0x20, y) + } + mstore(0x40, mulmod(mload(ZETA_POW_N_LOC), mload(ZETA_POW_N_LOC), p)) + // accumulator_2 = [T3].zeta^{2n} + success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) + // accumulator = accumulator + accumulator_2 + success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) + + // VALIDATE T4 + { + let x := mload(T4_X_LOC) + let y := mload(T4_Y_LOC) + let xx := mulmod(x, x, q) + // validate on curve + success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) + mstore(0x00, x) + mstore(0x20, y) + } + mstore(0x40, mulmod(mulmod(mload(ZETA_POW_N_LOC), mload(ZETA_POW_N_LOC), p), mload(ZETA_POW_N_LOC), p)) + // accumulator_2 = [T4].zeta^{3n} + success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) + // accumulator = accumulator + accumulator_2 + success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) + + // VALIDATE W1 + { + let x := mload(W1_X_LOC) + let y := mload(W1_Y_LOC) + let xx := mulmod(x, x, q) + // validate on curve + success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) + mstore(0x00, x) + mstore(0x20, y) + } + mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V0_LOC), p)) + // accumulator_2 = v0.(u + 1).[W1] + success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) + // accumulator = accumulator + accumulator_2 + success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) + + // VALIDATE W2 + { + let x := mload(W2_X_LOC) + let y := mload(W2_Y_LOC) + let xx := mulmod(x, x, q) + // validate on curve + success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) + mstore(0x00, x) + mstore(0x20, y) + } + mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V1_LOC), p)) + // accumulator_2 = v1.(u + 1).[W2] + success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) + // accumulator = accumulator + accumulator_2 + success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) + + // VALIDATE W3 + { + let x := mload(W3_X_LOC) + let y := mload(W3_Y_LOC) + let xx := mulmod(x, x, q) + // validate on curve + success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) + mstore(0x00, x) + mstore(0x20, y) + } + mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V2_LOC), p)) + // accumulator_2 = v2.(u + 1).[W3] + success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) + // accumulator = accumulator + accumulator_2 + success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) + + // VALIDATE W4 + { + let x := mload(W4_X_LOC) + let y := mload(W4_Y_LOC) + let xx := mulmod(x, x, q) + // validate on curve + success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) + mstore(0x00, x) + mstore(0x20, y) + } + mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V3_LOC), p)) + // accumulator_2 = v3.(u + 1).[W4] + success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) + // accumulator = accumulator + accumulator_2 + success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) + + // VALIDATE S + { + let x := mload(S_X_LOC) + let y := mload(S_Y_LOC) + let xx := mulmod(x, x, q) + // validate on curve + success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) + mstore(0x00, x) + mstore(0x20, y) + } + mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V4_LOC), p)) + // accumulator_2 = v4.(u + 1).[S] + success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) + // accumulator = accumulator + accumulator_2 + success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) + + // VALIDATE Z + { + let x := mload(Z_X_LOC) + let y := mload(Z_Y_LOC) + let xx := mulmod(x, x, q) + // validate on curve + success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) + mstore(0x00, x) + mstore(0x20, y) + } + mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V5_LOC), p)) + // accumulator_2 = v5.(u + 1).[Z] + success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) + // accumulator = accumulator + accumulator_2 + success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) + + // VALIDATE Z_LOOKUP + { + let x := mload(Z_LOOKUP_X_LOC) + let y := mload(Z_LOOKUP_Y_LOC) + let xx := mulmod(x, x, q) + // validate on curve + success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) + mstore(0x00, x) + mstore(0x20, y) + } + mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V6_LOC), p)) + // accumulator_2 = v6.(u + 1).[Z_LOOKUP] + success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) + // accumulator = accumulator + accumulator_2 + success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) + + // VALIDATE Q1 + { + let x := mload(Q1_X_LOC) + let y := mload(Q1_Y_LOC) + let xx := mulmod(x, x, q) + // validate on curve + success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) + mstore(0x00, x) + mstore(0x20, y) + } + mstore(0x40, mload(C_V7_LOC)) + // accumulator_2 = v7.[Q1] + success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) + // accumulator = accumulator + accumulator_2 + success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) + + // VALIDATE Q2 + { + let x := mload(Q2_X_LOC) + let y := mload(Q2_Y_LOC) + let xx := mulmod(x, x, q) + // validate on curve + success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) + mstore(0x00, x) + mstore(0x20, y) + } + mstore(0x40, mload(C_V8_LOC)) + // accumulator_2 = v8.[Q2] + success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) + // accumulator = accumulator + accumulator_2 + success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) + + // VALIDATE Q3 + { + let x := mload(Q3_X_LOC) + let y := mload(Q3_Y_LOC) + let xx := mulmod(x, x, q) + // validate on curve + success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) + mstore(0x00, x) + mstore(0x20, y) + } + mstore(0x40, mload(C_V9_LOC)) + // accumulator_2 = v9.[Q3] + success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) + // accumulator = accumulator + accumulator_2 + success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) + + // VALIDATE Q4 + { + let x := mload(Q4_X_LOC) + let y := mload(Q4_Y_LOC) + let xx := mulmod(x, x, q) + // validate on curve + success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) + mstore(0x00, x) + mstore(0x20, y) + } + mstore(0x40, mload(C_V10_LOC)) + // accumulator_2 = v10.[Q4] + success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) + // accumulator = accumulator + accumulator_2 + success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) + + // VALIDATE QM + { + let x := mload(QM_X_LOC) + let y := mload(QM_Y_LOC) + let xx := mulmod(x, x, q) + // validate on curve + success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) + mstore(0x00, x) + mstore(0x20, y) + } + mstore(0x40, mload(C_V11_LOC)) + // accumulator_2 = v11.[Q;] + success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) + // accumulator = accumulator + accumulator_2 + success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) + + // VALIDATE QC + { + let x := mload(QC_X_LOC) + let y := mload(QC_Y_LOC) + let xx := mulmod(x, x, q) + // validate on curve + success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) + mstore(0x00, x) + mstore(0x20, y) + } + mstore(0x40, mload(C_V12_LOC)) + // accumulator_2 = v12.[QC] + success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) + // accumulator = accumulator + accumulator_2 + success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) + + // VALIDATE QARITH + { + let x := mload(QARITH_X_LOC) + let y := mload(QARITH_Y_LOC) + let xx := mulmod(x, x, q) + // validate on curve + success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) + mstore(0x00, x) + mstore(0x20, y) + } + mstore(0x40, mload(C_V13_LOC)) + // accumulator_2 = v13.[QARITH] + success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) + // accumulator = accumulator + accumulator_2 + success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) + + // VALIDATE QSORT + { + let x := mload(QSORT_X_LOC) + let y := mload(QSORT_Y_LOC) + let xx := mulmod(x, x, q) + // validate on curve + success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) + mstore(0x00, x) + mstore(0x20, y) + } + mstore(0x40, mload(C_V14_LOC)) + // accumulator_2 = v14.[QSORT] + success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) + // accumulator = accumulator + accumulator_2 + success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) + + // VALIDATE QELLIPTIC + { + let x := mload(QELLIPTIC_X_LOC) + let y := mload(QELLIPTIC_Y_LOC) + let xx := mulmod(x, x, q) + // validate on curve + success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) + mstore(0x00, x) + mstore(0x20, y) + } + mstore(0x40, mload(C_V15_LOC)) + // accumulator_2 = v15.[QELLIPTIC] + success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) + // accumulator = accumulator + accumulator_2 + success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) + + // VALIDATE QAUX + { + let x := mload(QAUX_X_LOC) + let y := mload(QAUX_Y_LOC) + let xx := mulmod(x, x, q) + // validate on curve + success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) + mstore(0x00, x) + mstore(0x20, y) + } + mstore(0x40, mload(C_V16_LOC)) + // accumulator_2 = v15.[Q_AUX] + success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) + // accumulator = accumulator + accumulator_2 + success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) + + // VALIDATE SIGMA1 + { + let x := mload(SIGMA1_X_LOC) + let y := mload(SIGMA1_Y_LOC) + let xx := mulmod(x, x, q) + // validate on curve + success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) + mstore(0x00, x) + mstore(0x20, y) + } + mstore(0x40, mload(C_V17_LOC)) + // accumulator_2 = v17.[sigma1] + success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) + // accumulator = accumulator + accumulator_2 + success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) + + // VALIDATE SIGMA2 + { + let x := mload(SIGMA2_X_LOC) + let y := mload(SIGMA2_Y_LOC) + let xx := mulmod(x, x, q) + // validate on curve + success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) + mstore(0x00, x) + mstore(0x20, y) + } + mstore(0x40, mload(C_V18_LOC)) + // accumulator_2 = v18.[sigma2] + success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) + // accumulator = accumulator + accumulator_2 + success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) + + // VALIDATE SIGMA3 + { + let x := mload(SIGMA3_X_LOC) + let y := mload(SIGMA3_Y_LOC) + let xx := mulmod(x, x, q) + // validate on curve + success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) + mstore(0x00, x) + mstore(0x20, y) + } + mstore(0x40, mload(C_V19_LOC)) + // accumulator_2 = v19.[sigma3] + success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) + // accumulator = accumulator + accumulator_2 + success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) + + // VALIDATE SIGMA4 + { + let x := mload(SIGMA4_X_LOC) + let y := mload(SIGMA4_Y_LOC) + let xx := mulmod(x, x, q) + // validate on curve + success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) + mstore(0x00, x) + mstore(0x20, y) + } + mstore(0x40, mload(C_V20_LOC)) + // accumulator_2 = v20.[sigma4] + success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) + // accumulator = accumulator + accumulator_2 + success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) + + // VALIDATE TABLE1 + { + let x := mload(TABLE1_X_LOC) + let y := mload(TABLE1_Y_LOC) + let xx := mulmod(x, x, q) + // validate on curve + success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) + mstore(0x00, x) + mstore(0x20, y) + } + mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V21_LOC), p)) + // accumulator_2 = u.[table1] + success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) + // accumulator = accumulator + accumulator_2 + success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) + + // VALIDATE TABLE2 + { + let x := mload(TABLE2_X_LOC) + let y := mload(TABLE2_Y_LOC) + let xx := mulmod(x, x, q) + // validate on curve + success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) + mstore(0x00, x) + mstore(0x20, y) + } + mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V22_LOC), p)) + // accumulator_2 = u.[table2] + success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) + // accumulator = accumulator + accumulator_2 + success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) + + // VALIDATE TABLE3 + { + let x := mload(TABLE3_X_LOC) + let y := mload(TABLE3_Y_LOC) + let xx := mulmod(x, x, q) + // validate on curve + success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) + mstore(0x00, x) + mstore(0x20, y) + } + mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V23_LOC), p)) + // accumulator_2 = u.[table3] + success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) + // accumulator = accumulator + accumulator_2 + success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) + + // VALIDATE TABLE4 + { + let x := mload(TABLE4_X_LOC) + let y := mload(TABLE4_Y_LOC) + let xx := mulmod(x, x, q) + // validate on curve + success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) + mstore(0x00, x) + mstore(0x20, y) + } + mstore(0x40, mulmod(addmod(mload(C_U_LOC), 0x1, p), mload(C_V24_LOC), p)) + // accumulator_2 = u.[table4] + success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) + // accumulator = accumulator + accumulator_2 + success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) + + // VALIDATE TABLE_TYPE + { + let x := mload(TABLE_TYPE_X_LOC) + let y := mload(TABLE_TYPE_Y_LOC) + let xx := mulmod(x, x, q) + // validate on curve + success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) + mstore(0x00, x) + mstore(0x20, y) + } + mstore(0x40, mload(C_V25_LOC)) + // accumulator_2 = v25.[TableType] + success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) + // accumulator = accumulator + accumulator_2 + success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) + + // VALIDATE ID1 + { + let x := mload(ID1_X_LOC) + let y := mload(ID1_Y_LOC) + let xx := mulmod(x, x, q) + // validate on curve + success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) + mstore(0x00, x) + mstore(0x20, y) + } + mstore(0x40, mload(C_V26_LOC)) + // accumulator_2 = v26.[ID1] + success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) + // accumulator = accumulator + accumulator_2 + success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) + + // VALIDATE ID2 + { + let x := mload(ID2_X_LOC) + let y := mload(ID2_Y_LOC) + let xx := mulmod(x, x, q) + // validate on curve + success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) + mstore(0x00, x) + mstore(0x20, y) + } + mstore(0x40, mload(C_V27_LOC)) + // accumulator_2 = v27.[ID2] + success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) + // accumulator = accumulator + accumulator_2 + success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) + + // VALIDATE ID3 + { + let x := mload(ID3_X_LOC) + let y := mload(ID3_Y_LOC) + let xx := mulmod(x, x, q) + // validate on curve + success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) + mstore(0x00, x) + mstore(0x20, y) + } + mstore(0x40, mload(C_V28_LOC)) + // accumulator_2 = v28.[ID3] + success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) + // accumulator = accumulator + accumulator_2 + success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) + + // VALIDATE ID4 + { + let x := mload(ID4_X_LOC) + let y := mload(ID4_Y_LOC) + let xx := mulmod(x, x, q) + // validate on curve + success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) + mstore(0x00, x) + mstore(0x20, y) + } + mstore(0x40, mload(C_V29_LOC)) + // accumulator_2 = v29.[ID4] + success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) + // accumulator = accumulator + accumulator_2 + success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) + + /** + * COMPUTE BATCH EVALUATION SCALAR MULTIPLIER + */ + { + /** + * batch_evaluation = v0 * (w_1_omega * u + w_1_eval) + * batch_evaluation += v1 * (w_2_omega * u + w_2_eval) + * batch_evaluation += v2 * (w_3_omega * u + w_3_eval) + * batch_evaluation += v3 * (w_4_omega * u + w_4_eval) + * batch_evaluation += v4 * (s_omega_eval * u + s_eval) + * batch_evaluation += v5 * (z_omega_eval * u + z_eval) + * batch_evaluation += v6 * (z_lookup_omega_eval * u + z_lookup_eval) + */ + let batch_evaluation := + mulmod( + mload(C_V0_LOC), + addmod(mulmod(mload(W1_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(W1_EVAL_LOC), p), + p + ) + batch_evaluation := + addmod( + batch_evaluation, + mulmod( + mload(C_V1_LOC), + addmod(mulmod(mload(W2_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(W2_EVAL_LOC), p), + p + ), + p + ) + batch_evaluation := + addmod( + batch_evaluation, + mulmod( + mload(C_V2_LOC), + addmod(mulmod(mload(W3_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(W3_EVAL_LOC), p), + p + ), + p + ) + batch_evaluation := + addmod( + batch_evaluation, + mulmod( + mload(C_V3_LOC), + addmod(mulmod(mload(W4_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(W4_EVAL_LOC), p), + p + ), + p + ) + batch_evaluation := + addmod( + batch_evaluation, + mulmod( + mload(C_V4_LOC), + addmod(mulmod(mload(S_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(S_EVAL_LOC), p), + p + ), + p + ) + batch_evaluation := + addmod( + batch_evaluation, + mulmod( + mload(C_V5_LOC), + addmod(mulmod(mload(Z_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(Z_EVAL_LOC), p), + p + ), + p + ) + batch_evaluation := + addmod( + batch_evaluation, + mulmod( + mload(C_V6_LOC), + addmod(mulmod(mload(Z_LOOKUP_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(Z_LOOKUP_EVAL_LOC), p), + p + ), + p + ) + + /** + * batch_evaluation += v7 * Q1_EVAL + * batch_evaluation += v8 * Q2_EVAL + * batch_evaluation += v9 * Q3_EVAL + * batch_evaluation += v10 * Q4_EVAL + * batch_evaluation += v11 * QM_EVAL + * batch_evaluation += v12 * QC_EVAL + * batch_evaluation += v13 * QARITH_EVAL + * batch_evaluation += v14 * QSORT_EVAL_LOC + * batch_evaluation += v15 * QELLIPTIC_EVAL_LOC + * batch_evaluation += v16 * QAUX_EVAL_LOC + * batch_evaluation += v17 * SIGMA1_EVAL_LOC + * batch_evaluation += v18 * SIGMA2_EVAL_LOC + * batch_evaluation += v19 * SIGMA3_EVAL_LOC + * batch_evaluation += v20 * SIGMA4_EVAL_LOC + */ + batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V7_LOC), mload(Q1_EVAL_LOC), p), p) + batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V8_LOC), mload(Q2_EVAL_LOC), p), p) + batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V9_LOC), mload(Q3_EVAL_LOC), p), p) + batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V10_LOC), mload(Q4_EVAL_LOC), p), p) + batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V11_LOC), mload(QM_EVAL_LOC), p), p) + batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V12_LOC), mload(QC_EVAL_LOC), p), p) + batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V13_LOC), mload(QARITH_EVAL_LOC), p), p) + batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V14_LOC), mload(QSORT_EVAL_LOC), p), p) + batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V15_LOC), mload(QELLIPTIC_EVAL_LOC), p), p) + batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V16_LOC), mload(QAUX_EVAL_LOC), p), p) + batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V17_LOC), mload(SIGMA1_EVAL_LOC), p), p) + batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V18_LOC), mload(SIGMA2_EVAL_LOC), p), p) + batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V19_LOC), mload(SIGMA3_EVAL_LOC), p), p) + batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V20_LOC), mload(SIGMA4_EVAL_LOC), p), p) + + /** + * batch_evaluation += v21 * (table1(zw) * u + table1(z)) + * batch_evaluation += v22 * (table2(zw) * u + table2(z)) + * batch_evaluation += v23 * (table3(zw) * u + table3(z)) + * batch_evaluation += v24 * (table4(zw) * u + table4(z)) + * batch_evaluation += v25 * table_type_eval + * batch_evaluation += v26 * id1_eval + * batch_evaluation += v27 * id2_eval + * batch_evaluation += v28 * id3_eval + * batch_evaluation += v29 * id4_eval + * batch_evaluation += quotient_eval + */ + batch_evaluation := + addmod( + batch_evaluation, + mulmod( + mload(C_V21_LOC), + addmod(mulmod(mload(TABLE1_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(TABLE1_EVAL_LOC), p), + p + ), + p + ) + batch_evaluation := + addmod( + batch_evaluation, + mulmod( + mload(C_V22_LOC), + addmod(mulmod(mload(TABLE2_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(TABLE2_EVAL_LOC), p), + p + ), + p + ) + batch_evaluation := + addmod( + batch_evaluation, + mulmod( + mload(C_V23_LOC), + addmod(mulmod(mload(TABLE3_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(TABLE3_EVAL_LOC), p), + p + ), + p + ) + batch_evaluation := + addmod( + batch_evaluation, + mulmod( + mload(C_V24_LOC), + addmod(mulmod(mload(TABLE4_OMEGA_EVAL_LOC), mload(C_U_LOC), p), mload(TABLE4_EVAL_LOC), p), + p + ), + p + ) + batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V25_LOC), mload(TABLE_TYPE_EVAL_LOC), p), p) + batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V26_LOC), mload(ID1_EVAL_LOC), p), p) + batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V27_LOC), mload(ID2_EVAL_LOC), p), p) + batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V28_LOC), mload(ID3_EVAL_LOC), p), p) + batch_evaluation := addmod(batch_evaluation, mulmod(mload(C_V29_LOC), mload(ID4_EVAL_LOC), p), p) + batch_evaluation := addmod(batch_evaluation, mload(QUOTIENT_EVAL_LOC), p) + + mstore(0x00, 0x01) // [1].x + mstore(0x20, 0x02) // [1].y + mstore(0x40, sub(p, batch_evaluation)) + // accumulator_2 = -[1].(batch_evaluation) + success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) + // accumulator = accumulator + accumulator_2 + success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) + + mstore(OPENING_COMMITMENT_SUCCESS_FLAG, success) + } + + /** + * PERFORM PAIRING PREAMBLE + */ + { + let u := mload(C_U_LOC) + let zeta := mload(C_ZETA_LOC) + // VALIDATE PI_Z + { + let x := mload(PI_Z_X_LOC) + let y := mload(PI_Z_Y_LOC) + let xx := mulmod(x, x, q) + // validate on curve + success := eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)) + mstore(0x00, x) + mstore(0x20, y) + } + // compute zeta.[PI_Z] and add into accumulator + mstore(0x40, zeta) + success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) + // accumulator = accumulator + accumulator_2 + success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, ACCUMULATOR_X_LOC, 0x40)) + + // VALIDATE PI_Z_OMEGA + { + let x := mload(PI_Z_OMEGA_X_LOC) + let y := mload(PI_Z_OMEGA_Y_LOC) + let xx := mulmod(x, x, q) + // validate on curve + success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) + mstore(0x00, x) + mstore(0x20, y) + } + mstore(0x40, mulmod(mulmod(u, zeta, p), mload(OMEGA_LOC), p)) + // accumulator_2 = u.zeta.omega.[PI_Z_OMEGA] + success := and(success, staticcall(gas(), 7, 0x00, 0x60, ACCUMULATOR2_X_LOC, 0x40)) + // PAIRING_RHS = accumulator + accumulator_2 + success := and(success, staticcall(gas(), 6, ACCUMULATOR_X_LOC, 0x80, PAIRING_RHS_X_LOC, 0x40)) + + mstore(0x00, mload(PI_Z_X_LOC)) + mstore(0x20, mload(PI_Z_Y_LOC)) + mstore(0x40, mload(PI_Z_OMEGA_X_LOC)) + mstore(0x60, mload(PI_Z_OMEGA_Y_LOC)) + mstore(0x80, u) + success := and(success, staticcall(gas(), 7, 0x40, 0x60, 0x40, 0x40)) + // PAIRING_LHS = [PI_Z] + [PI_Z_OMEGA] * u + success := and(success, staticcall(gas(), 6, 0x00, 0x80, PAIRING_LHS_X_LOC, 0x40)) + // negate lhs y-coordinate + mstore(PAIRING_LHS_Y_LOC, sub(q, mload(PAIRING_LHS_Y_LOC))) + + if mload(CONTAINS_RECURSIVE_PROOF_LOC) { + // VALIDATE RECURSIVE P1 + { + let x := mload(RECURSIVE_P1_X_LOC) + let y := mload(RECURSIVE_P1_Y_LOC) + let xx := mulmod(x, x, q) + // validate on curve + success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) + mstore(0x00, x) + mstore(0x20, y) + } + + // compute u.u.[recursive_p1] and write into 0x60 + mstore(0x40, mulmod(u, u, p)) + success := and(success, staticcall(gas(), 7, 0x00, 0x60, 0x60, 0x40)) + // VALIDATE RECURSIVE P2 + { + let x := mload(RECURSIVE_P2_X_LOC) + let y := mload(RECURSIVE_P2_Y_LOC) + let xx := mulmod(x, x, q) + // validate on curve + success := and(success, eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q))) + mstore(0x00, x) + mstore(0x20, y) + } + // compute u.u.[recursive_p2] and write into 0x00 + // 0x40 still contains u*u + success := and(success, staticcall(gas(), 7, 0x00, 0x60, 0x00, 0x40)) + + // compute u.u.[recursiveP1] + rhs and write into rhs + mstore(0xa0, mload(PAIRING_RHS_X_LOC)) + mstore(0xc0, mload(PAIRING_RHS_Y_LOC)) + success := and(success, staticcall(gas(), 6, 0x60, 0x80, PAIRING_RHS_X_LOC, 0x40)) + + // compute u.u.[recursiveP2] + lhs and write into lhs + mstore(0x40, mload(PAIRING_LHS_X_LOC)) + mstore(0x60, mload(PAIRING_LHS_Y_LOC)) + success := and(success, staticcall(gas(), 6, 0x00, 0x80, PAIRING_LHS_X_LOC, 0x40)) + } + + if iszero(success) { + mstore(0x0, EC_SCALAR_MUL_FAILURE_SELECTOR) + revert(0x00, 0x04) + } + mstore(PAIRING_PREAMBLE_SUCCESS_FLAG, success) + } + + /** + * PERFORM PAIRING + */ + { + // rhs paired with [1]_2 + // lhs paired with [x]_2 + + mstore(0x00, mload(PAIRING_RHS_X_LOC)) + mstore(0x20, mload(PAIRING_RHS_Y_LOC)) + mstore(0x40, 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2) // this is [1]_2 + mstore(0x60, 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed) + mstore(0x80, 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b) + mstore(0xa0, 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa) + + mstore(0xc0, mload(PAIRING_LHS_X_LOC)) + mstore(0xe0, mload(PAIRING_LHS_Y_LOC)) + mstore(0x100, mload(G2X_X0_LOC)) + mstore(0x120, mload(G2X_X1_LOC)) + mstore(0x140, mload(G2X_Y0_LOC)) + mstore(0x160, mload(G2X_Y1_LOC)) + + success := staticcall(gas(), 8, 0x00, 0x180, 0x00, 0x20) + mstore(PAIRING_SUCCESS_FLAG, success) + mstore(RESULT_FLAG, mload(0x00)) + } + if iszero( + and( + and(and(mload(PAIRING_SUCCESS_FLAG), mload(RESULT_FLAG)), mload(PAIRING_PREAMBLE_SUCCESS_FLAG)), + mload(OPENING_COMMITMENT_SUCCESS_FLAG) + ) + ) { + mstore(0x0, PROOF_FAILURE_SELECTOR) + revert(0x00, 0x04) + } + { + mstore(0x00, 0x01) + return(0x00, 0x20) // Proof succeeded! + } + } + } +} diff --git a/barretenberg/sol/src/ultra/instance/Add2UltraVerifier.sol b/barretenberg/sol/src/ultra/instance/Add2UltraVerifier.sol new file mode 100644 index 000000000000..1bbaed721670 --- /dev/null +++ b/barretenberg/sol/src/ultra/instance/Add2UltraVerifier.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 Aztec +pragma solidity >=0.8.4; + +import {Add2UltraVerificationKey as VK} from "../keys/Add2UltraVerificationKey.sol"; +import {BaseUltraVerifier as BASE} from "../BaseUltraVerifier.sol"; + +contract Add2UltraVerifier is BASE { + function getVerificationKeyHash() public pure override(BASE) returns (bytes32) { + return VK.verificationKeyHash(); + } + + function loadVerificationKey(uint256 vk, uint256 _omegaInverseLoc) internal pure virtual override(BASE) { + VK.loadVerificationKey(vk, _omegaInverseLoc); + } +} diff --git a/barretenberg/sol/src/ultra/instance/BlakeUltraVerifier.sol b/barretenberg/sol/src/ultra/instance/BlakeUltraVerifier.sol new file mode 100644 index 000000000000..5442c999e16e --- /dev/null +++ b/barretenberg/sol/src/ultra/instance/BlakeUltraVerifier.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 Aztec +pragma solidity >=0.8.4; + +import {BlakeUltraVerificationKey as VK} from "../keys/BlakeUltraVerificationKey.sol"; +import {BaseUltraVerifier as BASE} from "../BaseUltraVerifier.sol"; + +contract BlakeUltraVerifier is BASE { + function getVerificationKeyHash() public pure override(BASE) returns (bytes32) { + return VK.verificationKeyHash(); + } + + function loadVerificationKey(uint256 vk, uint256 _omegaInverseLoc) internal pure virtual override(BASE) { + VK.loadVerificationKey(vk, _omegaInverseLoc); + } +} diff --git a/barretenberg/sol/src/ultra/instance/RecursiveUltraVerifier.sol b/barretenberg/sol/src/ultra/instance/RecursiveUltraVerifier.sol new file mode 100644 index 000000000000..f2147aada62d --- /dev/null +++ b/barretenberg/sol/src/ultra/instance/RecursiveUltraVerifier.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 Aztec +pragma solidity >=0.8.4; + +import {RecursiveUltraVerificationKey as VK} from "../keys/RecursiveUltraVerificationKey.sol"; +import {BaseUltraVerifier as BASE} from "../BaseUltraVerifier.sol"; + +contract RecursiveUltraVerifier is BASE { + function getVerificationKeyHash() public pure override(BASE) returns (bytes32) { + return VK.verificationKeyHash(); + } + + function loadVerificationKey(uint256 vk, uint256 _omegaInverseLoc) internal pure virtual override(BASE) { + VK.loadVerificationKey(vk, _omegaInverseLoc); + } +} diff --git a/barretenberg/sol/src/ultra/keys/Add2UltraVerificationKey.sol b/barretenberg/sol/src/ultra/keys/Add2UltraVerificationKey.sol new file mode 100644 index 000000000000..67c3b6080a15 --- /dev/null +++ b/barretenberg/sol/src/ultra/keys/Add2UltraVerificationKey.sol @@ -0,0 +1,72 @@ +// Verification Key Hash: a0e940165bfc708013d5b4f7940f3b07f3bcf3c0f57ee21d8b4bdb78630817a3 +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2022 Aztec +pragma solidity >=0.8.4; + +library Add2UltraVerificationKey { + function verificationKeyHash() internal pure returns (bytes32) { + return 0xa0e940165bfc708013d5b4f7940f3b07f3bcf3c0f57ee21d8b4bdb78630817a3; + } + + function loadVerificationKey(uint256 _vk, uint256 _omegaInverseLoc) internal pure { + assembly { + mstore(add(_vk, 0x00), 0x0000000000000000000000000000000000000000000000000000000000000010) // vk.circuit_size + mstore(add(_vk, 0x20), 0x0000000000000000000000000000000000000000000000000000000000000003) // vk.num_inputs + mstore(add(_vk, 0x40), 0x21082ca216cbbf4e1c6e4f4594dd508c996dfbe1174efb98b11509c6e306460b) // vk.work_root + mstore(add(_vk, 0x60), 0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001) // vk.domain_inverse + mstore(add(_vk, 0x80), 0x0fe8527a8494f827e4b332060e5569bdfe47aadc475cb1f03b4d222b0804e463) // vk.Q1.x + mstore(add(_vk, 0xa0), 0x2d051f6d8a9eec7ae2622b4b259fb91d942a8892632cffcdad4c03e2c9081593) // vk.Q1.y + mstore(add(_vk, 0xc0), 0x21be04985f898ef5e2a80f32fbaa16cd3eb8c451c243db46144b97113c8e556a) // vk.Q2.x + mstore(add(_vk, 0xe0), 0x2913bcdde62d6d2143aa6a0fed511ca527994df2cba6a780baa29dddac161de9) // vk.Q2.y + mstore(add(_vk, 0x100), 0x15bf0818d578953a624a127aa04d4b0aa1b09d3fe69b9a29e2546a41e9b08049) // vk.Q3.x + mstore(add(_vk, 0x120), 0x0148f0d2abf2ca1fe6cc20fdef0a8d4a88eb9602eaf48f0c1c02445c27cb9592) // vk.Q3.y + mstore(add(_vk, 0x140), 0x02d6fd9e84dbe74b7531e1801405a1c292117b1a17fefe9de0bfd9edf1a84bf9) // vk.Q4.x + mstore(add(_vk, 0x160), 0x293c6ab3c06a0669af13393a82c60a459a3b2a0b768da45ac7af7f2aec40fc42) // vk.Q4.y + mstore(add(_vk, 0x180), 0x2950076760523510abcfe90fa550b964e84b338f73af5222cdbbaefdacd4484e) // vk.Q_M.x + mstore(add(_vk, 0x1a0), 0x2e4e3e272c7b78ad894559812d7766e05615a8f7050a43d7ed1367adf30a9319) // vk.Q_M.y + mstore(add(_vk, 0x1c0), 0x1798c37010a4285e1774c1ad35779886380ee5ceee0ba183927e2a2103301a68) // vk.Q_C.x + mstore(add(_vk, 0x1e0), 0x2935f9e4d47a8e39aa0107f31a84584b47d903cfeb9690f6d850dc8ea7d2f4ea) // vk.Q_C.y + mstore(add(_vk, 0x200), 0x205aff7186f2cedc99e9cadb7930ef25f296991e5a7b574a33d6249f0a5db1f9) // vk.Q_ARITHMETIC.x + mstore(add(_vk, 0x220), 0x0beadc757bee3a2be1f6425385547099fc1eeeb1fed5a8552b07e997251d7c45) // vk.Q_ARITHMETIC.y + mstore(add(_vk, 0x240), 0x2cbce7beee3076b78dace04943d69d0d9e28aa6d00e046852781a5f20816645c) // vk.QSORT.x + mstore(add(_vk, 0x260), 0x2bc27ec2e1612ea284b08bcc55b6f2fd915d11bfedbdc0e59de09e5b28952080) // vk.QSORT.y + mstore(add(_vk, 0x280), 0x0ad34b5e8db72a5acf4427546c7294be6ed4f4d252a79059e505f9abc1bdf3ed) // vk.Q_ELLIPTIC.x + mstore(add(_vk, 0x2a0), 0x1e5b26790a26eb340217dd9ad28dbf90a049f42a3852acd45e6f521f24b4900e) // vk.Q_ELLIPTIC.y + mstore(add(_vk, 0x2c0), 0x155a0f51fec78c33ffceb7364d69d7ac27e570ae50bc180509764eb3fef94815) // vk.Q_AUX.x + mstore(add(_vk, 0x2e0), 0x1c1c4720bed44a591d97cbc72b6e44b644999713a8d3c66e9054aa5726324c76) // vk.Q_AUX.y + mstore(add(_vk, 0x300), 0x01d8b8ff3b1674e57a7d30ce1d9e07c686174c643eb20d38e604eec7095248a9) // vk.SIGMA1.x + mstore(add(_vk, 0x320), 0x261015d69327a58810e6eb1052ed694914b7a89034e1334c50b9e70a161489b7) // vk.SIGMA1.y + mstore(add(_vk, 0x340), 0x1987df111730a8a6a650423757dbf048f3f43860a7d24a5e2e8bd67b6931ca67) // vk.SIGMA2.x + mstore(add(_vk, 0x360), 0x1fe074ff24b35d7830bec2a3fad7e53038ffb5e2a3f718a23660d68daffc2953) // vk.SIGMA2.y + mstore(add(_vk, 0x380), 0x191d6881b9369e50141da3f2009a2f2dfc9adba276e874eb9f7f3183a0ea8f89) // vk.SIGMA3.x + mstore(add(_vk, 0x3a0), 0x2cbfbdd75526be7e9f2830cb1fd99457227f90fb024902635e342344cb6b2926) // vk.SIGMA3.y + mstore(add(_vk, 0x3c0), 0x2e7fe658b8f1f34dc561568e4ef6c74bb36330404967952b1e15390c1e2443cf) // vk.SIGMA4.x + mstore(add(_vk, 0x3e0), 0x293a5337eedc88329f10aa45a11e4adbb77f74717345987d70ed726e3e4556f1) // vk.SIGMA4.y + mstore(add(_vk, 0x400), 0x02c397073c8abce6d4140c9b961209dd783bff1a1cfc999bb29859cfb16c46fc) // vk.TABLE1.x + mstore(add(_vk, 0x420), 0x2b7bba2d1efffce0d033f596b4d030750599be670db593af86e1923fe8a1bb18) // vk.TABLE1.y + mstore(add(_vk, 0x440), 0x2c71c58b66498f903b3bbbda3d05ce8ffb571a4b3cf83533f3f71b99a04f6e6b) // vk.TABLE2.x + mstore(add(_vk, 0x460), 0x039dce37f94d1bbd97ccea32a224fe2afaefbcbd080c84dcea90b54f4e0a858f) // vk.TABLE2.y + mstore(add(_vk, 0x480), 0x27dc44977efe6b3746a290706f4f7275783c73cfe56847d848fd93b63bf32083) // vk.TABLE3.x + mstore(add(_vk, 0x4a0), 0x0a5366266dd7b71a10b356030226a2de0cbf2edc8f085b16d73652b15eced8f5) // vk.TABLE3.y + mstore(add(_vk, 0x4c0), 0x136097d79e1b0ae373255e8760c49900a7588ec4d6809c90bb451005a3de3077) // vk.TABLE4.x + mstore(add(_vk, 0x4e0), 0x13dd7515ccac4095302d204f06f0bff2595d77bdf72e4acdb0b0b43969860d98) // vk.TABLE4.y + mstore(add(_vk, 0x500), 0x16ff3501369121d410b445929239ba057fe211dad1b706e49a3b55920fac20ec) // vk.TABLE_TYPE.x + mstore(add(_vk, 0x520), 0x1e190987ebd9cf480f608b82134a00eb8007673c1ed10b834a695adf0068522a) // vk.TABLE_TYPE.y + mstore(add(_vk, 0x540), 0x0baaf073a203ba4ba0093c827bb5b3f7a55611765f111ecf80c456f631e99f29) // vk.ID1.x + mstore(add(_vk, 0x560), 0x27cd34cafd5735ec970278202765e0f1e9da3494b30b948ebce3414d078cef9e) // vk.ID1.y + mstore(add(_vk, 0x580), 0x0868357b28039385c5a5058b6d358ebb29f26f9890d6cc6401f4921d5884edca) // vk.ID2.x + mstore(add(_vk, 0x5a0), 0x1060afe929554ca473103f5e68193c36fb6e229dde8edf7ec858b12d7e8be485) // vk.ID2.y + mstore(add(_vk, 0x5c0), 0x0b1c02619282755533457230b19b4a15226e07e207744c0857074dcab883af4a) // vk.ID3.x + mstore(add(_vk, 0x5e0), 0x0d928deafed363659688ed4ccdef521f2a0277e4807e6e1cbabca21dde5eb5e1) // vk.ID3.y + mstore(add(_vk, 0x600), 0x2eea648c8732596b1314fe2a4d2f05363f0c994e91cecad25835338edee2294f) // vk.ID4.x + mstore(add(_vk, 0x620), 0x0ab49886c2b94bd0bd3f6ed1dbbe2cb2671d2ae51d31c1210433c3972bb64578) // vk.ID4.y + mstore(add(_vk, 0x640), 0x00) // vk.contains_recursive_proof + mstore(add(_vk, 0x660), 0) // vk.recursive_proof_public_input_indices + mstore(add(_vk, 0x680), 0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1) // vk.g2_x.X.c1 + mstore(add(_vk, 0x6a0), 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0) // vk.g2_x.X.c0 + mstore(add(_vk, 0x6c0), 0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4) // vk.g2_x.Y.c1 + mstore(add(_vk, 0x6e0), 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55) // vk.g2_x.Y.c0 + mstore(_omegaInverseLoc, 0x02e40daf409556c02bfc85eb303402b774954d30aeb0337eb85a71e6373428de) // vk.work_root_inverse + } + } +} diff --git a/barretenberg/sol/src/ultra/keys/BlakeUltraVerificationKey.sol b/barretenberg/sol/src/ultra/keys/BlakeUltraVerificationKey.sol new file mode 100644 index 000000000000..0b8676e4dabc --- /dev/null +++ b/barretenberg/sol/src/ultra/keys/BlakeUltraVerificationKey.sol @@ -0,0 +1,72 @@ +// Verification Key Hash: ab0e7eca8953a659e04b83b3e1eb0525036ab76b5c6c53b090c8e3e568df3912 +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2022 Aztec +pragma solidity >=0.8.4; + +library BlakeUltraVerificationKey { + function verificationKeyHash() internal pure returns (bytes32) { + return 0xab0e7eca8953a659e04b83b3e1eb0525036ab76b5c6c53b090c8e3e568df3912; + } + + function loadVerificationKey(uint256 _vk, uint256 _omegaInverseLoc) internal pure { + assembly { + mstore(add(_vk, 0x00), 0x0000000000000000000000000000000000000000000000000000000000008000) // vk.circuit_size + mstore(add(_vk, 0x20), 0x0000000000000000000000000000000000000000000000000000000000000004) // vk.num_inputs + mstore(add(_vk, 0x40), 0x2d1ba66f5941dc91017171fa69ec2bd0022a2a2d4115a009a93458fd4e26ecfb) // vk.work_root + mstore(add(_vk, 0x60), 0x3063edaa444bddc677fcd515f614555a777997e0a9287d1e62bf6dd004d82001) // vk.domain_inverse + mstore(add(_vk, 0x80), 0x1a80f3623f0778b08837c863a65a1947e56828b26496c5737e25041052f7009c) // vk.Q1.x + mstore(add(_vk, 0xa0), 0x2a3cacc92240779ca4f320c576190d0d6ec1266368357178a9a8260cfa235455) // vk.Q1.y + mstore(add(_vk, 0xc0), 0x2acda97834aca4418d41ed03d801e2dd48a1d0b5c41d76ad369a8f7af50e0661) // vk.Q2.x + mstore(add(_vk, 0xe0), 0x1b69f6a121b1cb789d87cbb3236233b7a4a480ee9bdde5b7bd47e62a227dc5fc) // vk.Q2.y + mstore(add(_vk, 0x100), 0x0ac1dea6a1af1f8971d1341ab89d8a04f441306474df59985c648f96d1de246c) // vk.Q3.x + mstore(add(_vk, 0x120), 0x0d41764f9161b19571da0200f8efa730ac7c2025e09a2decd3e4889261792e8b) // vk.Q3.y + mstore(add(_vk, 0x140), 0x26c4dcb136c91c0acb772bba73db577c1f0f3c09cf4dd7cf4a2bcecf4aa3057a) // vk.Q4.x + mstore(add(_vk, 0x160), 0x0a5072484a9fce7eef8d097a377375142da6f88bb9616d5ca1f2aad4bb092129) // vk.Q4.y + mstore(add(_vk, 0x180), 0x2c294f4909e331bfdaf0b65ac1b7197251217d346f0b990471359d419a632662) // vk.Q_M.x + mstore(add(_vk, 0x1a0), 0x0b3d987ce70e2cff4a2fd93156efd896e0f4f9f671ccb1ab8c3493217b3d9b07) // vk.Q_M.y + mstore(add(_vk, 0x1c0), 0x1dfbace7f5f42d72d8df9c6c40a118a8c05d340796095cdb30d5ae76807ff4c3) // vk.Q_C.x + mstore(add(_vk, 0x1e0), 0x1a8d4a2ec9837b684fa5b688e36f48288c1450591c7e807bbb86ec125d7b3dd4) // vk.Q_C.y + mstore(add(_vk, 0x200), 0x0a51756abf4c062b5f4d76b29e1dbba021b670d13442aee1fef9e3018eb4aca4) // vk.Q_ARITHMETIC.x + mstore(add(_vk, 0x220), 0x0ad4318714e1493aae41463ebd941fd2d7fb5357c55ae9eb6491c7c3ccb8399d) // vk.Q_ARITHMETIC.y + mstore(add(_vk, 0x240), 0x0d7d8284681025c0926d0bfda9a6887098f151a49b27160e76c0cbe5e081fcb8) // vk.QSORT.x + mstore(add(_vk, 0x260), 0x083b2abe0a5c29769ba8f427e7440a3dcb1ffcd86cb1b106f0aa27b6903655ba) // vk.QSORT.y + mstore(add(_vk, 0x280), 0x21959276775cd4749236c8bf773a9b2403cecb45fbf70e6439f73d75442e8850) // vk.Q_ELLIPTIC.x + mstore(add(_vk, 0x2a0), 0x017714509f01d1a9ee7ebaf4d50745e33a14150b4fe9850a27e44de56d88cb14) // vk.Q_ELLIPTIC.y + mstore(add(_vk, 0x2c0), 0x2e76c4474fcb457db84fb273ccc10a4647a1a37444369f2f275bb74540f5e2d0) // vk.Q_AUX.x + mstore(add(_vk, 0x2e0), 0x209035caddd02a78acd0ed617a85d782533bd142c6cad8e3338f3142b919c3a4) // vk.Q_AUX.y + mstore(add(_vk, 0x300), 0x12922936896c92f4773be96aa6eced49c9e6973d091baca38bcd2ce7c2432b13) // vk.SIGMA1.x + mstore(add(_vk, 0x320), 0x0195e866ae7531344bc2cec037b002d99caace033bdbdf98b4db64b1e23be236) // vk.SIGMA1.y + mstore(add(_vk, 0x340), 0x136128a1e6bc7bc733fcb9343fe23760d8d8a08d4ba4cdfc8970429696720a85) // vk.SIGMA2.x + mstore(add(_vk, 0x360), 0x222d19d2afae1cc8c5b1b802be90b5d4d648f5e89f4b32df381fb92135497a2c) // vk.SIGMA2.y + mstore(add(_vk, 0x380), 0x0dea033b8db2e2948559604ffac72f411931477ca58240ce0a3007c9a82d1b0a) // vk.SIGMA3.x + mstore(add(_vk, 0x3a0), 0x1fd05a799d3dfaba1bfed3145b27e4768be0bc0893b24962de96fdbce7cdc319) // vk.SIGMA3.y + mstore(add(_vk, 0x3c0), 0x07f75bc93d92e6dccd6ba833a23aa5d1ba7c36cd2e2d6c2e6406c62480e19119) // vk.SIGMA4.x + mstore(add(_vk, 0x3e0), 0x253f74ab6ef1778beba646f245c7dcd5e60cdaebcc37d2c5db22caf03ee7091f) // vk.SIGMA4.y + mstore(add(_vk, 0x400), 0x06c5d3c2a64587cf9dc278c6892854fc8f1aba4183115224cb2eda4c1aab64b8) // vk.TABLE1.x + mstore(add(_vk, 0x420), 0x132622df9222e04fa9c4cf2895212a49556038d4fdc6d0d7a15b1067bb446efa) // vk.TABLE1.y + mstore(add(_vk, 0x440), 0x2dbc1ac72b2f0c530b3bdbef307395e6059f82ce9f3beea34ff6c3a04ca112bc) // vk.TABLE2.x + mstore(add(_vk, 0x460), 0x23e9676a2c36926b3e10b1102f06aa3a9828d1422ae9e6ea77203025cd18ada0) // vk.TABLE2.y + mstore(add(_vk, 0x480), 0x298b6eb4baf5c75d4542a2089226886cc3ef984af332cae76356af6da70820fe) // vk.TABLE3.x + mstore(add(_vk, 0x4a0), 0x1bb16a4d3b60d47e572e02fac8bf861df5ba5f96942054e0896c7d4d602dc5c7) // vk.TABLE3.y + mstore(add(_vk, 0x4c0), 0x1f5976fc145f0524228ca90c221a21228ff9be92d487b56890a39c3bc0d22bf2) // vk.TABLE4.x + mstore(add(_vk, 0x4e0), 0x0f43d83a0d9eb36476e05c8d1280df98ec46ce93ae238597a687a4937ebec6cc) // vk.TABLE4.y + mstore(add(_vk, 0x500), 0x0b140556df3e8e29980eeae95a724d3c06c830707ce655b6bee64acc9be9e9c2) // vk.TABLE_TYPE.x + mstore(add(_vk, 0x520), 0x04de5162de4a6bea5e1e3b1386d87d41adef6c98203f749c98d2c2113ba12e9b) // vk.TABLE_TYPE.y + mstore(add(_vk, 0x540), 0x2e9d323290bebf84302163b43af960fde161663907a5e344ab62e3d0db9dc2a4) // vk.ID1.x + mstore(add(_vk, 0x560), 0x282cbb41af3a18746f880f35caf350a0e653cc9ff266380f9dad6c8145fc8532) // vk.ID1.y + mstore(add(_vk, 0x580), 0x29c73bc4fb55d00bb6b91bff171aaa98e3ab1d64829603b29a5364e510f692d7) // vk.ID2.x + mstore(add(_vk, 0x5a0), 0x1011dea47912a0daae13bf8f0e4b6df1f706252a27b69d34724cd05a3bbb5542) // vk.ID2.y + mstore(add(_vk, 0x5c0), 0x040261783eb93ad94557cb1c9c798d8468953eb65b00b3e0059fc7af5555ac5f) // vk.ID3.x + mstore(add(_vk, 0x5e0), 0x0e05422e3bc394ae55128d83ed0087f1d18810c951f4d03b2f0eff6d5b2e74a2) // vk.ID3.y + mstore(add(_vk, 0x600), 0x1b21ff32cadfbe3b43171ee3d0b14d1eed1c29e3719320d3e61f0e71c6aaf6d8) // vk.ID4.x + mstore(add(_vk, 0x620), 0x04f57e846a88c4a0254841cf7b6226e878e7a4ea49c34c3732870f1d8c4f6c18) // vk.ID4.y + mstore(add(_vk, 0x640), 0x00) // vk.contains_recursive_proof + mstore(add(_vk, 0x660), 0) // vk.recursive_proof_public_input_indices + mstore(add(_vk, 0x680), 0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1) // vk.g2_x.X.c1 + mstore(add(_vk, 0x6a0), 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0) // vk.g2_x.X.c0 + mstore(add(_vk, 0x6c0), 0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4) // vk.g2_x.Y.c1 + mstore(add(_vk, 0x6e0), 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55) // vk.g2_x.Y.c0 + mstore(_omegaInverseLoc, 0x05d33766e4590b3722701b6f2fa43d0dc3f028424d384e68c92a742fb2dbc0b4) // vk.work_root_inverse + } + } +} diff --git a/barretenberg/sol/src/ultra/keys/RecursiveUltraVerificationKey.sol b/barretenberg/sol/src/ultra/keys/RecursiveUltraVerificationKey.sol new file mode 100644 index 000000000000..4b118cdef605 --- /dev/null +++ b/barretenberg/sol/src/ultra/keys/RecursiveUltraVerificationKey.sol @@ -0,0 +1,72 @@ +// Verification Key Hash: 507de35addf16b79526d713259492d5d1764fdb6ce55ff4ccb03c147b72f381a +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2022 Aztec +pragma solidity >=0.8.4; + +library RecursiveUltraVerificationKey { + function verificationKeyHash() internal pure returns (bytes32) { + return 0x507de35addf16b79526d713259492d5d1764fdb6ce55ff4ccb03c147b72f381a; + } + + function loadVerificationKey(uint256 _vk, uint256 _omegaInverseLoc) internal pure { + assembly { + mstore(add(_vk, 0x00), 0x0000000000000000000000000000000000000000000000000000000000080000) // vk.circuit_size + mstore(add(_vk, 0x20), 0x0000000000000000000000000000000000000000000000000000000000000010) // vk.num_inputs + mstore(add(_vk, 0x40), 0x2260e724844bca5251829353968e4915305258418357473a5c1d597f613f6cbd) // vk.work_root + mstore(add(_vk, 0x60), 0x3064486657634403844b0eac78ca882cfd284341fcb0615a15cfcd17b14d8201) // vk.domain_inverse + mstore(add(_vk, 0x80), 0x18fe72968b540c1dad6c7648fcb3407edfc489d8dcf3fdce314c1f0e72684c43) // vk.Q1.x + mstore(add(_vk, 0xa0), 0x16f49263ee016852edfed2e84bf44c22b31064b9034b62059329b2af2f349c37) // vk.Q1.y + mstore(add(_vk, 0xc0), 0x1c382676d0f8e5691def3a60d533850f573c36aa200ab364c091acc4a7eb094f) // vk.Q2.x + mstore(add(_vk, 0xe0), 0x17c05ca7ea679681a3cf772fabcf2c1a988e39910f1ba8de3d1f68ffb0effda1) // vk.Q2.y + mstore(add(_vk, 0x100), 0x257d75dead2d8cbb2f63b3592a762a2c2dbe0195a533736fd01982370e768676) // vk.Q3.x + mstore(add(_vk, 0x120), 0x258b6d74446f5e532bce6e1a62372a82986eac9801c13a8553f373c30398a47c) // vk.Q3.y + mstore(add(_vk, 0x140), 0x290ff6a808f6abe7508a8c884ea0fc2f819e23a5b6d7c2dd1105da2a3f0637e0) // vk.Q4.x + mstore(add(_vk, 0x160), 0x2e6c3c419be44ed56b61069a06e980360f58830ad52b38bb69de92c456ebf0ca) // vk.Q4.y + mstore(add(_vk, 0x180), 0x282e6e14bbedfc7ef013feb4877ce9098389abfd3ad8899c957be4fdb20d0454) // vk.Q_M.x + mstore(add(_vk, 0x1a0), 0x2483d06975c3965d3f2d205ddeff196b90ca5883878bffc0bd190a357fee947e) // vk.Q_M.y + mstore(add(_vk, 0x1c0), 0x09af8fed71838d47b0052d8e3fdda11f55c62a6f2cb9aab24edd90b5e9640e9c) // vk.Q_C.x + mstore(add(_vk, 0x1e0), 0x2bdf7549fa146188dd750d032d9dec911c5799ca99f72405c4ac49f3f9e3a51a) // vk.Q_C.y + mstore(add(_vk, 0x200), 0x1479a535c87c413301d82c5ae1598b46c03117a57b878416d1143bb48f1df8bf) // vk.Q_ARITHMETIC.x + mstore(add(_vk, 0x220), 0x03203e3c02cc68282d93507d0ad9d56304d5a4b2908233bcb6f8682f8b264532) // vk.Q_ARITHMETIC.y + mstore(add(_vk, 0x240), 0x0cccd1de3f4ef2a2bfffbb7a91f8be2c49e9dc9b565ba4312015a88558f40d20) // vk.QSORT.x + mstore(add(_vk, 0x260), 0x092c5bd4edb996d6c1189a2682f6e93ede4b9aff7f07823605c894f833316718) // vk.QSORT.y + mstore(add(_vk, 0x280), 0x20089848d81ee4e8d7700679e7b5ed017916e2ee28bf76c0e0f4862274637bb8) // vk.Q_ELLIPTIC.x + mstore(add(_vk, 0x2a0), 0x0faae100924d24a70708e49a08ba2ba9df261088bf04e7b4c3f811cc0d8995fe) // vk.Q_ELLIPTIC.y + mstore(add(_vk, 0x2c0), 0x2de71f46452329536fe14dfff808692c405b9ef1ae47c451be8383ded868af5c) // vk.Q_AUX.x + mstore(add(_vk, 0x2e0), 0x0a520e2f877f19cc69aad2396bf741e6864a9f0b657887e80165b794f7612e71) // vk.Q_AUX.y + mstore(add(_vk, 0x300), 0x2779b1b7b8433eeee7333a1372feb4587da74e2c93cc54917e201748ed847204) // vk.SIGMA1.x + mstore(add(_vk, 0x320), 0x2198823f66ad59612f6cb77aff9437388abdbcc4d8f6eac792d8bca7d1b341d9) // vk.SIGMA1.y + mstore(add(_vk, 0x340), 0x1f6732b9d128931b2e32b2cae73b029720cca3cef23fee25363d520ed0ba3f92) // vk.SIGMA2.x + mstore(add(_vk, 0x360), 0x15fb336844e68b08361c10b83e7d6ea0f011958774e58e5f7c43e6606e989ecc) // vk.SIGMA2.y + mstore(add(_vk, 0x380), 0x0984b1b6c723afb4713656abf30b06e2ad04c054dd3acf016a6db1ee7111ca11) // vk.SIGMA3.x + mstore(add(_vk, 0x3a0), 0x03421d01f19c6b91e477648819f57d888b3b23b67599266293bddf91a2636ff1) // vk.SIGMA3.y + mstore(add(_vk, 0x3c0), 0x2f77cda90d366b151b17c5667f10526ab0fe144aecb307e00ede6039365bcfa0) // vk.SIGMA4.x + mstore(add(_vk, 0x3e0), 0x0d1e8f758babcbbf134dfe341c262ee25d0254cba8f5487ad5bddd190f27a9e8) // vk.SIGMA4.y + mstore(add(_vk, 0x400), 0x2f61a890b9f1dff4ef5c8b0eafe9b71c7a23dc4c8a6791d9c01418310f4a7b2e) // vk.TABLE1.x + mstore(add(_vk, 0x420), 0x07c8a51d1881fcdfe1cb7dcefc48a44047c7f5386797d5f8553ce2e12e8daba0) // vk.TABLE1.y + mstore(add(_vk, 0x440), 0x1adf56913dea23b7b14c952933b0b40fc476dc2697a758ec9df73802b0596c2f) // vk.TABLE2.x + mstore(add(_vk, 0x460), 0x212a1759e19285a35a70a245cca6477f89b6f156e4425cf52cfccb4594f59152) // vk.TABLE2.y + mstore(add(_vk, 0x480), 0x1527f8c19085ac209ebddbccae4dd0ca58b078e56fd20d651ce3a3194697b191) // vk.TABLE3.x + mstore(add(_vk, 0x4a0), 0x02247dca9c3cb09318aa6100a2a7c628281c69bc41cfda34aa72c263b69344b4) // vk.TABLE3.y + mstore(add(_vk, 0x4c0), 0x12eea56d2ada3befa5db215ea5ebbd37b5ce95fcd1cf7adb94d5a1784876b4f7) // vk.TABLE4.x + mstore(add(_vk, 0x4e0), 0x190df1146fbdd5cc79e8817ebcd6311e35cf5cc38795cee26371a707d685e05a) // vk.TABLE4.y + mstore(add(_vk, 0x500), 0x019b3a1970f9f77b13538cd8071ea3ee7c556fd98009e2a04be044ead0a94623) // vk.TABLE_TYPE.x + mstore(add(_vk, 0x520), 0x159cbdae3e194fe45524a171befdcb98b55c8d495fc463c98ac690eee947119f) // vk.TABLE_TYPE.y + mstore(add(_vk, 0x540), 0x16b2f7fa29f578aae3d4c0b8220101570adfcc9e8aa8a148267208540de189f1) // vk.ID1.x + mstore(add(_vk, 0x560), 0x2344a211fbbacc281de980197e4f12155d90d55a67f4ad08398bac665f813953) // vk.ID1.y + mstore(add(_vk, 0x580), 0x1af709df675db1688b95927324e71c5e551436ba7cb32478570a9cfaebf90614) // vk.ID2.x + mstore(add(_vk, 0x5a0), 0x2b83e76f61aa5cd70218c38e693ae0a99e9a2f4a192af5c77dbd27fa605fdae4) // vk.ID2.y + mstore(add(_vk, 0x5c0), 0x038c89635a8b6ec9766d5f98d13c16f8c312088f830610de72c00edf8c3b7800) // vk.ID3.x + mstore(add(_vk, 0x5e0), 0x1863d9217ba6c6764fa02298efe25fabfbe454a27431b970a6afff5d1986fadb) // vk.ID3.y + mstore(add(_vk, 0x600), 0x259a5dd47d44d6240407c26718201a122fb4b6b38d838f6e24d1c75515016761) // vk.ID4.x + mstore(add(_vk, 0x620), 0x14db344b735ffe084107e5cea07b00e4c41a82f0073f76e0536cd7118d78866f) // vk.ID4.y + mstore(add(_vk, 0x640), 0x01) // vk.contains_recursive_proof + mstore(add(_vk, 0x660), 0) // vk.recursive_proof_public_input_indices + mstore(add(_vk, 0x680), 0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1) // vk.g2_x.X.c1 + mstore(add(_vk, 0x6a0), 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0) // vk.g2_x.X.c0 + mstore(add(_vk, 0x6c0), 0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4) // vk.g2_x.Y.c1 + mstore(add(_vk, 0x6e0), 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55) // vk.g2_x.Y.c0 + mstore(_omegaInverseLoc, 0x06e402c0a314fb67a15cf806664ae1b722dbc0efe66e6c81d98f9924ca535321) // vk.work_root_inverse + } + } +} diff --git a/barretenberg/sol/test/base/DifferentialFuzzer.sol b/barretenberg/sol/test/base/DifferentialFuzzer.sol new file mode 100644 index 000000000000..3f6ee18e1d72 --- /dev/null +++ b/barretenberg/sol/test/base/DifferentialFuzzer.sol @@ -0,0 +1,101 @@ +import {Vm} from "forge-std/Vm.sol"; +import {strings} from "stringutils/strings.sol"; +import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; +import {TestBase} from "./TestBase.sol"; +import "forge-std/console.sol"; + +contract DifferentialFuzzer is TestBase { + using strings for *; + using Strings for uint256; + + enum PlonkFlavour { + Invalid, + Standard, + Turbo, + Ultra + } + enum CircuitFlavour { + Invalid, + Blake, + Add2, + Recursive + } + + constructor() {} + + /// @notice the fuzzing flavour + PlonkFlavour public plonkFlavour; + + /// @notice the circuit flavour + CircuitFlavour public circuitFlavour; + + /// @notice the proofs public inputs + uint256[] public inputs; + + function with_plonk_flavour(PlonkFlavour _flavour) public returns (DifferentialFuzzer) { + plonkFlavour = _flavour; + return this; + } + + function with_circuit_flavour(CircuitFlavour _flavour) public returns (DifferentialFuzzer) { + circuitFlavour = _flavour; + return this; + } + + function with_inputs(uint256[] memory _inputs) public returns (DifferentialFuzzer) { + inputs = _inputs; + return this; + } + + function get_plonk_flavour() internal view returns (string memory) { + if (plonkFlavour == PlonkFlavour.Standard) { + return "standard"; + } else if (plonkFlavour == PlonkFlavour.Turbo) { + return "turbo"; + } else if (plonkFlavour == PlonkFlavour.Ultra) { + return "ultra"; + } else { + revert("Invalid flavour"); + } + } + + function get_circuit_flavour() internal view returns (string memory) { + if (circuitFlavour == CircuitFlavour.Blake) { + return "blake"; + } else if (circuitFlavour == CircuitFlavour.Add2) { + return "add2"; + } else if (circuitFlavour == CircuitFlavour.Recursive) { + return "recursive"; + } else { + revert("Invalid circuit flavour"); + } + } + + // Encode inputs as a comma seperated string for the ffi call + function get_inputs() internal view returns (string memory input_params) { + input_params = ""; + if (inputs.length > 0) { + input_params = inputs[0].toHexString(); + for (uint256 i = 1; i < inputs.length; i++) { + input_params = string.concat(input_params, ",", inputs[i].toHexString()); + } + } + } + + function generate_proof() public returns (bytes memory proof) { + // Craft an ffi call to the prover binary + string memory prover_path = "./scripts/run_fuzzer.sh"; + string memory plonk_flavour = get_plonk_flavour(); + string memory circuit_flavour = get_circuit_flavour(); + string memory input_params = get_inputs(); + + // Execute the c++ prover binary + string[] memory ffi_cmds = new string[](4); + ffi_cmds[0] = prover_path; + ffi_cmds[1] = plonk_flavour; + ffi_cmds[2] = circuit_flavour; + ffi_cmds[3] = input_params; + + proof = vm.ffi(ffi_cmds); + } +} diff --git a/barretenberg/sol/test/base/TestBase.sol b/barretenberg/sol/test/base/TestBase.sol new file mode 100644 index 000000000000..78a239b5697e --- /dev/null +++ b/barretenberg/sol/test/base/TestBase.sol @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2022 Aztec. +pragma solidity >=0.8.4; + +import {Test} from "forge-std/Test.sol"; +import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; + +contract TestBase is Test { + using Strings for uint256; + + function readProofData(string memory path) internal view returns (bytes memory) { + // format [4 byte length][data] + // Reads the raw bytes + bytes memory rawBytes = vm.readFileBinary(path); + + // Extract the [data], contains inputs and proof + bytes memory proofData = new bytes(rawBytes.length - 4); // + assembly { + let length := shr(224, mload(add(rawBytes, 0x20))) + + let wLoc := add(proofData, 0x20) + let rLoc := add(rawBytes, 0x24) + let end := add(rLoc, length) + + for {} lt(rLoc, end) { + wLoc := add(wLoc, 0x20) + rLoc := add(rLoc, 0x20) + } { mstore(wLoc, mload(rLoc)) } + } + return proofData; + } + + function splitProof(bytes memory _proofData, uint256 _numberOfPublicInputs) + internal + view + returns (bytes32[] memory publicInputs, bytes memory proof) + { + publicInputs = new bytes32[](_numberOfPublicInputs); + for (uint256 i = 0; i < _numberOfPublicInputs; i++) { + // The proofs spit out by barretenberg have the public inputs at the beginning + publicInputs[i] = readWordByIndex(_proofData, i); + } + + proof = new bytes(_proofData.length - (_numberOfPublicInputs * 0x20)); + uint256 len = proof.length; + assembly { + pop( + staticcall( + gas(), 0x4, add(_proofData, add(0x20, mul(0x20, _numberOfPublicInputs))), len, add(proof, 0x20), len + ) + ) + } + } + + function printBytes(bytes memory _data, uint256 _offset) internal { + uint256 length = _data.length - _offset; + for (uint256 i = 0; i < length / 0x20; i++) { + bytes32 val; + assembly { + val := mload(add(_offset, add(_data, mul(0x20, add(1, i))))) + } + emit log_named_bytes32(toHexString(bytes32(i * 0x20)), val); + } + } + + function printList(bytes32[] memory _data, uint256 _offset) internal { + for (uint256 i = _offset; i < _data.length; i++) { + emit log_named_bytes32(i.toString(), _data[i]); + } + } + + function printList(uint256[] memory _data, uint256 _offset) internal { + for (uint256 i = _offset; i < _data.length; i++) { + emit log_named_bytes32(i.toString(), bytes32(_data[i])); + } + } + + function readWordByIndex(bytes memory _data, uint256 index) internal pure returns (bytes32 result) { + assembly { + result := mload(add(_data, add(0x20, mul(0x20, index)))) + } + } + + /** + * Convert a bytes32 into an ASCII encoded hex string + * @param input bytes32 variable + * @return result hex-encoded string + */ + function toHexString(bytes32 input) public pure returns (string memory result) { + if (uint256(input) == 0x00) { + assembly { + result := mload(0x40) + mstore(result, 0x40) + mstore(add(result, 0x20), 0x3030303030303030303030303030303030303030303030303030303030303030) + mstore(add(result, 0x40), 0x3030303030303030303030303030303030303030303030303030303030303030) + mstore(0x40, add(result, 0x60)) + } + return result; + } + assembly { + result := mload(0x40) + let table := add(result, 0x60) + + // Store lookup table that maps an integer from 0 to 99 into a 2-byte ASCII equivalent + // Store lookup table that maps an integer from 0 to ff into a 2-byte ASCII equivalent + mstore(add(table, 0x1e), 0x3030303130323033303430353036303730383039306130623063306430653066) + mstore(add(table, 0x3e), 0x3130313131323133313431353136313731383139316131623163316431653166) + mstore(add(table, 0x5e), 0x3230323132323233323432353236323732383239326132623263326432653266) + mstore(add(table, 0x7e), 0x3330333133323333333433353336333733383339336133623363336433653366) + mstore(add(table, 0x9e), 0x3430343134323433343434353436343734383439346134623463346434653466) + mstore(add(table, 0xbe), 0x3530353135323533353435353536353735383539356135623563356435653566) + mstore(add(table, 0xde), 0x3630363136323633363436353636363736383639366136623663366436653666) + mstore(add(table, 0xfe), 0x3730373137323733373437353736373737383739376137623763376437653766) + mstore(add(table, 0x11e), 0x3830383138323833383438353836383738383839386138623863386438653866) + mstore(add(table, 0x13e), 0x3930393139323933393439353936393739383939396139623963396439653966) + mstore(add(table, 0x15e), 0x6130613161326133613461356136613761386139616161626163616461656166) + mstore(add(table, 0x17e), 0x6230623162326233623462356236623762386239626162626263626462656266) + mstore(add(table, 0x19e), 0x6330633163326333633463356336633763386339636163626363636463656366) + mstore(add(table, 0x1be), 0x6430643164326433643464356436643764386439646164626463646464656466) + mstore(add(table, 0x1de), 0x6530653165326533653465356536653765386539656165626563656465656566) + mstore(add(table, 0x1fe), 0x6630663166326633663466356636663766386639666166626663666466656666) + /** + * Convert `input` into ASCII. + * + * Slice 2 base-10 digits off of the input, use to index the ASCII lookup table. + * + * We start from the least significant digits, write results into mem backwards, + * this prevents us from overwriting memory despite the fact that each mload + * only contains 2 byteso f useful data. + * + */ + + let base := input + function slice(v, tableptr) { + mstore(0x1e, mload(add(tableptr, shl(1, and(v, 0xff))))) + mstore(0x1c, mload(add(tableptr, shl(1, and(shr(8, v), 0xff))))) + mstore(0x1a, mload(add(tableptr, shl(1, and(shr(16, v), 0xff))))) + mstore(0x18, mload(add(tableptr, shl(1, and(shr(24, v), 0xff))))) + mstore(0x16, mload(add(tableptr, shl(1, and(shr(32, v), 0xff))))) + mstore(0x14, mload(add(tableptr, shl(1, and(shr(40, v), 0xff))))) + mstore(0x12, mload(add(tableptr, shl(1, and(shr(48, v), 0xff))))) + mstore(0x10, mload(add(tableptr, shl(1, and(shr(56, v), 0xff))))) + mstore(0x0e, mload(add(tableptr, shl(1, and(shr(64, v), 0xff))))) + mstore(0x0c, mload(add(tableptr, shl(1, and(shr(72, v), 0xff))))) + mstore(0x0a, mload(add(tableptr, shl(1, and(shr(80, v), 0xff))))) + mstore(0x08, mload(add(tableptr, shl(1, and(shr(88, v), 0xff))))) + mstore(0x06, mload(add(tableptr, shl(1, and(shr(96, v), 0xff))))) + mstore(0x04, mload(add(tableptr, shl(1, and(shr(104, v), 0xff))))) + mstore(0x02, mload(add(tableptr, shl(1, and(shr(112, v), 0xff))))) + mstore(0x00, mload(add(tableptr, shl(1, and(shr(120, v), 0xff))))) + } + + mstore(result, 0x40) + slice(base, table) + mstore(add(result, 0x40), mload(0x1e)) + base := shr(128, base) + slice(base, table) + mstore(add(result, 0x20), mload(0x1e)) + mstore(0x40, add(result, 0x60)) + } + } +} diff --git a/barretenberg/sol/test/ultra/Add2.t.sol b/barretenberg/sol/test/ultra/Add2.t.sol new file mode 100644 index 000000000000..33a3ef83891f --- /dev/null +++ b/barretenberg/sol/test/ultra/Add2.t.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2022 Aztec +pragma solidity >=0.8.4; + +import {TestBaseUltra} from "./TestBaseUltra.sol"; +import {Add2UltraVerifier} from "../../src/ultra/instance/Add2UltraVerifier.sol"; +import {DifferentialFuzzer} from "../base/DifferentialFuzzer.sol"; +import {IVerifier} from "../../src/interfaces/IVerifier.sol"; + +contract Add2UltraTest is TestBaseUltra { + function setUp() public override(TestBaseUltra) { + super.setUp(); + + verifier = IVerifier(address(new Add2UltraVerifier())); + fuzzer = fuzzer.with_circuit_flavour(DifferentialFuzzer.CircuitFlavour.Add2); + + PUBLIC_INPUT_COUNT = 3; + + // Add default inputs to the fuzzer (we will override these in fuzz test) + uint256[] memory defaultInputs = new uint256[](3); + defaultInputs[0] = 5; + defaultInputs[1] = 10; + defaultInputs[2] = 15; + + fuzzer = fuzzer.with_inputs(defaultInputs); + } + + function testFuzzProof(uint16 input1, uint16 input2) public { + uint256[] memory inputs = new uint256[](3); + inputs[0] = uint256(input1); + inputs[1] = uint256(input2); + inputs[2] = inputs[0] + inputs[1]; + + bytes memory proofData = fuzzer.with_inputs(inputs).generate_proof(); + + (bytes32[] memory publicInputs, bytes memory proof) = splitProof(proofData, PUBLIC_INPUT_COUNT); + + assertTrue(verifier.verify(proof, publicInputs), "The proof is not valid"); + } +} diff --git a/barretenberg/sol/test/ultra/Blake.t.sol b/barretenberg/sol/test/ultra/Blake.t.sol new file mode 100644 index 000000000000..3403574616ec --- /dev/null +++ b/barretenberg/sol/test/ultra/Blake.t.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2022 Aztec +pragma solidity >=0.8.4; + +import {TestBaseUltra} from "./TestBaseUltra.sol"; +import {BlakeUltraVerifier} from "../../src/ultra/instance/BlakeUltraVerifier.sol"; +import {DifferentialFuzzer} from "../base/DifferentialFuzzer.sol"; +import {IVerifier} from "../../src/interfaces/IVerifier.sol"; + +contract BlakeUltraTest is TestBaseUltra { + function setUp() public override(TestBaseUltra) { + super.setUp(); + + verifier = IVerifier(address(new BlakeUltraVerifier())); + fuzzer = fuzzer.with_circuit_flavour(DifferentialFuzzer.CircuitFlavour.Blake); + + PUBLIC_INPUT_COUNT = 4; + + // Add default inputs to the fuzzer (we will override these in fuzz test) + uint256[] memory defaultInputs = new uint256[](4); + defaultInputs[0] = 1; + defaultInputs[1] = 2; + defaultInputs[2] = 3; + defaultInputs[3] = 4; + + fuzzer = fuzzer.with_inputs(defaultInputs); + } + + function testFuzzProof(uint256 input1, uint256 input2, uint256 input3, uint256 input4) public { + uint256[] memory inputs = new uint256[](4); + inputs[0] = input1; + inputs[1] = input2; + inputs[2] = input3; + inputs[3] = input4; + + bytes memory proofData = fuzzer.with_inputs(inputs).generate_proof(); + + (bytes32[] memory publicInputs, bytes memory proof) = splitProof(proofData, PUBLIC_INPUT_COUNT); + + assertTrue(verifier.verify(proof, publicInputs), "The proof is not valid"); + } +} diff --git a/barretenberg/sol/test/ultra/Recursive.t.sol b/barretenberg/sol/test/ultra/Recursive.t.sol new file mode 100644 index 000000000000..43dac05dd6d7 --- /dev/null +++ b/barretenberg/sol/test/ultra/Recursive.t.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2022 Aztec +pragma solidity >=0.8.4; + +import {TestBaseUltra} from "./TestBaseUltra.sol"; +import {RecursiveUltraVerifier} from "../../src/ultra/instance/RecursiveUltraVerifier.sol"; +import {DifferentialFuzzer} from "../base/DifferentialFuzzer.sol"; +import {IVerifier} from "../../src/interfaces/IVerifier.sol"; + +contract RecursiveUltraTest is TestBaseUltra { + function setUp() public override(TestBaseUltra) { + super.setUp(); + + verifier = IVerifier(address(new RecursiveUltraVerifier())); + fuzzer = fuzzer.with_circuit_flavour(DifferentialFuzzer.CircuitFlavour.Recursive); + + PUBLIC_INPUT_COUNT = 16; + + // Add default inputs to the fuzzer (we will override these in fuzz test) + uint256[] memory defaultInputs = new uint256[](3); + defaultInputs[0] = 5; + defaultInputs[1] = 10; + defaultInputs[2] = 15; + + fuzzer = fuzzer.with_inputs(defaultInputs); + } + + function testFuzzProof(uint16 input1, uint16 input2) public { + uint256[] memory inputs = new uint256[](3); + inputs[0] = uint256(input1); + inputs[1] = uint256(input2); + inputs[2] = inputs[0] + inputs[1]; + + bytes memory proofData = fuzzer.with_inputs(inputs).generate_proof(); + + (bytes32[] memory publicInputs, bytes memory proof) = splitProof(proofData, PUBLIC_INPUT_COUNT); + + assertTrue(verifier.verify(proof, publicInputs), "The proof is not valid"); + } +} diff --git a/barretenberg/sol/test/ultra/TestBaseUltra.sol b/barretenberg/sol/test/ultra/TestBaseUltra.sol new file mode 100644 index 000000000000..51a621b03cfb --- /dev/null +++ b/barretenberg/sol/test/ultra/TestBaseUltra.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {TestBase} from "../base/TestBase.sol"; +import {DifferentialFuzzer} from "../base/DifferentialFuzzer.sol"; +import {BaseUltraVerifier} from "../../src/ultra/BaseUltraVerifier.sol"; +import {IVerifier} from "../../src/interfaces/IVerifier.sol"; + +contract TestBaseUltra is TestBase { + IVerifier public verifier; + DifferentialFuzzer public fuzzer; + uint256 public PUBLIC_INPUT_COUNT; + + function setUp() public virtual { + fuzzer = new DifferentialFuzzer().with_plonk_flavour(DifferentialFuzzer.PlonkFlavour.Ultra); + } + + function testValidProof() public { + bytes memory proofData = fuzzer.generate_proof(); + (bytes32[] memory publicInputs, bytes memory proof) = splitProof(proofData, PUBLIC_INPUT_COUNT); + assertTrue(verifier.verify(proof, publicInputs), "The proof is not valid"); + } +}