From 3218d4547971a3496b84a457c4d9227b74fb6938 Mon Sep 17 00:00:00 2001 From: Willem Olding Date: Wed, 22 Nov 2023 16:51:58 +0300 Subject: [PATCH 1/3] Adds scripts and docs for deploying contracts (#32) * adds scripts and docs for deploying contracts * remove broadcast files and add this to .gitignore --- .env.example | 13 +++++++++ README.md | 32 +++++++++++++++++++++++ contracts/.env.example | 4 --- contracts/.gitignore | 4 +-- contracts/foundry.toml | 5 ++-- contracts/script/DeploySpectre.s.sol | 27 +++++++++++++++++++ contracts/script/SpectreDeployLocal.s.sol | 23 ---------------- contracts/script/deploy_local.sh | 6 ----- justfile | 11 ++++++++ 9 files changed, 87 insertions(+), 38 deletions(-) create mode 100644 .env.example delete mode 100644 contracts/.env.example create mode 100644 contracts/script/DeploySpectre.s.sol delete mode 100644 contracts/script/SpectreDeployLocal.s.sol delete mode 100755 contracts/script/deploy_local.sh diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..589b20ef --- /dev/null +++ b/.env.example @@ -0,0 +1,13 @@ +DEPLOYER_PRIVATE_KEY="" + +LOCAL_RPC_URL="http://localhost:8545" +MAINNET_RPC_URL="https://mainnet.infura.io/ws/v3/" +GOERLI_RPC_URL="https://goerli.infura.io/ws/v3/" +SEPOLIA_RPC_URL="https://sepolia.infura.io/ws/v3/" + +# These two variables define the starting point for the Spectre light-client. +INITIAL_SYNC_PERIOD=10 # sync period for the initial committee +INITIAL_COMMITTEE_POSEIDON="0x9ab5d3df6912b5bfeeb8e33b5bddad7c3a4f448fd99804dae38c3641f55680fe" # hex encoded poseidon hash of the initial committee + +# This is a fixed property of the chain that Spectre is acting as a light-client for +SLOTS_PER_PERIOD=8192 # 8192 = 256 epochs * 32 slots for mainnet ethereum. diff --git a/README.md b/README.md index a3fc52a2..78d7ab73 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,34 @@ # Spectre Spectre is a ZK-based block header oracle protocol based on Altair fork light-client sync protocol. + +## Deploying contracts + +Just scripts are provided to deploy the contracts either to a local testnet, or public networks. + +For either make a copy of the `.env.example` file called `.env`. Set the `INITIAL_SYNC_PERIOD`, `INITIAL_COMMITTEE_POSEIDON` and `SLOTS_PER_PERIOD` variables according to the network you want Spectre to act as a light-client for and the starting point. + +### Deploying locally + +1. Start a local anvil instance with: + +```shell +anvil +``` + +2. Copy one of the private key strings printed into the `DEPLOYER_PRIVATE_KEY` in the `.env` file then run + +```shell +just deploy-contracts-local +``` + +### Deploying to a public network + +1. Obtain the required gas token and obtain the private key for the deployer account. Set the `DEPLOYER_PRIVATE_KEY` in the `.env` file. +2. Obtain a public RPC URL for the network and set the variable `_RPC_URL` in the `.env` file (If using Infura this will require an API key) +3. Run + +```shell +just deploy-contracts +``` + +where `` is one of `["GOERLI", "SEPOLIA", "MAINNET"]`. diff --git a/contracts/.env.example b/contracts/.env.example deleted file mode 100644 index f4795498..00000000 --- a/contracts/.env.example +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -ANVIL_PRIVATE_KEY=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 -MAINNET_RPC_URL= -GOERLI_RPC_URL= diff --git a/contracts/.gitignore b/contracts/.gitignore index 85198aaa..9d91b783 100644 --- a/contracts/.gitignore +++ b/contracts/.gitignore @@ -3,9 +3,7 @@ cache/ out/ # Ignores development broadcast logs -!/broadcast -/broadcast/*/31337/ -/broadcast/**/dry-run/ +broadcast/ # Docs docs/ diff --git a/contracts/foundry.toml b/contracts/foundry.toml index 817004ea..ac99a4c6 100644 --- a/contracts/foundry.toml +++ b/contracts/foundry.toml @@ -12,7 +12,8 @@ fs_permissions = [{ access = "read", path = "./test/data/"}] constantOptimizer = true yul = false -# See more config options https://github.com/foundry-rs/foundry/tree/master/config - [rpc_endpoints] mainnet = "${MAINNET_RPC_URL}" +goerli = "${GOERLI_RPC_URL}" +sepolia = "${SEPOLIA_RPC_URL}" +local = "${LOCAL_RPC_URL}" diff --git a/contracts/script/DeploySpectre.s.sol b/contracts/script/DeploySpectre.s.sol new file mode 100644 index 00000000..191b4721 --- /dev/null +++ b/contracts/script/DeploySpectre.s.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "forge-std/Script.sol"; + +import {Spectre} from "../src/Spectre.sol"; +import {Verifier as SyncStepVerifier} from "../snark-verifiers/sync_step.sol"; +import {Verifier as CommitteeUpdateVerifier} from "../snark-verifiers/committee_update_aggregated.sol"; + +contract DeploySpectre is Script { + + function run() external { + uint256 deployerPrivateKey = vm.envUint("DEPLOYER_PRIVATE_KEY"); + uint256 initialSyncPeriod = vm.envUint("INITIAL_SYNC_PERIOD"); + bytes32 initialCommitteePoseidon = vm.envBytes32("INITIAL_COMMITTEE_POSEIDON"); + uint256 slotsPerPeriod = vm.envUint("SLOTS_PER_PERIOD"); + + vm.startBroadcast(deployerPrivateKey); + + SyncStepVerifier stepVerifier = new SyncStepVerifier(); + CommitteeUpdateVerifier updateVerifier = new CommitteeUpdateVerifier(); + + Spectre spectre = new Spectre(address(stepVerifier), address(updateVerifier), initialSyncPeriod, initialCommitteePoseidon, slotsPerPeriod); + + vm.stopBroadcast(); + } +} diff --git a/contracts/script/SpectreDeployLocal.s.sol b/contracts/script/SpectreDeployLocal.s.sol deleted file mode 100644 index 91c64873..00000000 --- a/contracts/script/SpectreDeployLocal.s.sol +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.19; - -import "forge-std/Script.sol"; -import "forge-std/safeconsole.sol"; -import {Spectre} from "../src/Spectre.sol"; -import {Verifier as SyncStepVerifier} from "../snark-verifiers/sync_step.sol"; -import {Verifier as CommitteeUpdateVerifier} from "../snark-verifiers/committee_update_aggregated.sol"; - -contract SpectreDeployLocal is Script { - bytes proof; - address syncStepVerifierAddress; - - function run() external { - vm.startBroadcast(); - - new SyncStepVerifier(); - new CommitteeUpdateVerifier(); - - - vm.stopBroadcast(); - } -} diff --git a/contracts/script/deploy_local.sh b/contracts/script/deploy_local.sh deleted file mode 100755 index 58e2ed80..00000000 --- a/contracts/script/deploy_local.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh -cd $(git rev-parse --show-toplevel) -source .env -LOCAL_RPC_URL="http://localhost:8545" - -forge script script/SpectreDeployLocal.s.sol:SpectreDeployLocal --private-key $ANVIL_PRIVATE_KEY --rpc-url $LOCAL_RPC_URL --broadcast -vvvv diff --git a/justfile b/justfile index 9ca27712..507879ab 100644 --- a/justfile +++ b/justfile @@ -1,3 +1,6 @@ +set dotenv-load # automatically loads .env file in the current directory +set positional-arguments + test: cargo test --workspace @@ -28,6 +31,14 @@ gen-rotation-evm-verifier: build-contracts: cd contracts && forge build +deploy-contracts-local: + cd contracts && forge script ./script/DeploySpectre.s.sol:DeploySpectre --fork-url $LOCAL_RPC_URL --broadcast + +deploy-contracts network: # network one of [MAINNET, GOERLI, SEPOLIA] + #! /usr/bin/env bash + RPC_URL="$1_RPC_URL" + cd contracts && forge script ./script/DeploySpectre.s.sol:DeploySpectre --rpc-url ${!RPC_URL} --broadcast --verify -vvvv + # downloads spec tests and copies them to the right locations. download-spec-tests: clean-spec-tests #!/usr/bin/env bash From 356324c8dabf5746ce945b681e12cd22c064bd08 Mon Sep 17 00:00:00 2001 From: Eric Tu <6364934+ec2@users.noreply.github.com> Date: Wed, 22 Nov 2023 16:54:16 +0300 Subject: [PATCH 2/3] Fix prover key path for Rotation circuit (#29) --- prover/src/rpc.rs | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/prover/src/rpc.rs b/prover/src/rpc.rs index 7c4fb054..c1107b1d 100644 --- a/prover/src/rpc.rs +++ b/prover/src/rpc.rs @@ -81,35 +81,30 @@ pub(crate) async fn gen_evm_proof_rotation_circuit_handler( // TODO: use config/build paths from CLI flags let app_config_path = PathBuf::from("../lightclient-circuits/config/committee_update.json"); - let app_pk_path = PathBuf::from("./build/committee_update_circuit.pkey"); + + let agg_l2_pk_path = PathBuf::from("./build/step_agg_l2.pkey"); + let agg_l1_pk_path = PathBuf::from("./build/step_agg_l1.pkey"); let agg_l2_config_path = PathBuf::from("../lightclient-circuits/config/committee_update_aggregation_2.json"); let agg_l1_config_path = PathBuf::from("../lightclient-circuits/config/committee_update_aggregation_1.json"); - let _build_dir = PathBuf::from("./build"); - let (l0_snark, _pk_filename) = match spec { + let l0_snark = match spec { Spec::Minimal => { let witness = fetch_rotation_args(beacon_api).await?; - ( - gen_app_snark::(app_config_path, app_pk_path, witness)?, - "agg_rotation_circuit_minimal.pkey", - ) + let app_pk_path = PathBuf::from("./build/committee_update_circuit_minimal.pkey"); + gen_app_snark::(app_config_path, app_pk_path, witness)? } Spec::Testnet => { let witness = fetch_rotation_args(beacon_api).await?; - ( - gen_app_snark::(app_config_path, app_pk_path, witness)?, - "agg_rotation_circuit_testnet.pkey", - ) + let app_pk_path = PathBuf::from("./build/committee_update_circuit_testnet.pkey"); + gen_app_snark::(app_config_path, app_pk_path, witness)? } Spec::Mainnet => { let witness = fetch_rotation_args(beacon_api).await?; - ( - gen_app_snark::(app_config_path, app_pk_path, witness)?, - "agg_rotation_circuit_mainnet.pkey", - ) + let app_pk_path = PathBuf::from("./build/committee_update_circuit_mainnet.pkey"); + gen_app_snark::(app_config_path, app_pk_path, witness)? } }; @@ -121,7 +116,7 @@ pub(crate) async fn gen_evm_proof_rotation_circuit_handler( println!("L1 Keygen num_instances: {:?}", circuit.num_instance()); - let pk_l1 = gen_pk(&p1, &circuit, None); + let pk_l1 = gen_pk(&p1, &circuit, Some(&agg_l1_pk_path)); let pinning = AggregationConfigPinning::from_path(agg_l1_config_path); let lookup_bits = k as usize - 1; let mut circuit = AggregationCircuit::new::( @@ -147,7 +142,7 @@ pub(crate) async fn gen_evm_proof_rotation_circuit_handler( AggregationCircuit::keygen::(&p2, std::iter::once(l1_snark.clone())); circuit.expose_previous_instances(true); - let pk_l2 = gen_pk(&p2, &circuit, None); + let pk_l2 = gen_pk(&p2, &circuit, Some(&agg_l2_pk_path)); let pinning = AggregationConfigPinning::from_path(agg_l2_config_path); let mut circuit = AggregationCircuit::prover::( From 6902971adff3c0030a1bd4225df8759131efef81 Mon Sep 17 00:00:00 2001 From: Eric Tu <6364934+ec2@users.noreply.github.com> Date: Thu, 23 Nov 2023 16:08:58 +0100 Subject: [PATCH 3/3] Hash-oracle-node Integration (#30) * RPC as a lib * Add new RPC endpoints to allow for proof gen with witnesses already supplied * Witness <> Arg conversion. Needs Cleanup * Some cleanup * Change state id to head * Remove sync commitee rs lib * Update SSZ and ethereum consensus crates * fix test compilation * clippy * More clippy * reexport jsonrpc_v2 lib * fix prover * clippy * break up rotation args gen * lightcleint update to args * Remove all beacon fetching out of the prover node * simplify generic * verifier contract gen fixes * add rpc endpoint for calculating poseidon committment * clippy --- Cargo.toml | 40 +-- contract-tests/Cargo.toml | 2 +- .../tests/rotation_input_encoding.rs | 8 +- contract-tests/tests/step_input_encoding.rs | 10 +- contracts/Cargo.toml | 6 + contracts/rust-abi/lib.rs | 117 ++++++- contracts/snark-verifiers/sync_step.sol | 2 +- eth-types/src/spec.rs | 2 + justfile | 10 +- lightclient-circuits/Cargo.toml | 4 +- .../src/committee_update_circuit.rs | 19 +- lightclient-circuits/src/poseidon.rs | 31 ++ lightclient-circuits/src/sync_step_circuit.rs | 2 +- lightclient-circuits/src/witness/rotation.rs | 7 +- lightclient-circuits/src/witness/sync.rs | 10 +- preprocessor/Cargo.toml | 9 +- preprocessor/src/lib.rs | 202 ++++++++++++ preprocessor/src/rotation.rs | 121 +++++--- preprocessor/src/sync.rs | 278 ++++++++--------- prover/Cargo.toml | 9 +- prover/contractyul | 0 prover/src/cli.rs | 84 +++-- prover/src/lib.rs | 5 + prover/src/main.rs | 14 +- prover/src/rpc.rs | 293 +++++++++++++++++- prover/src/rpc_api.rs | 41 +++ prover/src/rpc_client.rs | 40 ++- test-utils/Cargo.toml | 3 +- test-utils/src/conversions.rs | 7 +- test-utils/src/execution_payload_header.rs | 30 +- test-utils/src/lib.rs | 75 ++--- 31 files changed, 1104 insertions(+), 377 deletions(-) create mode 100644 prover/contractyul create mode 100644 prover/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index df75563b..7d42525d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,29 +24,14 @@ incremental = true [workspace.dependencies] lightclient-circuits = { path = "lightclient-circuits" } test-utils = { path = "test-utils" } -ethereum-consensus-types = { git = "ssh://git@github.com/sygmaprotocol/Zipline.git", rev = "27e8a01" } +ethereum-consensus-types = { git = "ssh://git@github.com/sygmaprotocol/Zipline.git", rev = "5ecf28d24690862814a7ebc5385c7e5c2eeb6e3d" } eth-types = { path = "eth-types" } contracts = { path = "contracts" } preprocessor = { path = "preprocessor" } -ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs", rev = "adf1a0b14cef90b9536f28ef89da1fab316465e1" } -zipline-test-utils = { package = "test-utils", git = "ssh://git@github.com/sygmaprotocol/Zipline.git", rev = "27e8a01" } -light-client-verifier = { git = "ssh://git@github.com/sygmaprotocol/Zipline.git", rev = "27e8a01" } -sync-committee-primitives = { git = "https://github.com/polytope-labs/sync-committee-rs", version = "0.1.0" } -halo2curves = { git = "https://github.com/privacy-scaling-explorations/halo2curves", tag = "0.3.1" } -halo2-base = { git = "https://github.com/axiom-crypto/halo2-lib", branch = "community-edition", default-features = false, features = [ - "halo2-pse", - "display", -] } -snark-verifier-sdk = { git = "https://github.com/axiom-crypto/snark-verifier.git", branch = "community-edition", default-features = false, features = [ - "display", - "loader_halo2", - "loader_evm", - "halo2-pse", -] } - -[patch."https://github.com/privacy-scaling-explorations/halo2curves"] -halo2curves = { git = "https://github.com/sygmaprotocol/halo2curves", branch = "dev/bls12_381" } -# halo2curves = { path = "../halo2curves" } +zipline-test-utils = { package = "test-utils", git = "ssh://git@github.com/sygmaprotocol/Zipline.git", rev = "5ecf28d24690862814a7ebc5385c7e5c2eeb6e3d" } +zipline-cryptography = { package = "cryptography", git = "ssh://git@github.com/sygmaprotocol/Zipline.git", rev = "5ecf28d24690862814a7ebc5385c7e5c2eeb6e3d" } +light-client-verifier = { git = "ssh://git@github.com/sygmaprotocol/Zipline.git", rev = "5ecf28d24690862814a7ebc5385c7e5c2eeb6e3d" } +beacon-api-client = { git = "https://github.com/ralexstokes/ethereum-consensus.git", rev = "f3bff52e9c43866f231ec40c8ab0e34125a8957f" } # halo2 halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.git", tag = "v2023_02_02", features = [ @@ -75,12 +60,7 @@ snark-verifier-sdk = { git = "https://github.com/axiom-crypto/snark-verifier.git ] } halo2_solidity_verifier = { git = "https://github.com/privacy-scaling-explorations/halo2-solidity-verifier", branch = "ac/initial-verifier-PR" } -# ethereum types -sync-committee-primitives = { git = "https://github.com/polytope-labs/sync-committee-rs", version = "0.1.0" } -sync-committee-prover = { git = "https://github.com/polytope-labs/sync-committee-rs", version = "0.1.0", features = [ - "testnet", -] } -ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs", rev = "adf1a0b14cef90b9536f28ef89da1fab316465e1" } +ssz_rs = "0.9" # crypto group = "0.12" @@ -111,9 +91,5 @@ poseidon = { git = "https://github.com/timoftime/halo2-lib", rev = "95bf9a5ce6b6 snark-verifier = { git = "https://github.com/timoftime/snark-verifier", branch = "timoftime/bump-revm" } snark-verifier-sdk = { git = "https://github.com/timoftime/snark-verifier", branch = "timoftime/bump-revm" } -[patch."https://github.com/ralexstokes/ssz-rs"] -ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch = "main" } - -[patch."https://github.com/polytope-labs/sync-committee-rs"] -sync-committee-prover = { git = "https://github.com/timoftime/sync-committee-rs", branch = "dev/accept-ssz" } -sync-committee-primitives = { git = "https://github.com/timoftime/sync-committee-rs", branch = "dev/accept-ssz" } +[patch.crates-io] +ssz_rs = { git = "https://github.com/ralexstokes/ssz-rs", rev = "5f1ec833718efa07bbbff427ab28a1eeaa706164" } diff --git a/contract-tests/Cargo.toml b/contract-tests/Cargo.toml index a6563db0..907e1208 100644 --- a/contract-tests/Cargo.toml +++ b/contract-tests/Cargo.toml @@ -16,7 +16,7 @@ lightclient-circuits = { workspace = true } test-utils = { workspace = true } halo2curves = { workspace = true } eth-types = { workspace = true } -ssz-rs = { workspace = true } +ssz_rs = { workspace = true } halo2-base = { workspace = true } snark-verifier-sdk = { workspace = true } contracts = { workspace = true } diff --git a/contract-tests/tests/rotation_input_encoding.rs b/contract-tests/tests/rotation_input_encoding.rs index b16f1fe5..0a47c2c6 100644 --- a/contract-tests/tests/rotation_input_encoding.rs +++ b/contract-tests/tests/rotation_input_encoding.rs @@ -8,11 +8,13 @@ use ethers::contract::abigen; use halo2curves::bn256::{self, Fr}; use itertools::Itertools; use lightclient_circuits::committee_update_circuit::CommitteeUpdateCircuit; +use lightclient_circuits::poseidon::poseidon_committee_commitment_from_compressed; use lightclient_circuits::witness::CommitteeRotationArgs; use rstest::rstest; use ssz_rs::prelude::*; use ssz_rs::Merkleized; -use test_utils::{poseidon_committee_commitment_from_compressed, read_test_files_and_gen_witness}; +use std::ops::Deref; +use test_utils::read_test_files_and_gen_witness; abigen!( RotateExternal, @@ -33,7 +35,7 @@ async fn test_rotate_public_input_evm_equivalence( .clone() .hash_tree_root() .unwrap() - .as_bytes() + .deref() .try_into() .unwrap(); @@ -91,7 +93,7 @@ where let sync_committee_ssz = pk_vector .hash_tree_root() .unwrap() - .as_bytes() + .deref() .try_into() .unwrap(); diff --git a/contract-tests/tests/step_input_encoding.rs b/contract-tests/tests/step_input_encoding.rs index bb3fdd4c..2fa1f07b 100644 --- a/contract-tests/tests/step_input_encoding.rs +++ b/contract-tests/tests/step_input_encoding.rs @@ -1,16 +1,16 @@ +use std::ops::Deref; use std::path::PathBuf; use contract_tests::make_client; use eth_types::Minimal; use ethers::contract::abigen; use halo2curves::bn256; +use lightclient_circuits::poseidon::poseidon_committee_commitment_from_uncompressed; use lightclient_circuits::sync_step_circuit::SyncStepCircuit; use lightclient_circuits::witness::SyncStepArgs; use rstest::rstest; use ssz_rs::Merkleized; -use test_utils::{ - poseidon_committee_commitment_from_uncompressed, read_test_files_and_gen_witness, -}; +use test_utils::read_test_files_and_gen_witness; abigen!( SyncStepExternal, @@ -57,7 +57,7 @@ impl From> for SyncStepInput { .clone() .hash_tree_root() .unwrap() - .as_bytes() + .deref() .try_into() .unwrap(); @@ -66,7 +66,7 @@ impl From> for SyncStepInput { SyncStepInput { attested_slot: args.attested_header.slot, finalized_slot: args.finalized_header.slot, - participation: participation, + participation, finalized_header_root, execution_payload_root, } diff --git a/contracts/Cargo.toml b/contracts/Cargo.toml index e747da84..c867fa75 100644 --- a/contracts/Cargo.toml +++ b/contracts/Cargo.toml @@ -8,3 +8,9 @@ path = "rust-abi/lib.rs" [dependencies] ethers = "2.0.10" +lightclient-circuits.workspace = true +eth-types.workspace = true +ssz_rs.workspace = true +halo2curves = { workspace = true } +itertools = { workspace = true } +halo2-base = { workspace = true } \ No newline at end of file diff --git a/contracts/rust-abi/lib.rs b/contracts/rust-abi/lib.rs index 5d54d71c..016840c2 100644 --- a/contracts/rust-abi/lib.rs +++ b/contracts/rust-abi/lib.rs @@ -1,20 +1,109 @@ -use ethers::contract::abigen; - -abigen!(Spectre, "./out/Spectre.sol/Spectre.json"); - -abigen!(StepVerifier, "./out/sync_step.sol/Verifier.json"); +#![feature(generic_const_exprs)] +use std::ops::Deref; +use ethers::contract::abigen; +use halo2_base::utils::ScalarField; +use halo2curves::bls12_381::{self}; +use halo2curves::bn256::Fr; +use itertools::Itertools; +use lightclient_circuits::{ + poseidon::fq_array_poseidon_native, + witness::{CommitteeRotationArgs, SyncStepArgs}, +}; +use ssz_rs::{Merkleized, Vector}; abigen!( + Spectre, + "./out/Spectre.sol/Spectre.json"; + StepVerifier, + "./out/sync_step.sol/Verifier.json"; CommitteeUpdateVerifier, - "./out/committee_update_aggregated.sol/Verifier.json" -); - -abigen!( + "./out/committee_update_aggregated.sol/Verifier.json"; StepMockVerifier, - "./out/SyncStepMockVerifier.sol/SyncStepMockVerifier.json" -); - -abigen!( + "./out/SyncStepMockVerifier.sol/SyncStepMockVerifier.json"; CommitteeUpdateMockVerifier, - "./out/CommitteeUpdateMockVerifier.sol/CommitteeUpdateMockVerifier.json" + "./out/CommitteeUpdateMockVerifier.sol/CommitteeUpdateMockVerifier.json"; + RotateExternal, + "./out/RotateExternal.sol/RotateExternal.json"; + SyncStepExternal, + "./out/SyncStepExternal.sol/SyncStepExternal.json"; ); + +// SyncStepInput type produced by abigen macro matches the solidity struct type +impl From> for SyncStepInput { + fn from(args: SyncStepArgs) -> Self { + let participation = args + .pariticipation_bits + .iter() + .map(|v| *v as u64) + .sum::(); + + let finalized_header_root: [u8; 32] = args + .finalized_header + .clone() + .hash_tree_root() + .unwrap() + .deref() + .try_into() + .unwrap(); + + let execution_payload_root: [u8; 32] = args.execution_payload_root.try_into().unwrap(); + + SyncStepInput { + attested_slot: args.attested_header.slot, + finalized_slot: args.finalized_header.slot, + participation, + finalized_header_root, + execution_payload_root, + } + } +} + +// CommitteeRotationArgs type produced by abigen macro matches the solidity struct type +impl From> for RotateInput +where + [(); Spec::SYNC_COMMITTEE_SIZE]:, +{ + fn from(args: CommitteeRotationArgs) -> Self { + let poseidon_commitment_le = poseidon_committee_commitment_from_compressed( + &args + .pubkeys_compressed + .iter() + .cloned() + .map(|mut b| { + b.reverse(); + b + }) + .collect_vec(), + ); + + let mut pk_vector: Vector, { Spec::SYNC_COMMITTEE_SIZE }> = args + .pubkeys_compressed + .iter() + .cloned() + .map(|v| v.try_into().unwrap()) + .collect_vec() + .try_into() + .unwrap(); + + let sync_committee_ssz = pk_vector + .hash_tree_root() + .unwrap() + .deref() + .try_into() + .unwrap(); + + RotateInput { + sync_committee_ssz, + sync_committee_poseidon: poseidon_commitment_le, + } + } +} + +pub fn poseidon_committee_commitment_from_compressed(pubkeys_compressed: &[Vec]) -> [u8; 32] { + let pubkeys_x = pubkeys_compressed.iter().cloned().map(|mut bytes| { + bytes[47] &= 0b00011111; + bls12_381::Fq::from_bytes_le(&bytes) + }); + let poseidon_commitment = fq_array_poseidon_native::(pubkeys_x).unwrap(); + poseidon_commitment.to_bytes_le().try_into().unwrap() +} diff --git a/contracts/snark-verifiers/sync_step.sol b/contracts/snark-verifiers/sync_step.sol index 4e6f52d2..a35f2db1 100644 --- a/contracts/snark-verifiers/sync_step.sol +++ b/contracts/snark-verifiers/sync_step.sol @@ -20,4 +20,4 @@ contract Verifier { for (uint i = 0; i < pubInputs.length; i++) { require(pubInputs[i] < SIZE_LIMIT); } - assembly { let f_p := 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47 let f_q := 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001 function validate_ec_point(x, y) -> valid { { let x_lt_p := lt(x, 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47) let y_lt_p := lt(y, 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47) valid := and(x_lt_p, y_lt_p) } { let y_square := mulmod(y, y, 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47) let x_square := mulmod(x, x, 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47) let x_cube := mulmod(x_square, x, 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47) let x_cube_plus_3 := addmod(x_cube, 3, 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47) let is_affine := eq(x_cube_plus_3, y_square) valid := and(valid, is_affine) } } mstore(0xa0, mod(calldataload(0x4), f_q))mstore(0x80, 12252411032085666170989519148059445533841057314882952128252589464866590048872) { let x := calldataload(0x64) mstore(0xc0, x) let y := calldataload(0x84) mstore(0xe0, y) success := and(validate_ec_point(x, y), success) } { let x := calldataload(0xa4) mstore(0x100, x) let y := calldataload(0xc4) mstore(0x120, y) success := and(validate_ec_point(x, y), success) } { let x := calldataload(0xe4) mstore(0x140, x) let y := calldataload(0x104) mstore(0x160, y) success := and(validate_ec_point(x, y), success) } { let x := calldataload(0x124) mstore(0x180, x) let y := calldataload(0x144) mstore(0x1a0, y) success := and(validate_ec_point(x, y), success) } { let x := calldataload(0x164) mstore(0x1c0, x) let y := calldataload(0x184) mstore(0x1e0, y) success := and(validate_ec_point(x, y), success) } { let x := calldataload(0x1a4) mstore(0x200, x) let y := calldataload(0x1c4) mstore(0x220, y) success := and(validate_ec_point(x, y), success) } { let x := calldataload(0x1e4) mstore(0x240, x) let y := calldataload(0x204) mstore(0x260, y) success := and(validate_ec_point(x, y), success) } { let x := calldataload(0x224) mstore(0x280, x) let y := calldataload(0x244) mstore(0x2a0, y) success := and(validate_ec_point(x, y), success) } { let x := calldataload(0x264) mstore(0x2c0, x) let y := calldataload(0x284) mstore(0x2e0, y) success := and(validate_ec_point(x, y), success) }mstore(0x300, keccak256(0x80, 640)){ let hash := mload(0x300) mstore(0x320, mod(hash, f_q)) mstore(0x340, hash) } { let x := calldataload(0x2a4) mstore(0x360, x) let y := calldataload(0x2c4) mstore(0x380, y) success := and(validate_ec_point(x, y), success) } { let x := calldataload(0x2e4) mstore(0x3a0, x) let y := calldataload(0x304) mstore(0x3c0, y) success := and(validate_ec_point(x, y), success) } { let x := calldataload(0x324) mstore(0x3e0, x) let y := calldataload(0x344) mstore(0x400, y) success := and(validate_ec_point(x, y), success) } { let x := calldataload(0x364) mstore(0x420, x) let y := calldataload(0x384) mstore(0x440, y) success := and(validate_ec_point(x, y), success) }mstore(0x460, keccak256(0x340, 288)){ let hash := mload(0x460) mstore(0x480, mod(hash, f_q)) mstore(0x4a0, hash) }mstore8(0x4c0, 1)mstore(0x4c0, keccak256(0x4a0, 33)){ let hash := mload(0x4c0) mstore(0x4e0, mod(hash, f_q)) mstore(0x500, hash) } { let x := calldataload(0x3a4) mstore(0x520, x) let y := calldataload(0x3c4) mstore(0x540, y) success := and(validate_ec_point(x, y), success) } { let x := calldataload(0x3e4) mstore(0x560, x) let y := calldataload(0x404) mstore(0x580, y) success := and(validate_ec_point(x, y), success) } { let x := calldataload(0x424) mstore(0x5a0, x) let y := calldataload(0x444) mstore(0x5c0, y) success := and(validate_ec_point(x, y), success) } { let x := calldataload(0x464) mstore(0x5e0, x) let y := calldataload(0x484) mstore(0x600, y) success := and(validate_ec_point(x, y), success) } { let x := calldataload(0x4a4) mstore(0x620, x) let y := calldataload(0x4c4) mstore(0x640, y) success := and(validate_ec_point(x, y), success) } { let x := calldataload(0x4e4) mstore(0x660, x) let y := calldataload(0x504) mstore(0x680, y) success := and(validate_ec_point(x, y), success) } { let x := calldataload(0x524) mstore(0x6a0, x) let y := calldataload(0x544) mstore(0x6c0, y) success := and(validate_ec_point(x, y), success) } { let x := calldataload(0x564) mstore(0x6e0, x) let y := calldataload(0x584) mstore(0x700, y) success := and(validate_ec_point(x, y), success) } { let x := calldataload(0x5a4) mstore(0x720, x) let y := calldataload(0x5c4) mstore(0x740, y) success := and(validate_ec_point(x, y), success) }mstore(0x760, keccak256(0x500, 608)){ let hash := mload(0x760) mstore(0x780, mod(hash, f_q)) mstore(0x7a0, hash) } { let x := calldataload(0x5e4) mstore(0x7c0, x) let y := calldataload(0x604) mstore(0x7e0, y) success := and(validate_ec_point(x, y), success) } { let x := calldataload(0x624) mstore(0x800, x) let y := calldataload(0x644) mstore(0x820, y) success := and(validate_ec_point(x, y), success) } { let x := calldataload(0x664) mstore(0x840, x) let y := calldataload(0x684) mstore(0x860, y) success := and(validate_ec_point(x, y), success) }mstore(0x880, keccak256(0x7a0, 224)){ let hash := mload(0x880) mstore(0x8a0, mod(hash, f_q)) mstore(0x8c0, hash) }mstore(0x8e0, mod(calldataload(0x6a4), f_q))mstore(0x900, mod(calldataload(0x6c4), f_q))mstore(0x920, mod(calldataload(0x6e4), f_q))mstore(0x940, mod(calldataload(0x704), f_q))mstore(0x960, mod(calldataload(0x724), f_q))mstore(0x980, mod(calldataload(0x744), f_q))mstore(0x9a0, mod(calldataload(0x764), f_q))mstore(0x9c0, mod(calldataload(0x784), f_q))mstore(0x9e0, mod(calldataload(0x7a4), f_q))mstore(0xa00, mod(calldataload(0x7c4), f_q))mstore(0xa20, mod(calldataload(0x7e4), f_q))mstore(0xa40, mod(calldataload(0x804), f_q))mstore(0xa60, mod(calldataload(0x824), f_q))mstore(0xa80, mod(calldataload(0x844), f_q))mstore(0xaa0, mod(calldataload(0x864), f_q))mstore(0xac0, mod(calldataload(0x884), f_q))mstore(0xae0, mod(calldataload(0x8a4), f_q))mstore(0xb00, mod(calldataload(0x8c4), f_q))mstore(0xb20, mod(calldataload(0x8e4), f_q))mstore(0xb40, mod(calldataload(0x904), f_q))mstore(0xb60, mod(calldataload(0x924), f_q))mstore(0xb80, mod(calldataload(0x944), f_q))mstore(0xba0, mod(calldataload(0x964), f_q))mstore(0xbc0, mod(calldataload(0x984), f_q))mstore(0xbe0, mod(calldataload(0x9a4), f_q))mstore(0xc00, mod(calldataload(0x9c4), f_q))mstore(0xc20, mod(calldataload(0x9e4), f_q))mstore(0xc40, mod(calldataload(0xa04), f_q))mstore(0xc60, mod(calldataload(0xa24), f_q))mstore(0xc80, mod(calldataload(0xa44), f_q))mstore(0xca0, mod(calldataload(0xa64), f_q))mstore(0xcc0, mod(calldataload(0xa84), f_q))mstore(0xce0, mod(calldataload(0xaa4), f_q))mstore(0xd00, mod(calldataload(0xac4), f_q))mstore(0xd20, mod(calldataload(0xae4), f_q))mstore(0xd40, mod(calldataload(0xb04), f_q))mstore(0xd60, mod(calldataload(0xb24), f_q))mstore(0xd80, mod(calldataload(0xb44), f_q))mstore(0xda0, mod(calldataload(0xb64), f_q))mstore(0xdc0, mod(calldataload(0xb84), f_q))mstore(0xde0, mod(calldataload(0xba4), f_q))mstore(0xe00, mod(calldataload(0xbc4), f_q))mstore(0xe20, mod(calldataload(0xbe4), f_q))mstore(0xe40, mod(calldataload(0xc04), f_q))mstore(0xe60, mod(calldataload(0xc24), f_q))mstore(0xe80, mod(calldataload(0xc44), f_q))mstore(0xea0, mod(calldataload(0xc64), f_q))mstore(0xec0, mod(calldataload(0xc84), f_q))mstore(0xee0, mod(calldataload(0xca4), f_q))mstore(0xf00, mod(calldataload(0xcc4), f_q))mstore(0xf20, mod(calldataload(0xce4), f_q))mstore(0xf40, mod(calldataload(0xd04), f_q))mstore(0xf60, mod(calldataload(0xd24), f_q))mstore(0xf80, mod(calldataload(0xd44), f_q))mstore(0xfa0, mod(calldataload(0xd64), f_q))mstore(0xfc0, mod(calldataload(0xd84), f_q))mstore(0xfe0, mod(calldataload(0xda4), f_q))mstore(0x1000, mod(calldataload(0xdc4), f_q))mstore(0x1020, mod(calldataload(0xde4), f_q))mstore(0x1040, mod(calldataload(0xe04), f_q))mstore(0x1060, mod(calldataload(0xe24), f_q))mstore(0x1080, mod(calldataload(0xe44), f_q))mstore(0x10a0, mod(calldataload(0xe64), f_q))mstore(0x10c0, mod(calldataload(0xe84), f_q))mstore(0x10e0, mod(calldataload(0xea4), f_q))mstore(0x1100, mod(calldataload(0xec4), f_q))mstore(0x1120, mod(calldataload(0xee4), f_q))mstore(0x1140, mod(calldataload(0xf04), f_q))mstore(0x1160, mod(calldataload(0xf24), f_q))mstore(0x1180, mod(calldataload(0xf44), f_q))mstore(0x11a0, mod(calldataload(0xf64), f_q))mstore(0x11c0, mod(calldataload(0xf84), f_q))mstore(0x11e0, mod(calldataload(0xfa4), f_q))mstore(0x1200, mod(calldataload(0xfc4), f_q))mstore(0x1220, mod(calldataload(0xfe4), f_q))mstore(0x1240, mod(calldataload(0x1004), f_q))mstore(0x1260, keccak256(0x8c0, 2464)){ let hash := mload(0x1260) mstore(0x1280, mod(hash, f_q)) mstore(0x12a0, hash) }mstore8(0x12c0, 1)mstore(0x12c0, keccak256(0x12a0, 33)){ let hash := mload(0x12c0) mstore(0x12e0, mod(hash, f_q)) mstore(0x1300, hash) } { let x := calldataload(0x1024) mstore(0x1320, x) let y := calldataload(0x1044) mstore(0x1340, y) success := and(validate_ec_point(x, y), success) }mstore(0x1360, keccak256(0x1300, 96)){ let hash := mload(0x1360) mstore(0x1380, mod(hash, f_q)) mstore(0x13a0, hash) } { let x := calldataload(0x1064) mstore(0x13c0, x) let y := calldataload(0x1084) mstore(0x13e0, y) success := and(validate_ec_point(x, y), success) }mstore(0x1400, mulmod(mload(0x8a0), mload(0x8a0), f_q))mstore(0x1420, mulmod(mload(0x1400), mload(0x1400), f_q))mstore(0x1440, mulmod(mload(0x1420), mload(0x1420), f_q))mstore(0x1460, mulmod(mload(0x1440), mload(0x1440), f_q))mstore(0x1480, mulmod(mload(0x1460), mload(0x1460), f_q))mstore(0x14a0, mulmod(mload(0x1480), mload(0x1480), f_q))mstore(0x14c0, mulmod(mload(0x14a0), mload(0x14a0), f_q))mstore(0x14e0, mulmod(mload(0x14c0), mload(0x14c0), f_q))mstore(0x1500, mulmod(mload(0x14e0), mload(0x14e0), f_q))mstore(0x1520, mulmod(mload(0x1500), mload(0x1500), f_q))mstore(0x1540, mulmod(mload(0x1520), mload(0x1520), f_q))mstore(0x1560, mulmod(mload(0x1540), mload(0x1540), f_q))mstore(0x1580, mulmod(mload(0x1560), mload(0x1560), f_q))mstore(0x15a0, mulmod(mload(0x1580), mload(0x1580), f_q))mstore(0x15c0, mulmod(mload(0x15a0), mload(0x15a0), f_q))mstore(0x15e0, mulmod(mload(0x15c0), mload(0x15c0), f_q))mstore(0x1600, mulmod(mload(0x15e0), mload(0x15e0), f_q))mstore(0x1620, mulmod(mload(0x1600), mload(0x1600), f_q))mstore(0x1640, mulmod(mload(0x1620), mload(0x1620), f_q))mstore(0x1660, mulmod(mload(0x1640), mload(0x1640), f_q))mstore(0x1680, mulmod(mload(0x1660), mload(0x1660), f_q))mstore(0x16a0, mulmod(mload(0x1680), mload(0x1680), f_q))mstore(0x16c0, addmod(mload(0x16a0), 21888242871839275222246405745257275088548364400416034343698204186575808495616, f_q))mstore(0x16e0, mulmod(mload(0x16c0), 21888237653275510688422624196183639687472264873923820041627027729598873448513, f_q))mstore(0x1700, mulmod(mload(0x16e0), 13225785879531581993054172815365636627224369411478295502904397545373139154045, f_q))mstore(0x1720, addmod(mload(0x8a0), 8662456992307693229192232929891638461323994988937738840793806641202669341572, f_q))mstore(0x1740, mulmod(mload(0x16e0), 10939663269433627367777756708678102241564365262857670666700619874077960926249, f_q))mstore(0x1760, addmod(mload(0x8a0), 10948579602405647854468649036579172846983999137558363676997584312497847569368, f_q))mstore(0x1780, mulmod(mload(0x16e0), 11016257578652593686382655500910603527869149377564754001549454008164059876499, f_q))mstore(0x17a0, addmod(mload(0x8a0), 10871985293186681535863750244346671560679215022851280342148750178411748619118, f_q))mstore(0x17c0, mulmod(mload(0x16e0), 15402826414547299628414612080036060696555554914079673875872749760617770134879, f_q))mstore(0x17e0, addmod(mload(0x8a0), 6485416457291975593831793665221214391992809486336360467825454425958038360738, f_q))mstore(0x1800, mulmod(mload(0x16e0), 21710372849001950800533397158415938114909991150039389063546734567764856596059, f_q))mstore(0x1820, addmod(mload(0x8a0), 177870022837324421713008586841336973638373250376645280151469618810951899558, f_q))mstore(0x1840, mulmod(mload(0x16e0), 2785514556381676080176937710880804108647911392478702105860685610379369825016, f_q))mstore(0x1860, addmod(mload(0x8a0), 19102728315457599142069468034376470979900453007937332237837518576196438670601, f_q))mstore(0x1880, mulmod(mload(0x16e0), 8734126352828345679573237859165904705806588461301144420590422589042130041188, f_q))mstore(0x18a0, addmod(mload(0x8a0), 13154116519010929542673167886091370382741775939114889923107781597533678454429, f_q))mstore(0x18c0, mulmod(mload(0x16e0), 1, f_q))mstore(0x18e0, addmod(mload(0x8a0), 21888242871839275222246405745257275088548364400416034343698204186575808495616, f_q)){ let prod := mload(0x1720) prod := mulmod(mload(0x1760), prod, f_q) mstore(0x1900, prod) prod := mulmod(mload(0x17a0), prod, f_q) mstore(0x1920, prod) prod := mulmod(mload(0x17e0), prod, f_q) mstore(0x1940, prod) prod := mulmod(mload(0x1820), prod, f_q) mstore(0x1960, prod) prod := mulmod(mload(0x1860), prod, f_q) mstore(0x1980, prod) prod := mulmod(mload(0x18a0), prod, f_q) mstore(0x19a0, prod) prod := mulmod(mload(0x18e0), prod, f_q) mstore(0x19c0, prod) prod := mulmod(mload(0x16c0), prod, f_q) mstore(0x19e0, prod) }mstore(0x1a20, 32)mstore(0x1a40, 32)mstore(0x1a60, 32)mstore(0x1a80, mload(0x19e0))mstore(0x1aa0, 21888242871839275222246405745257275088548364400416034343698204186575808495615)mstore(0x1ac0, 21888242871839275222246405745257275088548364400416034343698204186575808495617)success := and(eq(staticcall(gas(), 0x5, 0x1a20, 0xc0, 0x1a00, 0x20), 1), success){ let inv := mload(0x1a00) let v v := mload(0x16c0) mstore(0x16c0, mulmod(mload(0x19c0), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x18e0) mstore(0x18e0, mulmod(mload(0x19a0), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x18a0) mstore(0x18a0, mulmod(mload(0x1980), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x1860) mstore(0x1860, mulmod(mload(0x1960), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x1820) mstore(0x1820, mulmod(mload(0x1940), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x17e0) mstore(0x17e0, mulmod(mload(0x1920), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x17a0) mstore(0x17a0, mulmod(mload(0x1900), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x1760) mstore(0x1760, mulmod(mload(0x1720), inv, f_q)) inv := mulmod(v, inv, f_q) mstore(0x1720, inv) }mstore(0x1ae0, mulmod(mload(0x1700), mload(0x1720), f_q))mstore(0x1b00, mulmod(mload(0x1740), mload(0x1760), f_q))mstore(0x1b20, mulmod(mload(0x1780), mload(0x17a0), f_q))mstore(0x1b40, mulmod(mload(0x17c0), mload(0x17e0), f_q))mstore(0x1b60, mulmod(mload(0x1800), mload(0x1820), f_q))mstore(0x1b80, mulmod(mload(0x1840), mload(0x1860), f_q))mstore(0x1ba0, mulmod(mload(0x1880), mload(0x18a0), f_q))mstore(0x1bc0, mulmod(mload(0x18c0), mload(0x18e0), f_q)){ let result := mulmod(mload(0x1bc0), mload(0xa0), f_q)mstore(0x1be0, result) }mstore(0x1c00, mulmod(mload(0x920), mload(0x900), f_q))mstore(0x1c20, addmod(mload(0x8e0), mload(0x1c00), f_q))mstore(0x1c40, addmod(mload(0x1c20), sub(f_q, mload(0x940)), f_q))mstore(0x1c60, mulmod(mload(0x1c40), mload(0xcc0), f_q))mstore(0x1c80, mulmod(mload(0x780), mload(0x1c60), f_q))mstore(0x1ca0, mulmod(mload(0x9a0), mload(0x980), f_q))mstore(0x1cc0, addmod(mload(0x960), mload(0x1ca0), f_q))mstore(0x1ce0, addmod(mload(0x1cc0), sub(f_q, mload(0x9c0)), f_q))mstore(0x1d00, mulmod(mload(0x1ce0), mload(0xce0), f_q))mstore(0x1d20, addmod(mload(0x1c80), mload(0x1d00), f_q))mstore(0x1d40, mulmod(mload(0x780), mload(0x1d20), f_q))mstore(0x1d60, mulmod(mload(0xa20), mload(0xa00), f_q))mstore(0x1d80, addmod(mload(0x9e0), mload(0x1d60), f_q))mstore(0x1da0, addmod(mload(0x1d80), sub(f_q, mload(0xa40)), f_q))mstore(0x1dc0, mulmod(mload(0x1da0), mload(0xd00), f_q))mstore(0x1de0, addmod(mload(0x1d40), mload(0x1dc0), f_q))mstore(0x1e00, mulmod(mload(0x780), mload(0x1de0), f_q))mstore(0x1e20, mulmod(mload(0xaa0), mload(0xa80), f_q))mstore(0x1e40, addmod(mload(0xa60), mload(0x1e20), f_q))mstore(0x1e60, addmod(mload(0x1e40), sub(f_q, mload(0xac0)), f_q))mstore(0x1e80, mulmod(mload(0x1e60), mload(0xd20), f_q))mstore(0x1ea0, addmod(mload(0x1e00), mload(0x1e80), f_q))mstore(0x1ec0, mulmod(mload(0x780), mload(0x1ea0), f_q))mstore(0x1ee0, mulmod(mload(0xb20), mload(0xb00), f_q))mstore(0x1f00, addmod(mload(0xae0), mload(0x1ee0), f_q))mstore(0x1f20, addmod(mload(0x1f00), sub(f_q, mload(0xb40)), f_q))mstore(0x1f40, mulmod(mload(0x1f20), mload(0xd40), f_q))mstore(0x1f60, addmod(mload(0x1ec0), mload(0x1f40), f_q))mstore(0x1f80, mulmod(mload(0x780), mload(0x1f60), f_q))mstore(0x1fa0, mulmod(mload(0xba0), mload(0xb80), f_q))mstore(0x1fc0, addmod(mload(0xb60), mload(0x1fa0), f_q))mstore(0x1fe0, addmod(mload(0x1fc0), sub(f_q, mload(0xbc0)), f_q))mstore(0x2000, mulmod(mload(0x1fe0), mload(0xd60), f_q))mstore(0x2020, addmod(mload(0x1f80), mload(0x2000), f_q))mstore(0x2040, mulmod(mload(0x780), mload(0x2020), f_q))mstore(0x2060, addmod(1, sub(f_q, mload(0xf00)), f_q))mstore(0x2080, mulmod(mload(0x2060), mload(0x1bc0), f_q))mstore(0x20a0, addmod(mload(0x2040), mload(0x2080), f_q))mstore(0x20c0, mulmod(mload(0x780), mload(0x20a0), f_q))mstore(0x20e0, mulmod(mload(0x10e0), mload(0x10e0), f_q))mstore(0x2100, addmod(mload(0x20e0), sub(f_q, mload(0x10e0)), f_q))mstore(0x2120, mulmod(mload(0x2100), mload(0x1ae0), f_q))mstore(0x2140, addmod(mload(0x20c0), mload(0x2120), f_q))mstore(0x2160, mulmod(mload(0x780), mload(0x2140), f_q))mstore(0x2180, addmod(mload(0xf60), sub(f_q, mload(0xf40)), f_q))mstore(0x21a0, mulmod(mload(0x2180), mload(0x1bc0), f_q))mstore(0x21c0, addmod(mload(0x2160), mload(0x21a0), f_q))mstore(0x21e0, mulmod(mload(0x780), mload(0x21c0), f_q))mstore(0x2200, addmod(mload(0xfc0), sub(f_q, mload(0xfa0)), f_q))mstore(0x2220, mulmod(mload(0x2200), mload(0x1bc0), f_q))mstore(0x2240, addmod(mload(0x21e0), mload(0x2220), f_q))mstore(0x2260, mulmod(mload(0x780), mload(0x2240), f_q))mstore(0x2280, addmod(mload(0x1020), sub(f_q, mload(0x1000)), f_q))mstore(0x22a0, mulmod(mload(0x2280), mload(0x1bc0), f_q))mstore(0x22c0, addmod(mload(0x2260), mload(0x22a0), f_q))mstore(0x22e0, mulmod(mload(0x780), mload(0x22c0), f_q))mstore(0x2300, addmod(mload(0x1080), sub(f_q, mload(0x1060)), f_q))mstore(0x2320, mulmod(mload(0x2300), mload(0x1bc0), f_q))mstore(0x2340, addmod(mload(0x22e0), mload(0x2320), f_q))mstore(0x2360, mulmod(mload(0x780), mload(0x2340), f_q))mstore(0x2380, addmod(mload(0x10e0), sub(f_q, mload(0x10c0)), f_q))mstore(0x23a0, mulmod(mload(0x2380), mload(0x1bc0), f_q))mstore(0x23c0, addmod(mload(0x2360), mload(0x23a0), f_q))mstore(0x23e0, mulmod(mload(0x780), mload(0x23c0), f_q))mstore(0x2400, addmod(1, sub(f_q, mload(0x1ae0)), f_q))mstore(0x2420, addmod(mload(0x1b00), mload(0x1b20), f_q))mstore(0x2440, addmod(mload(0x2420), mload(0x1b40), f_q))mstore(0x2460, addmod(mload(0x2440), mload(0x1b60), f_q))mstore(0x2480, addmod(mload(0x2460), mload(0x1b80), f_q))mstore(0x24a0, addmod(mload(0x2480), mload(0x1ba0), f_q))mstore(0x24c0, addmod(mload(0x2400), sub(f_q, mload(0x24a0)), f_q))mstore(0x24e0, mulmod(mload(0xda0), mload(0x480), f_q))mstore(0x2500, addmod(mload(0xc40), mload(0x24e0), f_q))mstore(0x2520, addmod(mload(0x2500), mload(0x4e0), f_q))mstore(0x2540, mulmod(mload(0xdc0), mload(0x480), f_q))mstore(0x2560, addmod(mload(0x8e0), mload(0x2540), f_q))mstore(0x2580, addmod(mload(0x2560), mload(0x4e0), f_q))mstore(0x25a0, mulmod(mload(0x2580), mload(0x2520), f_q))mstore(0x25c0, mulmod(mload(0x25a0), mload(0xf20), f_q))mstore(0x25e0, mulmod(1, mload(0x480), f_q))mstore(0x2600, mulmod(mload(0x8a0), mload(0x25e0), f_q))mstore(0x2620, addmod(mload(0xc40), mload(0x2600), f_q))mstore(0x2640, addmod(mload(0x2620), mload(0x4e0), f_q))mstore(0x2660, mulmod(4131629893567559867359510883348571134090853742863529169391034518566172092834, mload(0x480), f_q))mstore(0x2680, mulmod(mload(0x8a0), mload(0x2660), f_q))mstore(0x26a0, addmod(mload(0x8e0), mload(0x2680), f_q))mstore(0x26c0, addmod(mload(0x26a0), mload(0x4e0), f_q))mstore(0x26e0, mulmod(mload(0x26c0), mload(0x2640), f_q))mstore(0x2700, mulmod(mload(0x26e0), mload(0xf00), f_q))mstore(0x2720, addmod(mload(0x25c0), sub(f_q, mload(0x2700)), f_q))mstore(0x2740, mulmod(mload(0x2720), mload(0x24c0), f_q))mstore(0x2760, addmod(mload(0x23e0), mload(0x2740), f_q))mstore(0x2780, mulmod(mload(0x780), mload(0x2760), f_q))mstore(0x27a0, mulmod(mload(0xde0), mload(0x480), f_q))mstore(0x27c0, addmod(mload(0x960), mload(0x27a0), f_q))mstore(0x27e0, addmod(mload(0x27c0), mload(0x4e0), f_q))mstore(0x2800, mulmod(mload(0xe00), mload(0x480), f_q))mstore(0x2820, addmod(mload(0x9e0), mload(0x2800), f_q))mstore(0x2840, addmod(mload(0x2820), mload(0x4e0), f_q))mstore(0x2860, mulmod(mload(0x2840), mload(0x27e0), f_q))mstore(0x2880, mulmod(mload(0x2860), mload(0xf80), f_q))mstore(0x28a0, mulmod(8910878055287538404433155982483128285667088683464058436815641868457422632747, mload(0x480), f_q))mstore(0x28c0, mulmod(mload(0x8a0), mload(0x28a0), f_q))mstore(0x28e0, addmod(mload(0x960), mload(0x28c0), f_q))mstore(0x2900, addmod(mload(0x28e0), mload(0x4e0), f_q))mstore(0x2920, mulmod(11166246659983828508719468090013646171463329086121580628794302409516816350802, mload(0x480), f_q))mstore(0x2940, mulmod(mload(0x8a0), mload(0x2920), f_q))mstore(0x2960, addmod(mload(0x9e0), mload(0x2940), f_q))mstore(0x2980, addmod(mload(0x2960), mload(0x4e0), f_q))mstore(0x29a0, mulmod(mload(0x2980), mload(0x2900), f_q))mstore(0x29c0, mulmod(mload(0x29a0), mload(0xf60), f_q))mstore(0x29e0, addmod(mload(0x2880), sub(f_q, mload(0x29c0)), f_q))mstore(0x2a00, mulmod(mload(0x29e0), mload(0x24c0), f_q))mstore(0x2a20, addmod(mload(0x2780), mload(0x2a00), f_q))mstore(0x2a40, mulmod(mload(0x780), mload(0x2a20), f_q))mstore(0x2a60, mulmod(mload(0xe20), mload(0x480), f_q))mstore(0x2a80, addmod(mload(0xa60), mload(0x2a60), f_q))mstore(0x2aa0, addmod(mload(0x2a80), mload(0x4e0), f_q))mstore(0x2ac0, mulmod(mload(0xe40), mload(0x480), f_q))mstore(0x2ae0, addmod(mload(0xae0), mload(0x2ac0), f_q))mstore(0x2b00, addmod(mload(0x2ae0), mload(0x4e0), f_q))mstore(0x2b20, mulmod(mload(0x2b00), mload(0x2aa0), f_q))mstore(0x2b40, mulmod(mload(0x2b20), mload(0xfe0), f_q))mstore(0x2b60, mulmod(284840088355319032285349970403338060113257071685626700086398481893096618818, mload(0x480), f_q))mstore(0x2b80, mulmod(mload(0x8a0), mload(0x2b60), f_q))mstore(0x2ba0, addmod(mload(0xa60), mload(0x2b80), f_q))mstore(0x2bc0, addmod(mload(0x2ba0), mload(0x4e0), f_q))mstore(0x2be0, mulmod(21134065618345176623193549882539580312263652408302468683943992798037078993309, mload(0x480), f_q))mstore(0x2c00, mulmod(mload(0x8a0), mload(0x2be0), f_q))mstore(0x2c20, addmod(mload(0xae0), mload(0x2c00), f_q))mstore(0x2c40, addmod(mload(0x2c20), mload(0x4e0), f_q))mstore(0x2c60, mulmod(mload(0x2c40), mload(0x2bc0), f_q))mstore(0x2c80, mulmod(mload(0x2c60), mload(0xfc0), f_q))mstore(0x2ca0, addmod(mload(0x2b40), sub(f_q, mload(0x2c80)), f_q))mstore(0x2cc0, mulmod(mload(0x2ca0), mload(0x24c0), f_q))mstore(0x2ce0, addmod(mload(0x2a40), mload(0x2cc0), f_q))mstore(0x2d00, mulmod(mload(0x780), mload(0x2ce0), f_q))mstore(0x2d20, mulmod(mload(0xe60), mload(0x480), f_q))mstore(0x2d40, addmod(mload(0xb60), mload(0x2d20), f_q))mstore(0x2d60, addmod(mload(0x2d40), mload(0x4e0), f_q))mstore(0x2d80, mulmod(mload(0xe80), mload(0x480), f_q))mstore(0x2da0, addmod(mload(0xbe0), mload(0x2d80), f_q))mstore(0x2dc0, addmod(mload(0x2da0), mload(0x4e0), f_q))mstore(0x2de0, mulmod(mload(0x2dc0), mload(0x2d60), f_q))mstore(0x2e00, mulmod(mload(0x2de0), mload(0x1040), f_q))mstore(0x2e20, mulmod(5625741653535312224677218588085279924365897425605943700675464992185016992283, mload(0x480), f_q))mstore(0x2e40, mulmod(mload(0x8a0), mload(0x2e20), f_q))mstore(0x2e60, addmod(mload(0xb60), mload(0x2e40), f_q))mstore(0x2e80, addmod(mload(0x2e60), mload(0x4e0), f_q))mstore(0x2ea0, mulmod(14704729814417906439424896605881467874595262020190401576785074330126828718155, mload(0x480), f_q))mstore(0x2ec0, mulmod(mload(0x8a0), mload(0x2ea0), f_q))mstore(0x2ee0, addmod(mload(0xbe0), mload(0x2ec0), f_q))mstore(0x2f00, addmod(mload(0x2ee0), mload(0x4e0), f_q))mstore(0x2f20, mulmod(mload(0x2f00), mload(0x2e80), f_q))mstore(0x2f40, mulmod(mload(0x2f20), mload(0x1020), f_q))mstore(0x2f60, addmod(mload(0x2e00), sub(f_q, mload(0x2f40)), f_q))mstore(0x2f80, mulmod(mload(0x2f60), mload(0x24c0), f_q))mstore(0x2fa0, addmod(mload(0x2d00), mload(0x2f80), f_q))mstore(0x2fc0, mulmod(mload(0x780), mload(0x2fa0), f_q))mstore(0x2fe0, mulmod(mload(0xea0), mload(0x480), f_q))mstore(0x3000, addmod(mload(0xc00), mload(0x2fe0), f_q))mstore(0x3020, addmod(mload(0x3000), mload(0x4e0), f_q))mstore(0x3040, mulmod(mload(0xec0), mload(0x480), f_q))mstore(0x3060, addmod(mload(0xc20), mload(0x3040), f_q))mstore(0x3080, addmod(mload(0x3060), mload(0x4e0), f_q))mstore(0x30a0, mulmod(mload(0x3080), mload(0x3020), f_q))mstore(0x30c0, mulmod(mload(0x30a0), mload(0x10a0), f_q))mstore(0x30e0, mulmod(8343274462013750416000956870576256937330525306073862550863787263304548803879, mload(0x480), f_q))mstore(0x3100, mulmod(mload(0x8a0), mload(0x30e0), f_q))mstore(0x3120, addmod(mload(0xc00), mload(0x3100), f_q))mstore(0x3140, addmod(mload(0x3120), mload(0x4e0), f_q))mstore(0x3160, mulmod(20928372310071051017340352686640453451620397549739756658327314209761852842004, mload(0x480), f_q))mstore(0x3180, mulmod(mload(0x8a0), mload(0x3160), f_q))mstore(0x31a0, addmod(mload(0xc20), mload(0x3180), f_q))mstore(0x31c0, addmod(mload(0x31a0), mload(0x4e0), f_q))mstore(0x31e0, mulmod(mload(0x31c0), mload(0x3140), f_q))mstore(0x3200, mulmod(mload(0x31e0), mload(0x1080), f_q))mstore(0x3220, addmod(mload(0x30c0), sub(f_q, mload(0x3200)), f_q))mstore(0x3240, mulmod(mload(0x3220), mload(0x24c0), f_q))mstore(0x3260, addmod(mload(0x2fc0), mload(0x3240), f_q))mstore(0x3280, mulmod(mload(0x780), mload(0x3260), f_q))mstore(0x32a0, mulmod(mload(0xee0), mload(0x480), f_q))mstore(0x32c0, addmod(mload(0x1be0), mload(0x32a0), f_q))mstore(0x32e0, addmod(mload(0x32c0), mload(0x4e0), f_q))mstore(0x3300, mulmod(mload(0x32e0), mload(0x1100), f_q))mstore(0x3320, mulmod(15845651941796975697993789271154426079663327509658641548785793587449119139335, mload(0x480), f_q))mstore(0x3340, mulmod(mload(0x8a0), mload(0x3320), f_q))mstore(0x3360, addmod(mload(0x1be0), mload(0x3340), f_q))mstore(0x3380, addmod(mload(0x3360), mload(0x4e0), f_q))mstore(0x33a0, mulmod(mload(0x3380), mload(0x10e0), f_q))mstore(0x33c0, addmod(mload(0x3300), sub(f_q, mload(0x33a0)), f_q))mstore(0x33e0, mulmod(mload(0x33c0), mload(0x24c0), f_q))mstore(0x3400, addmod(mload(0x3280), mload(0x33e0), f_q))mstore(0x3420, mulmod(mload(0x780), mload(0x3400), f_q))mstore(0x3440, addmod(1, sub(f_q, mload(0x1120)), f_q))mstore(0x3460, mulmod(mload(0x3440), mload(0x1bc0), f_q))mstore(0x3480, addmod(mload(0x3420), mload(0x3460), f_q))mstore(0x34a0, mulmod(mload(0x780), mload(0x3480), f_q))mstore(0x34c0, mulmod(mload(0x1120), mload(0x1120), f_q))mstore(0x34e0, addmod(mload(0x34c0), sub(f_q, mload(0x1120)), f_q))mstore(0x3500, mulmod(mload(0x34e0), mload(0x1ae0), f_q))mstore(0x3520, addmod(mload(0x34a0), mload(0x3500), f_q))mstore(0x3540, mulmod(mload(0x780), mload(0x3520), f_q))mstore(0x3560, addmod(mload(0x1160), mload(0x480), f_q))mstore(0x3580, mulmod(mload(0x3560), mload(0x1140), f_q))mstore(0x35a0, addmod(mload(0x11a0), mload(0x4e0), f_q))mstore(0x35c0, mulmod(mload(0x35a0), mload(0x3580), f_q))mstore(0x35e0, addmod(mload(0xbe0), mload(0x480), f_q))mstore(0x3600, mulmod(mload(0x35e0), mload(0x1120), f_q))mstore(0x3620, addmod(mload(0xc60), mload(0x4e0), f_q))mstore(0x3640, mulmod(mload(0x3620), mload(0x3600), f_q))mstore(0x3660, addmod(mload(0x35c0), sub(f_q, mload(0x3640)), f_q))mstore(0x3680, mulmod(mload(0x3660), mload(0x24c0), f_q))mstore(0x36a0, addmod(mload(0x3540), mload(0x3680), f_q))mstore(0x36c0, mulmod(mload(0x780), mload(0x36a0), f_q))mstore(0x36e0, addmod(mload(0x1160), sub(f_q, mload(0x11a0)), f_q))mstore(0x3700, mulmod(mload(0x36e0), mload(0x1bc0), f_q))mstore(0x3720, addmod(mload(0x36c0), mload(0x3700), f_q))mstore(0x3740, mulmod(mload(0x780), mload(0x3720), f_q))mstore(0x3760, mulmod(mload(0x36e0), mload(0x24c0), f_q))mstore(0x3780, addmod(mload(0x1160), sub(f_q, mload(0x1180)), f_q))mstore(0x37a0, mulmod(mload(0x3780), mload(0x3760), f_q))mstore(0x37c0, addmod(mload(0x3740), mload(0x37a0), f_q))mstore(0x37e0, mulmod(mload(0x780), mload(0x37c0), f_q))mstore(0x3800, addmod(1, sub(f_q, mload(0x11c0)), f_q))mstore(0x3820, mulmod(mload(0x3800), mload(0x1bc0), f_q))mstore(0x3840, addmod(mload(0x37e0), mload(0x3820), f_q))mstore(0x3860, mulmod(mload(0x780), mload(0x3840), f_q))mstore(0x3880, mulmod(mload(0x11c0), mload(0x11c0), f_q))mstore(0x38a0, addmod(mload(0x3880), sub(f_q, mload(0x11c0)), f_q))mstore(0x38c0, mulmod(mload(0x38a0), mload(0x1ae0), f_q))mstore(0x38e0, addmod(mload(0x3860), mload(0x38c0), f_q))mstore(0x3900, mulmod(mload(0x780), mload(0x38e0), f_q))mstore(0x3920, addmod(mload(0x1200), mload(0x480), f_q))mstore(0x3940, mulmod(mload(0x3920), mload(0x11e0), f_q))mstore(0x3960, addmod(mload(0x1240), mload(0x4e0), f_q))mstore(0x3980, mulmod(mload(0x3960), mload(0x3940), f_q))mstore(0x39a0, mulmod(mload(0x320), mload(0xc00), f_q))mstore(0x39c0, addmod(mload(0x39a0), mload(0xc20), f_q))mstore(0x39e0, addmod(mload(0x39c0), mload(0x480), f_q))mstore(0x3a00, mulmod(mload(0x39e0), mload(0x11c0), f_q))mstore(0x3a20, mulmod(mload(0x320), mload(0xc80), f_q))mstore(0x3a40, addmod(mload(0x3a20), mload(0xca0), f_q))mstore(0x3a60, addmod(mload(0x3a40), mload(0x4e0), f_q))mstore(0x3a80, mulmod(mload(0x3a60), mload(0x3a00), f_q))mstore(0x3aa0, addmod(mload(0x3980), sub(f_q, mload(0x3a80)), f_q))mstore(0x3ac0, mulmod(mload(0x3aa0), mload(0x24c0), f_q))mstore(0x3ae0, addmod(mload(0x3900), mload(0x3ac0), f_q))mstore(0x3b00, mulmod(mload(0x780), mload(0x3ae0), f_q))mstore(0x3b20, addmod(mload(0x1200), sub(f_q, mload(0x1240)), f_q))mstore(0x3b40, mulmod(mload(0x3b20), mload(0x1bc0), f_q))mstore(0x3b60, addmod(mload(0x3b00), mload(0x3b40), f_q))mstore(0x3b80, mulmod(mload(0x780), mload(0x3b60), f_q))mstore(0x3ba0, mulmod(mload(0x3b20), mload(0x24c0), f_q))mstore(0x3bc0, addmod(mload(0x1200), sub(f_q, mload(0x1220)), f_q))mstore(0x3be0, mulmod(mload(0x3bc0), mload(0x3ba0), f_q))mstore(0x3c00, addmod(mload(0x3b80), mload(0x3be0), f_q))mstore(0x3c20, mulmod(mload(0x16a0), mload(0x16a0), f_q))mstore(0x3c40, mulmod(mload(0x3c20), mload(0x16a0), f_q))mstore(0x3c60, mulmod(1, mload(0x16a0), f_q))mstore(0x3c80, mulmod(1, mload(0x3c20), f_q))mstore(0x3ca0, mulmod(mload(0x3c00), mload(0x16c0), f_q))mstore(0x3cc0, mulmod(mload(0x1400), mload(0x8a0), f_q))mstore(0x3ce0, mulmod(mload(0x3cc0), mload(0x8a0), f_q))mstore(0x3d00, mulmod(mload(0x8a0), 1, f_q))mstore(0x3d20, addmod(mload(0x1380), sub(f_q, mload(0x3d00)), f_q))mstore(0x3d40, mulmod(mload(0x8a0), 1426404432721484388505361748317961535523355871255605456897797744433766488507, f_q))mstore(0x3d60, addmod(mload(0x1380), sub(f_q, mload(0x3d40)), f_q))mstore(0x3d80, mulmod(mload(0x8a0), 8734126352828345679573237859165904705806588461301144420590422589042130041188, f_q))mstore(0x3da0, addmod(mload(0x1380), sub(f_q, mload(0x3d80)), f_q))mstore(0x3dc0, mulmod(mload(0x8a0), 11211301017135681023579411905410872569206244553457844956874280139879520583390, f_q))mstore(0x3de0, addmod(mload(0x1380), sub(f_q, mload(0x3dc0)), f_q))mstore(0x3e00, mulmod(mload(0x8a0), 12619617507853212586156872920672483948819476989779550311307282715684870266992, f_q))mstore(0x3e20, addmod(mload(0x1380), sub(f_q, mload(0x3e00)), f_q))mstore(0x3e40, mulmod(mload(0x8a0), 13225785879531581993054172815365636627224369411478295502904397545373139154045, f_q))mstore(0x3e60, addmod(mload(0x1380), sub(f_q, mload(0x3e40)), f_q)){ let result := mulmod(mload(0x1380), mulmod(mload(0x3cc0), 3544324119167359571073009690693121464267965232733679586767649244433889388945, f_q), f_q)result := addmod(mulmod(mload(0x8a0), mulmod(mload(0x3cc0), 18343918752671915651173396054564153624280399167682354756930554942141919106672, f_q), f_q), result, f_q)mstore(0x3e80, result) }{ let result := mulmod(mload(0x1380), mulmod(mload(0x3cc0), 3860370625838117017501327045244227871206764201116468958063324100051382735289, f_q), f_q)result := addmod(mulmod(mload(0x8a0), mulmod(mload(0x3cc0), 21616901807277407275624036604424346159916096890712898844034238973395610537327, f_q), f_q), result, f_q)mstore(0x3ea0, result) }{ let result := mulmod(mload(0x1380), mulmod(mload(0x3cc0), 21616901807277407275624036604424346159916096890712898844034238973395610537327, f_q), f_q)result := addmod(mulmod(mload(0x8a0), mulmod(mload(0x3cc0), 889236556954614024749610889108815341999962898269585485843658889664869519176, f_q), f_q), result, f_q)mstore(0x3ec0, result) }{ let result := mulmod(mload(0x1380), mulmod(mload(0x3cc0), 3209408481237076479025468386201293941554240476766691830436732310949352383503, f_q), f_q)result := addmod(mulmod(mload(0x8a0), mulmod(mload(0x3cc0), 12080394110851700286656425387058292751221637853580771255128961096834426654570, f_q), f_q), result, f_q)mstore(0x3ee0, result) }mstore(0x3f00, mulmod(1, mload(0x3d20), f_q))mstore(0x3f20, mulmod(mload(0x3f00), mload(0x3de0), f_q))mstore(0x3f40, mulmod(mload(0x3f20), mload(0x3d60), f_q))mstore(0x3f60, mulmod(mload(0x3f40), mload(0x3e20), f_q)){ let result := mulmod(mload(0x1380), 1, f_q)result := addmod(mulmod(mload(0x8a0), 21888242871839275222246405745257275088548364400416034343698204186575808495616, f_q), result, f_q)mstore(0x3f80, result) }{ let result := mulmod(mload(0x1380), mulmod(mload(0x1400), 8390819244605639573390577733158868133682115698337564550620146375401109684432, f_q), f_q)result := addmod(mulmod(mload(0x8a0), mulmod(mload(0x1400), 13497423627233635648855828012098406954866248702078469793078057811174698811185, f_q), f_q), result, f_q)mstore(0x3fa0, result) }{ let result := mulmod(mload(0x1380), mulmod(mload(0x1400), 14389468897523033212448771694851898440525479866834419679925499462425232628530, f_q), f_q)result := addmod(mulmod(mload(0x8a0), mulmod(mload(0x1400), 10771624105926513343199793365135253961557027396599172824137553349410803667382, f_q), f_q), result, f_q)mstore(0x3fc0, result) }{ let result := mulmod(mload(0x1380), mulmod(mload(0x1400), 8021781111580269725587432039983408559403601261632071736490564397134126857583, f_q), f_q)result := addmod(mulmod(mload(0x8a0), mulmod(mload(0x1400), 13263758384809315129424392494083758423780924407584659157289746760747196496964, f_q), f_q), result, f_q)mstore(0x3fe0, result) }mstore(0x4000, mulmod(mload(0x3f20), mload(0x3e60), f_q)){ let result := mulmod(mload(0x1380), mulmod(mload(0x8a0), 10676941854703594198666993839846402519342119846958189386823924046696287912228, f_q), f_q)result := addmod(mulmod(mload(0x8a0), mulmod(mload(0x8a0), 11211301017135681023579411905410872569206244553457844956874280139879520583389, f_q), f_q), result, f_q)mstore(0x4020, result) }{ let result := mulmod(mload(0x1380), mulmod(mload(0x8a0), 11211301017135681023579411905410872569206244553457844956874280139879520583389, f_q), f_q)result := addmod(mulmod(mload(0x8a0), mulmod(mload(0x8a0), 9784896584414196635074050157092911033682888682202239499976482395445754094883, f_q), f_q), result, f_q)mstore(0x4040, result) }{ let result := mulmod(mload(0x1380), mulmod(mload(0x8a0), 13154116519010929542673167886091370382741775939114889923107781597533678454430, f_q), f_q)result := addmod(mulmod(mload(0x8a0), mulmod(mload(0x8a0), 8734126352828345679573237859165904705806588461301144420590422589042130041187, f_q), f_q), result, f_q)mstore(0x4060, result) }{ let result := mulmod(mload(0x1380), mulmod(mload(0x8a0), 8734126352828345679573237859165904705806588461301144420590422589042130041187, f_q), f_q)result := addmod(mulmod(mload(0x8a0), mulmod(mload(0x8a0), 5948611796446669599396300148285100597158677068822442314729736978662760216172, f_q), f_q), result, f_q)mstore(0x4080, result) }mstore(0x40a0, mulmod(mload(0x3f00), mload(0x3da0), f_q)){ let prod := mload(0x3e80) prod := mulmod(mload(0x3ea0), prod, f_q) mstore(0x40c0, prod) prod := mulmod(mload(0x3ec0), prod, f_q) mstore(0x40e0, prod) prod := mulmod(mload(0x3ee0), prod, f_q) mstore(0x4100, prod) prod := mulmod(mload(0x3f80), prod, f_q) mstore(0x4120, prod) prod := mulmod(mload(0x3f00), prod, f_q) mstore(0x4140, prod) prod := mulmod(mload(0x3fa0), prod, f_q) mstore(0x4160, prod) prod := mulmod(mload(0x3fc0), prod, f_q) mstore(0x4180, prod) prod := mulmod(mload(0x3fe0), prod, f_q) mstore(0x41a0, prod) prod := mulmod(mload(0x4000), prod, f_q) mstore(0x41c0, prod) prod := mulmod(mload(0x4020), prod, f_q) mstore(0x41e0, prod) prod := mulmod(mload(0x4040), prod, f_q) mstore(0x4200, prod) prod := mulmod(mload(0x3f20), prod, f_q) mstore(0x4220, prod) prod := mulmod(mload(0x4060), prod, f_q) mstore(0x4240, prod) prod := mulmod(mload(0x4080), prod, f_q) mstore(0x4260, prod) prod := mulmod(mload(0x40a0), prod, f_q) mstore(0x4280, prod) }mstore(0x42c0, 32)mstore(0x42e0, 32)mstore(0x4300, 32)mstore(0x4320, mload(0x4280))mstore(0x4340, 21888242871839275222246405745257275088548364400416034343698204186575808495615)mstore(0x4360, 21888242871839275222246405745257275088548364400416034343698204186575808495617)success := and(eq(staticcall(gas(), 0x5, 0x42c0, 0xc0, 0x42a0, 0x20), 1), success){ let inv := mload(0x42a0) let v v := mload(0x40a0) mstore(0x40a0, mulmod(mload(0x4260), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x4080) mstore(0x4080, mulmod(mload(0x4240), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x4060) mstore(0x4060, mulmod(mload(0x4220), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x3f20) mstore(0x3f20, mulmod(mload(0x4200), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x4040) mstore(0x4040, mulmod(mload(0x41e0), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x4020) mstore(0x4020, mulmod(mload(0x41c0), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x4000) mstore(0x4000, mulmod(mload(0x41a0), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x3fe0) mstore(0x3fe0, mulmod(mload(0x4180), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x3fc0) mstore(0x3fc0, mulmod(mload(0x4160), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x3fa0) mstore(0x3fa0, mulmod(mload(0x4140), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x3f00) mstore(0x3f00, mulmod(mload(0x4120), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x3f80) mstore(0x3f80, mulmod(mload(0x4100), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x3ee0) mstore(0x3ee0, mulmod(mload(0x40e0), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x3ec0) mstore(0x3ec0, mulmod(mload(0x40c0), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x3ea0) mstore(0x3ea0, mulmod(mload(0x3e80), inv, f_q)) inv := mulmod(v, inv, f_q) mstore(0x3e80, inv) }{ let result := mload(0x3e80)result := addmod(mload(0x3ea0), result, f_q)result := addmod(mload(0x3ec0), result, f_q)result := addmod(mload(0x3ee0), result, f_q)mstore(0x4380, result) }mstore(0x43a0, mulmod(mload(0x3f60), mload(0x3f00), f_q)){ let result := mload(0x3f80)mstore(0x43c0, result) }mstore(0x43e0, mulmod(mload(0x3f60), mload(0x4000), f_q)){ let result := mload(0x3fa0)result := addmod(mload(0x3fc0), result, f_q)result := addmod(mload(0x3fe0), result, f_q)mstore(0x4400, result) }mstore(0x4420, mulmod(mload(0x3f60), mload(0x3f20), f_q)){ let result := mload(0x4020)result := addmod(mload(0x4040), result, f_q)mstore(0x4440, result) }mstore(0x4460, mulmod(mload(0x3f60), mload(0x40a0), f_q)){ let result := mload(0x4060)result := addmod(mload(0x4080), result, f_q)mstore(0x4480, result) }{ let prod := mload(0x4380) prod := mulmod(mload(0x43c0), prod, f_q) mstore(0x44a0, prod) prod := mulmod(mload(0x4400), prod, f_q) mstore(0x44c0, prod) prod := mulmod(mload(0x4440), prod, f_q) mstore(0x44e0, prod) prod := mulmod(mload(0x4480), prod, f_q) mstore(0x4500, prod) }mstore(0x4540, 32)mstore(0x4560, 32)mstore(0x4580, 32)mstore(0x45a0, mload(0x4500))mstore(0x45c0, 21888242871839275222246405745257275088548364400416034343698204186575808495615)mstore(0x45e0, 21888242871839275222246405745257275088548364400416034343698204186575808495617)success := and(eq(staticcall(gas(), 0x5, 0x4540, 0xc0, 0x4520, 0x20), 1), success){ let inv := mload(0x4520) let v v := mload(0x4480) mstore(0x4480, mulmod(mload(0x44e0), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x4440) mstore(0x4440, mulmod(mload(0x44c0), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x4400) mstore(0x4400, mulmod(mload(0x44a0), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x43c0) mstore(0x43c0, mulmod(mload(0x4380), inv, f_q)) inv := mulmod(v, inv, f_q) mstore(0x4380, inv) }mstore(0x4600, mulmod(mload(0x43a0), mload(0x43c0), f_q))mstore(0x4620, mulmod(mload(0x43e0), mload(0x4400), f_q))mstore(0x4640, mulmod(mload(0x4420), mload(0x4440), f_q))mstore(0x4660, mulmod(mload(0x4460), mload(0x4480), f_q))mstore(0x4680, mulmod(mload(0x1280), mload(0x1280), f_q))mstore(0x46a0, mulmod(mload(0x4680), mload(0x1280), f_q))mstore(0x46c0, mulmod(mload(0x46a0), mload(0x1280), f_q))mstore(0x46e0, mulmod(mload(0x46c0), mload(0x1280), f_q))mstore(0x4700, mulmod(mload(0x46e0), mload(0x1280), f_q))mstore(0x4720, mulmod(mload(0x4700), mload(0x1280), f_q))mstore(0x4740, mulmod(mload(0x4720), mload(0x1280), f_q))mstore(0x4760, mulmod(mload(0x4740), mload(0x1280), f_q))mstore(0x4780, mulmod(mload(0x4760), mload(0x1280), f_q))mstore(0x47a0, mulmod(mload(0x4780), mload(0x1280), f_q))mstore(0x47c0, mulmod(mload(0x47a0), mload(0x1280), f_q))mstore(0x47e0, mulmod(mload(0x47c0), mload(0x1280), f_q))mstore(0x4800, mulmod(mload(0x47e0), mload(0x1280), f_q))mstore(0x4820, mulmod(mload(0x4800), mload(0x1280), f_q))mstore(0x4840, mulmod(mload(0x4820), mload(0x1280), f_q))mstore(0x4860, mulmod(mload(0x4840), mload(0x1280), f_q))mstore(0x4880, mulmod(mload(0x4860), mload(0x1280), f_q))mstore(0x48a0, mulmod(mload(0x4880), mload(0x1280), f_q))mstore(0x48c0, mulmod(mload(0x48a0), mload(0x1280), f_q))mstore(0x48e0, mulmod(mload(0x48c0), mload(0x1280), f_q))mstore(0x4900, mulmod(mload(0x48e0), mload(0x1280), f_q))mstore(0x4920, mulmod(mload(0x4900), mload(0x1280), f_q))mstore(0x4940, mulmod(mload(0x4920), mload(0x1280), f_q))mstore(0x4960, mulmod(mload(0x4940), mload(0x1280), f_q))mstore(0x4980, mulmod(mload(0x4960), mload(0x1280), f_q))mstore(0x49a0, mulmod(mload(0x4980), mload(0x1280), f_q))mstore(0x49c0, mulmod(mload(0x49a0), mload(0x1280), f_q))mstore(0x49e0, mulmod(mload(0x12e0), mload(0x12e0), f_q))mstore(0x4a00, mulmod(mload(0x49e0), mload(0x12e0), f_q))mstore(0x4a20, mulmod(mload(0x4a00), mload(0x12e0), f_q))mstore(0x4a40, mulmod(mload(0x4a20), mload(0x12e0), f_q)){ let result := mulmod(mload(0x8e0), mload(0x3e80), f_q)result := addmod(mulmod(mload(0x900), mload(0x3ea0), f_q), result, f_q)result := addmod(mulmod(mload(0x920), mload(0x3ec0), f_q), result, f_q)result := addmod(mulmod(mload(0x940), mload(0x3ee0), f_q), result, f_q)mstore(0x4a60, result) }mstore(0x4a80, mulmod(mload(0x4a60), mload(0x4380), f_q))mstore(0x4aa0, mulmod(sub(f_q, mload(0x4a80)), 1, f_q)){ let result := mulmod(mload(0x960), mload(0x3e80), f_q)result := addmod(mulmod(mload(0x980), mload(0x3ea0), f_q), result, f_q)result := addmod(mulmod(mload(0x9a0), mload(0x3ec0), f_q), result, f_q)result := addmod(mulmod(mload(0x9c0), mload(0x3ee0), f_q), result, f_q)mstore(0x4ac0, result) }mstore(0x4ae0, mulmod(mload(0x4ac0), mload(0x4380), f_q))mstore(0x4b00, mulmod(sub(f_q, mload(0x4ae0)), mload(0x1280), f_q))mstore(0x4b20, mulmod(1, mload(0x1280), f_q))mstore(0x4b40, addmod(mload(0x4aa0), mload(0x4b00), f_q)){ let result := mulmod(mload(0x9e0), mload(0x3e80), f_q)result := addmod(mulmod(mload(0xa00), mload(0x3ea0), f_q), result, f_q)result := addmod(mulmod(mload(0xa20), mload(0x3ec0), f_q), result, f_q)result := addmod(mulmod(mload(0xa40), mload(0x3ee0), f_q), result, f_q)mstore(0x4b60, result) }mstore(0x4b80, mulmod(mload(0x4b60), mload(0x4380), f_q))mstore(0x4ba0, mulmod(sub(f_q, mload(0x4b80)), mload(0x4680), f_q))mstore(0x4bc0, mulmod(1, mload(0x4680), f_q))mstore(0x4be0, addmod(mload(0x4b40), mload(0x4ba0), f_q)){ let result := mulmod(mload(0xa60), mload(0x3e80), f_q)result := addmod(mulmod(mload(0xa80), mload(0x3ea0), f_q), result, f_q)result := addmod(mulmod(mload(0xaa0), mload(0x3ec0), f_q), result, f_q)result := addmod(mulmod(mload(0xac0), mload(0x3ee0), f_q), result, f_q)mstore(0x4c00, result) }mstore(0x4c20, mulmod(mload(0x4c00), mload(0x4380), f_q))mstore(0x4c40, mulmod(sub(f_q, mload(0x4c20)), mload(0x46a0), f_q))mstore(0x4c60, mulmod(1, mload(0x46a0), f_q))mstore(0x4c80, addmod(mload(0x4be0), mload(0x4c40), f_q)){ let result := mulmod(mload(0xae0), mload(0x3e80), f_q)result := addmod(mulmod(mload(0xb00), mload(0x3ea0), f_q), result, f_q)result := addmod(mulmod(mload(0xb20), mload(0x3ec0), f_q), result, f_q)result := addmod(mulmod(mload(0xb40), mload(0x3ee0), f_q), result, f_q)mstore(0x4ca0, result) }mstore(0x4cc0, mulmod(mload(0x4ca0), mload(0x4380), f_q))mstore(0x4ce0, mulmod(sub(f_q, mload(0x4cc0)), mload(0x46c0), f_q))mstore(0x4d00, mulmod(1, mload(0x46c0), f_q))mstore(0x4d20, addmod(mload(0x4c80), mload(0x4ce0), f_q)){ let result := mulmod(mload(0xb60), mload(0x3e80), f_q)result := addmod(mulmod(mload(0xb80), mload(0x3ea0), f_q), result, f_q)result := addmod(mulmod(mload(0xba0), mload(0x3ec0), f_q), result, f_q)result := addmod(mulmod(mload(0xbc0), mload(0x3ee0), f_q), result, f_q)mstore(0x4d40, result) }mstore(0x4d60, mulmod(mload(0x4d40), mload(0x4380), f_q))mstore(0x4d80, mulmod(sub(f_q, mload(0x4d60)), mload(0x46e0), f_q))mstore(0x4da0, mulmod(1, mload(0x46e0), f_q))mstore(0x4dc0, addmod(mload(0x4d20), mload(0x4d80), f_q))mstore(0x4de0, mulmod(mload(0x4dc0), 1, f_q))mstore(0x4e00, mulmod(mload(0x4b20), 1, f_q))mstore(0x4e20, mulmod(mload(0x4bc0), 1, f_q))mstore(0x4e40, mulmod(mload(0x4c60), 1, f_q))mstore(0x4e60, mulmod(mload(0x4d00), 1, f_q))mstore(0x4e80, mulmod(mload(0x4da0), 1, f_q))mstore(0x4ea0, mulmod(1, mload(0x43a0), f_q)){ let result := mulmod(mload(0xbe0), mload(0x3f80), f_q)mstore(0x4ec0, result) }mstore(0x4ee0, mulmod(mload(0x4ec0), mload(0x4600), f_q))mstore(0x4f00, mulmod(sub(f_q, mload(0x4ee0)), 1, f_q))mstore(0x4f20, mulmod(mload(0x4ea0), 1, f_q)){ let result := mulmod(mload(0xc00), mload(0x3f80), f_q)mstore(0x4f40, result) }mstore(0x4f60, mulmod(mload(0x4f40), mload(0x4600), f_q))mstore(0x4f80, mulmod(sub(f_q, mload(0x4f60)), mload(0x1280), f_q))mstore(0x4fa0, mulmod(mload(0x4ea0), mload(0x1280), f_q))mstore(0x4fc0, addmod(mload(0x4f00), mload(0x4f80), f_q)){ let result := mulmod(mload(0xc20), mload(0x3f80), f_q)mstore(0x4fe0, result) }mstore(0x5000, mulmod(mload(0x4fe0), mload(0x4600), f_q))mstore(0x5020, mulmod(sub(f_q, mload(0x5000)), mload(0x4680), f_q))mstore(0x5040, mulmod(mload(0x4ea0), mload(0x4680), f_q))mstore(0x5060, addmod(mload(0x4fc0), mload(0x5020), f_q)){ let result := mulmod(mload(0x11a0), mload(0x3f80), f_q)mstore(0x5080, result) }mstore(0x50a0, mulmod(mload(0x5080), mload(0x4600), f_q))mstore(0x50c0, mulmod(sub(f_q, mload(0x50a0)), mload(0x46a0), f_q))mstore(0x50e0, mulmod(mload(0x4ea0), mload(0x46a0), f_q))mstore(0x5100, addmod(mload(0x5060), mload(0x50c0), f_q)){ let result := mulmod(mload(0x1240), mload(0x3f80), f_q)mstore(0x5120, result) }mstore(0x5140, mulmod(mload(0x5120), mload(0x4600), f_q))mstore(0x5160, mulmod(sub(f_q, mload(0x5140)), mload(0x46c0), f_q))mstore(0x5180, mulmod(mload(0x4ea0), mload(0x46c0), f_q))mstore(0x51a0, addmod(mload(0x5100), mload(0x5160), f_q)){ let result := mulmod(mload(0xc40), mload(0x3f80), f_q)mstore(0x51c0, result) }mstore(0x51e0, mulmod(mload(0x51c0), mload(0x4600), f_q))mstore(0x5200, mulmod(sub(f_q, mload(0x51e0)), mload(0x46e0), f_q))mstore(0x5220, mulmod(mload(0x4ea0), mload(0x46e0), f_q))mstore(0x5240, addmod(mload(0x51a0), mload(0x5200), f_q)){ let result := mulmod(mload(0xc60), mload(0x3f80), f_q)mstore(0x5260, result) }mstore(0x5280, mulmod(mload(0x5260), mload(0x4600), f_q))mstore(0x52a0, mulmod(sub(f_q, mload(0x5280)), mload(0x4700), f_q))mstore(0x52c0, mulmod(mload(0x4ea0), mload(0x4700), f_q))mstore(0x52e0, addmod(mload(0x5240), mload(0x52a0), f_q)){ let result := mulmod(mload(0xc80), mload(0x3f80), f_q)mstore(0x5300, result) }mstore(0x5320, mulmod(mload(0x5300), mload(0x4600), f_q))mstore(0x5340, mulmod(sub(f_q, mload(0x5320)), mload(0x4720), f_q))mstore(0x5360, mulmod(mload(0x4ea0), mload(0x4720), f_q))mstore(0x5380, addmod(mload(0x52e0), mload(0x5340), f_q)){ let result := mulmod(mload(0xca0), mload(0x3f80), f_q)mstore(0x53a0, result) }mstore(0x53c0, mulmod(mload(0x53a0), mload(0x4600), f_q))mstore(0x53e0, mulmod(sub(f_q, mload(0x53c0)), mload(0x4740), f_q))mstore(0x5400, mulmod(mload(0x4ea0), mload(0x4740), f_q))mstore(0x5420, addmod(mload(0x5380), mload(0x53e0), f_q)){ let result := mulmod(mload(0xcc0), mload(0x3f80), f_q)mstore(0x5440, result) }mstore(0x5460, mulmod(mload(0x5440), mload(0x4600), f_q))mstore(0x5480, mulmod(sub(f_q, mload(0x5460)), mload(0x4760), f_q))mstore(0x54a0, mulmod(mload(0x4ea0), mload(0x4760), f_q))mstore(0x54c0, addmod(mload(0x5420), mload(0x5480), f_q)){ let result := mulmod(mload(0xce0), mload(0x3f80), f_q)mstore(0x54e0, result) }mstore(0x5500, mulmod(mload(0x54e0), mload(0x4600), f_q))mstore(0x5520, mulmod(sub(f_q, mload(0x5500)), mload(0x4780), f_q))mstore(0x5540, mulmod(mload(0x4ea0), mload(0x4780), f_q))mstore(0x5560, addmod(mload(0x54c0), mload(0x5520), f_q)){ let result := mulmod(mload(0xd00), mload(0x3f80), f_q)mstore(0x5580, result) }mstore(0x55a0, mulmod(mload(0x5580), mload(0x4600), f_q))mstore(0x55c0, mulmod(sub(f_q, mload(0x55a0)), mload(0x47a0), f_q))mstore(0x55e0, mulmod(mload(0x4ea0), mload(0x47a0), f_q))mstore(0x5600, addmod(mload(0x5560), mload(0x55c0), f_q)){ let result := mulmod(mload(0xd20), mload(0x3f80), f_q)mstore(0x5620, result) }mstore(0x5640, mulmod(mload(0x5620), mload(0x4600), f_q))mstore(0x5660, mulmod(sub(f_q, mload(0x5640)), mload(0x47c0), f_q))mstore(0x5680, mulmod(mload(0x4ea0), mload(0x47c0), f_q))mstore(0x56a0, addmod(mload(0x5600), mload(0x5660), f_q)){ let result := mulmod(mload(0xd40), mload(0x3f80), f_q)mstore(0x56c0, result) }mstore(0x56e0, mulmod(mload(0x56c0), mload(0x4600), f_q))mstore(0x5700, mulmod(sub(f_q, mload(0x56e0)), mload(0x47e0), f_q))mstore(0x5720, mulmod(mload(0x4ea0), mload(0x47e0), f_q))mstore(0x5740, addmod(mload(0x56a0), mload(0x5700), f_q)){ let result := mulmod(mload(0xd60), mload(0x3f80), f_q)mstore(0x5760, result) }mstore(0x5780, mulmod(mload(0x5760), mload(0x4600), f_q))mstore(0x57a0, mulmod(sub(f_q, mload(0x5780)), mload(0x4800), f_q))mstore(0x57c0, mulmod(mload(0x4ea0), mload(0x4800), f_q))mstore(0x57e0, addmod(mload(0x5740), mload(0x57a0), f_q)){ let result := mulmod(mload(0xda0), mload(0x3f80), f_q)mstore(0x5800, result) }mstore(0x5820, mulmod(mload(0x5800), mload(0x4600), f_q))mstore(0x5840, mulmod(sub(f_q, mload(0x5820)), mload(0x4820), f_q))mstore(0x5860, mulmod(mload(0x4ea0), mload(0x4820), f_q))mstore(0x5880, addmod(mload(0x57e0), mload(0x5840), f_q)){ let result := mulmod(mload(0xdc0), mload(0x3f80), f_q)mstore(0x58a0, result) }mstore(0x58c0, mulmod(mload(0x58a0), mload(0x4600), f_q))mstore(0x58e0, mulmod(sub(f_q, mload(0x58c0)), mload(0x4840), f_q))mstore(0x5900, mulmod(mload(0x4ea0), mload(0x4840), f_q))mstore(0x5920, addmod(mload(0x5880), mload(0x58e0), f_q)){ let result := mulmod(mload(0xde0), mload(0x3f80), f_q)mstore(0x5940, result) }mstore(0x5960, mulmod(mload(0x5940), mload(0x4600), f_q))mstore(0x5980, mulmod(sub(f_q, mload(0x5960)), mload(0x4860), f_q))mstore(0x59a0, mulmod(mload(0x4ea0), mload(0x4860), f_q))mstore(0x59c0, addmod(mload(0x5920), mload(0x5980), f_q)){ let result := mulmod(mload(0xe00), mload(0x3f80), f_q)mstore(0x59e0, result) }mstore(0x5a00, mulmod(mload(0x59e0), mload(0x4600), f_q))mstore(0x5a20, mulmod(sub(f_q, mload(0x5a00)), mload(0x4880), f_q))mstore(0x5a40, mulmod(mload(0x4ea0), mload(0x4880), f_q))mstore(0x5a60, addmod(mload(0x59c0), mload(0x5a20), f_q)){ let result := mulmod(mload(0xe20), mload(0x3f80), f_q)mstore(0x5a80, result) }mstore(0x5aa0, mulmod(mload(0x5a80), mload(0x4600), f_q))mstore(0x5ac0, mulmod(sub(f_q, mload(0x5aa0)), mload(0x48a0), f_q))mstore(0x5ae0, mulmod(mload(0x4ea0), mload(0x48a0), f_q))mstore(0x5b00, addmod(mload(0x5a60), mload(0x5ac0), f_q)){ let result := mulmod(mload(0xe40), mload(0x3f80), f_q)mstore(0x5b20, result) }mstore(0x5b40, mulmod(mload(0x5b20), mload(0x4600), f_q))mstore(0x5b60, mulmod(sub(f_q, mload(0x5b40)), mload(0x48c0), f_q))mstore(0x5b80, mulmod(mload(0x4ea0), mload(0x48c0), f_q))mstore(0x5ba0, addmod(mload(0x5b00), mload(0x5b60), f_q)){ let result := mulmod(mload(0xe60), mload(0x3f80), f_q)mstore(0x5bc0, result) }mstore(0x5be0, mulmod(mload(0x5bc0), mload(0x4600), f_q))mstore(0x5c00, mulmod(sub(f_q, mload(0x5be0)), mload(0x48e0), f_q))mstore(0x5c20, mulmod(mload(0x4ea0), mload(0x48e0), f_q))mstore(0x5c40, addmod(mload(0x5ba0), mload(0x5c00), f_q)){ let result := mulmod(mload(0xe80), mload(0x3f80), f_q)mstore(0x5c60, result) }mstore(0x5c80, mulmod(mload(0x5c60), mload(0x4600), f_q))mstore(0x5ca0, mulmod(sub(f_q, mload(0x5c80)), mload(0x4900), f_q))mstore(0x5cc0, mulmod(mload(0x4ea0), mload(0x4900), f_q))mstore(0x5ce0, addmod(mload(0x5c40), mload(0x5ca0), f_q)){ let result := mulmod(mload(0xea0), mload(0x3f80), f_q)mstore(0x5d00, result) }mstore(0x5d20, mulmod(mload(0x5d00), mload(0x4600), f_q))mstore(0x5d40, mulmod(sub(f_q, mload(0x5d20)), mload(0x4920), f_q))mstore(0x5d60, mulmod(mload(0x4ea0), mload(0x4920), f_q))mstore(0x5d80, addmod(mload(0x5ce0), mload(0x5d40), f_q)){ let result := mulmod(mload(0xec0), mload(0x3f80), f_q)mstore(0x5da0, result) }mstore(0x5dc0, mulmod(mload(0x5da0), mload(0x4600), f_q))mstore(0x5de0, mulmod(sub(f_q, mload(0x5dc0)), mload(0x4940), f_q))mstore(0x5e00, mulmod(mload(0x4ea0), mload(0x4940), f_q))mstore(0x5e20, addmod(mload(0x5d80), mload(0x5de0), f_q)){ let result := mulmod(mload(0xee0), mload(0x3f80), f_q)mstore(0x5e40, result) }mstore(0x5e60, mulmod(mload(0x5e40), mload(0x4600), f_q))mstore(0x5e80, mulmod(sub(f_q, mload(0x5e60)), mload(0x4960), f_q))mstore(0x5ea0, mulmod(mload(0x4ea0), mload(0x4960), f_q))mstore(0x5ec0, addmod(mload(0x5e20), mload(0x5e80), f_q))mstore(0x5ee0, mulmod(mload(0x3c60), mload(0x43a0), f_q))mstore(0x5f00, mulmod(mload(0x3c80), mload(0x43a0), f_q)){ let result := mulmod(mload(0x3ca0), mload(0x3f80), f_q)mstore(0x5f20, result) }mstore(0x5f40, mulmod(mload(0x5f20), mload(0x4600), f_q))mstore(0x5f60, mulmod(sub(f_q, mload(0x5f40)), mload(0x4980), f_q))mstore(0x5f80, mulmod(mload(0x4ea0), mload(0x4980), f_q))mstore(0x5fa0, mulmod(mload(0x5ee0), mload(0x4980), f_q))mstore(0x5fc0, mulmod(mload(0x5f00), mload(0x4980), f_q))mstore(0x5fe0, addmod(mload(0x5ec0), mload(0x5f60), f_q)){ let result := mulmod(mload(0xd80), mload(0x3f80), f_q)mstore(0x6000, result) }mstore(0x6020, mulmod(mload(0x6000), mload(0x4600), f_q))mstore(0x6040, mulmod(sub(f_q, mload(0x6020)), mload(0x49a0), f_q))mstore(0x6060, mulmod(mload(0x4ea0), mload(0x49a0), f_q))mstore(0x6080, addmod(mload(0x5fe0), mload(0x6040), f_q))mstore(0x60a0, mulmod(mload(0x6080), mload(0x12e0), f_q))mstore(0x60c0, mulmod(mload(0x4f20), mload(0x12e0), f_q))mstore(0x60e0, mulmod(mload(0x4fa0), mload(0x12e0), f_q))mstore(0x6100, mulmod(mload(0x5040), mload(0x12e0), f_q))mstore(0x6120, mulmod(mload(0x50e0), mload(0x12e0), f_q))mstore(0x6140, mulmod(mload(0x5180), mload(0x12e0), f_q))mstore(0x6160, mulmod(mload(0x5220), mload(0x12e0), f_q))mstore(0x6180, mulmod(mload(0x52c0), mload(0x12e0), f_q))mstore(0x61a0, mulmod(mload(0x5360), mload(0x12e0), f_q))mstore(0x61c0, mulmod(mload(0x5400), mload(0x12e0), f_q))mstore(0x61e0, mulmod(mload(0x54a0), mload(0x12e0), f_q))mstore(0x6200, mulmod(mload(0x5540), mload(0x12e0), f_q))mstore(0x6220, mulmod(mload(0x55e0), mload(0x12e0), f_q))mstore(0x6240, mulmod(mload(0x5680), mload(0x12e0), f_q))mstore(0x6260, mulmod(mload(0x5720), mload(0x12e0), f_q))mstore(0x6280, mulmod(mload(0x57c0), mload(0x12e0), f_q))mstore(0x62a0, mulmod(mload(0x5860), mload(0x12e0), f_q))mstore(0x62c0, mulmod(mload(0x5900), mload(0x12e0), f_q))mstore(0x62e0, mulmod(mload(0x59a0), mload(0x12e0), f_q))mstore(0x6300, mulmod(mload(0x5a40), mload(0x12e0), f_q))mstore(0x6320, mulmod(mload(0x5ae0), mload(0x12e0), f_q))mstore(0x6340, mulmod(mload(0x5b80), mload(0x12e0), f_q))mstore(0x6360, mulmod(mload(0x5c20), mload(0x12e0), f_q))mstore(0x6380, mulmod(mload(0x5cc0), mload(0x12e0), f_q))mstore(0x63a0, mulmod(mload(0x5d60), mload(0x12e0), f_q))mstore(0x63c0, mulmod(mload(0x5e00), mload(0x12e0), f_q))mstore(0x63e0, mulmod(mload(0x5ea0), mload(0x12e0), f_q))mstore(0x6400, mulmod(mload(0x5f80), mload(0x12e0), f_q))mstore(0x6420, mulmod(mload(0x5fa0), mload(0x12e0), f_q))mstore(0x6440, mulmod(mload(0x5fc0), mload(0x12e0), f_q))mstore(0x6460, mulmod(mload(0x6060), mload(0x12e0), f_q))mstore(0x6480, addmod(mload(0x4de0), mload(0x60a0), f_q))mstore(0x64a0, mulmod(1, mload(0x43e0), f_q)){ let result := mulmod(mload(0xf00), mload(0x3fa0), f_q)result := addmod(mulmod(mload(0xf20), mload(0x3fc0), f_q), result, f_q)result := addmod(mulmod(mload(0xf40), mload(0x3fe0), f_q), result, f_q)mstore(0x64c0, result) }mstore(0x64e0, mulmod(mload(0x64c0), mload(0x4620), f_q))mstore(0x6500, mulmod(sub(f_q, mload(0x64e0)), 1, f_q))mstore(0x6520, mulmod(mload(0x64a0), 1, f_q)){ let result := mulmod(mload(0xf60), mload(0x3fa0), f_q)result := addmod(mulmod(mload(0xf80), mload(0x3fc0), f_q), result, f_q)result := addmod(mulmod(mload(0xfa0), mload(0x3fe0), f_q), result, f_q)mstore(0x6540, result) }mstore(0x6560, mulmod(mload(0x6540), mload(0x4620), f_q))mstore(0x6580, mulmod(sub(f_q, mload(0x6560)), mload(0x1280), f_q))mstore(0x65a0, mulmod(mload(0x64a0), mload(0x1280), f_q))mstore(0x65c0, addmod(mload(0x6500), mload(0x6580), f_q)){ let result := mulmod(mload(0xfc0), mload(0x3fa0), f_q)result := addmod(mulmod(mload(0xfe0), mload(0x3fc0), f_q), result, f_q)result := addmod(mulmod(mload(0x1000), mload(0x3fe0), f_q), result, f_q)mstore(0x65e0, result) }mstore(0x6600, mulmod(mload(0x65e0), mload(0x4620), f_q))mstore(0x6620, mulmod(sub(f_q, mload(0x6600)), mload(0x4680), f_q))mstore(0x6640, mulmod(mload(0x64a0), mload(0x4680), f_q))mstore(0x6660, addmod(mload(0x65c0), mload(0x6620), f_q)){ let result := mulmod(mload(0x1020), mload(0x3fa0), f_q)result := addmod(mulmod(mload(0x1040), mload(0x3fc0), f_q), result, f_q)result := addmod(mulmod(mload(0x1060), mload(0x3fe0), f_q), result, f_q)mstore(0x6680, result) }mstore(0x66a0, mulmod(mload(0x6680), mload(0x4620), f_q))mstore(0x66c0, mulmod(sub(f_q, mload(0x66a0)), mload(0x46a0), f_q))mstore(0x66e0, mulmod(mload(0x64a0), mload(0x46a0), f_q))mstore(0x6700, addmod(mload(0x6660), mload(0x66c0), f_q)){ let result := mulmod(mload(0x1080), mload(0x3fa0), f_q)result := addmod(mulmod(mload(0x10a0), mload(0x3fc0), f_q), result, f_q)result := addmod(mulmod(mload(0x10c0), mload(0x3fe0), f_q), result, f_q)mstore(0x6720, result) }mstore(0x6740, mulmod(mload(0x6720), mload(0x4620), f_q))mstore(0x6760, mulmod(sub(f_q, mload(0x6740)), mload(0x46c0), f_q))mstore(0x6780, mulmod(mload(0x64a0), mload(0x46c0), f_q))mstore(0x67a0, addmod(mload(0x6700), mload(0x6760), f_q))mstore(0x67c0, mulmod(mload(0x67a0), mload(0x49e0), f_q))mstore(0x67e0, mulmod(mload(0x6520), mload(0x49e0), f_q))mstore(0x6800, mulmod(mload(0x65a0), mload(0x49e0), f_q))mstore(0x6820, mulmod(mload(0x6640), mload(0x49e0), f_q))mstore(0x6840, mulmod(mload(0x66e0), mload(0x49e0), f_q))mstore(0x6860, mulmod(mload(0x6780), mload(0x49e0), f_q))mstore(0x6880, addmod(mload(0x6480), mload(0x67c0), f_q))mstore(0x68a0, mulmod(1, mload(0x4420), f_q)){ let result := mulmod(mload(0x10e0), mload(0x4020), f_q)result := addmod(mulmod(mload(0x1100), mload(0x4040), f_q), result, f_q)mstore(0x68c0, result) }mstore(0x68e0, mulmod(mload(0x68c0), mload(0x4640), f_q))mstore(0x6900, mulmod(sub(f_q, mload(0x68e0)), 1, f_q))mstore(0x6920, mulmod(mload(0x68a0), 1, f_q)){ let result := mulmod(mload(0x1120), mload(0x4020), f_q)result := addmod(mulmod(mload(0x1140), mload(0x4040), f_q), result, f_q)mstore(0x6940, result) }mstore(0x6960, mulmod(mload(0x6940), mload(0x4640), f_q))mstore(0x6980, mulmod(sub(f_q, mload(0x6960)), mload(0x1280), f_q))mstore(0x69a0, mulmod(mload(0x68a0), mload(0x1280), f_q))mstore(0x69c0, addmod(mload(0x6900), mload(0x6980), f_q)){ let result := mulmod(mload(0x11c0), mload(0x4020), f_q)result := addmod(mulmod(mload(0x11e0), mload(0x4040), f_q), result, f_q)mstore(0x69e0, result) }mstore(0x6a00, mulmod(mload(0x69e0), mload(0x4640), f_q))mstore(0x6a20, mulmod(sub(f_q, mload(0x6a00)), mload(0x4680), f_q))mstore(0x6a40, mulmod(mload(0x68a0), mload(0x4680), f_q))mstore(0x6a60, addmod(mload(0x69c0), mload(0x6a20), f_q))mstore(0x6a80, mulmod(mload(0x6a60), mload(0x4a00), f_q))mstore(0x6aa0, mulmod(mload(0x6920), mload(0x4a00), f_q))mstore(0x6ac0, mulmod(mload(0x69a0), mload(0x4a00), f_q))mstore(0x6ae0, mulmod(mload(0x6a40), mload(0x4a00), f_q))mstore(0x6b00, addmod(mload(0x6880), mload(0x6a80), f_q))mstore(0x6b20, mulmod(1, mload(0x4460), f_q)){ let result := mulmod(mload(0x1160), mload(0x4060), f_q)result := addmod(mulmod(mload(0x1180), mload(0x4080), f_q), result, f_q)mstore(0x6b40, result) }mstore(0x6b60, mulmod(mload(0x6b40), mload(0x4660), f_q))mstore(0x6b80, mulmod(sub(f_q, mload(0x6b60)), 1, f_q))mstore(0x6ba0, mulmod(mload(0x6b20), 1, f_q)){ let result := mulmod(mload(0x1200), mload(0x4060), f_q)result := addmod(mulmod(mload(0x1220), mload(0x4080), f_q), result, f_q)mstore(0x6bc0, result) }mstore(0x6be0, mulmod(mload(0x6bc0), mload(0x4660), f_q))mstore(0x6c00, mulmod(sub(f_q, mload(0x6be0)), mload(0x1280), f_q))mstore(0x6c20, mulmod(mload(0x6b20), mload(0x1280), f_q))mstore(0x6c40, addmod(mload(0x6b80), mload(0x6c00), f_q))mstore(0x6c60, mulmod(mload(0x6c40), mload(0x4a20), f_q))mstore(0x6c80, mulmod(mload(0x6ba0), mload(0x4a20), f_q))mstore(0x6ca0, mulmod(mload(0x6c20), mload(0x4a20), f_q))mstore(0x6cc0, addmod(mload(0x6b00), mload(0x6c60), f_q))mstore(0x6ce0, mulmod(1, mload(0x3f60), f_q))mstore(0x6d00, mulmod(1, mload(0x1380), f_q))mstore(0x6d20, 0x0000000000000000000000000000000000000000000000000000000000000001) mstore(0x6d40, 0x0000000000000000000000000000000000000000000000000000000000000002)mstore(0x6d60, mload(0x6cc0))success := and(eq(staticcall(gas(), 0x7, 0x6d20, 0x60, 0x6d20, 0x40), 1), success)mstore(0x6d80, mload(0x6d20)) mstore(0x6da0, mload(0x6d40))mstore(0x6dc0, mload(0xc0)) mstore(0x6de0, mload(0xe0))success := and(eq(staticcall(gas(), 0x6, 0x6d80, 0x80, 0x6d80, 0x40), 1), success)mstore(0x6e00, mload(0x100)) mstore(0x6e20, mload(0x120))mstore(0x6e40, mload(0x4e00))success := and(eq(staticcall(gas(), 0x7, 0x6e00, 0x60, 0x6e00, 0x40), 1), success)mstore(0x6e60, mload(0x6d80)) mstore(0x6e80, mload(0x6da0))mstore(0x6ea0, mload(0x6e00)) mstore(0x6ec0, mload(0x6e20))success := and(eq(staticcall(gas(), 0x6, 0x6e60, 0x80, 0x6e60, 0x40), 1), success)mstore(0x6ee0, mload(0x140)) mstore(0x6f00, mload(0x160))mstore(0x6f20, mload(0x4e20))success := and(eq(staticcall(gas(), 0x7, 0x6ee0, 0x60, 0x6ee0, 0x40), 1), success)mstore(0x6f40, mload(0x6e60)) mstore(0x6f60, mload(0x6e80))mstore(0x6f80, mload(0x6ee0)) mstore(0x6fa0, mload(0x6f00))success := and(eq(staticcall(gas(), 0x6, 0x6f40, 0x80, 0x6f40, 0x40), 1), success)mstore(0x6fc0, mload(0x180)) mstore(0x6fe0, mload(0x1a0))mstore(0x7000, mload(0x4e40))success := and(eq(staticcall(gas(), 0x7, 0x6fc0, 0x60, 0x6fc0, 0x40), 1), success)mstore(0x7020, mload(0x6f40)) mstore(0x7040, mload(0x6f60))mstore(0x7060, mload(0x6fc0)) mstore(0x7080, mload(0x6fe0))success := and(eq(staticcall(gas(), 0x6, 0x7020, 0x80, 0x7020, 0x40), 1), success)mstore(0x70a0, mload(0x1c0)) mstore(0x70c0, mload(0x1e0))mstore(0x70e0, mload(0x4e60))success := and(eq(staticcall(gas(), 0x7, 0x70a0, 0x60, 0x70a0, 0x40), 1), success)mstore(0x7100, mload(0x7020)) mstore(0x7120, mload(0x7040))mstore(0x7140, mload(0x70a0)) mstore(0x7160, mload(0x70c0))success := and(eq(staticcall(gas(), 0x6, 0x7100, 0x80, 0x7100, 0x40), 1), success)mstore(0x7180, mload(0x200)) mstore(0x71a0, mload(0x220))mstore(0x71c0, mload(0x4e80))success := and(eq(staticcall(gas(), 0x7, 0x7180, 0x60, 0x7180, 0x40), 1), success)mstore(0x71e0, mload(0x7100)) mstore(0x7200, mload(0x7120))mstore(0x7220, mload(0x7180)) mstore(0x7240, mload(0x71a0))success := and(eq(staticcall(gas(), 0x6, 0x71e0, 0x80, 0x71e0, 0x40), 1), success)mstore(0x7260, mload(0x240)) mstore(0x7280, mload(0x260))mstore(0x72a0, mload(0x60c0))success := and(eq(staticcall(gas(), 0x7, 0x7260, 0x60, 0x7260, 0x40), 1), success)mstore(0x72c0, mload(0x71e0)) mstore(0x72e0, mload(0x7200))mstore(0x7300, mload(0x7260)) mstore(0x7320, mload(0x7280))success := and(eq(staticcall(gas(), 0x6, 0x72c0, 0x80, 0x72c0, 0x40), 1), success)mstore(0x7340, mload(0x280)) mstore(0x7360, mload(0x2a0))mstore(0x7380, mload(0x60e0))success := and(eq(staticcall(gas(), 0x7, 0x7340, 0x60, 0x7340, 0x40), 1), success)mstore(0x73a0, mload(0x72c0)) mstore(0x73c0, mload(0x72e0))mstore(0x73e0, mload(0x7340)) mstore(0x7400, mload(0x7360))success := and(eq(staticcall(gas(), 0x6, 0x73a0, 0x80, 0x73a0, 0x40), 1), success)mstore(0x7420, mload(0x2c0)) mstore(0x7440, mload(0x2e0))mstore(0x7460, mload(0x6100))success := and(eq(staticcall(gas(), 0x7, 0x7420, 0x60, 0x7420, 0x40), 1), success)mstore(0x7480, mload(0x73a0)) mstore(0x74a0, mload(0x73c0))mstore(0x74c0, mload(0x7420)) mstore(0x74e0, mload(0x7440))success := and(eq(staticcall(gas(), 0x6, 0x7480, 0x80, 0x7480, 0x40), 1), success)mstore(0x7500, mload(0x3a0)) mstore(0x7520, mload(0x3c0))mstore(0x7540, mload(0x6120))success := and(eq(staticcall(gas(), 0x7, 0x7500, 0x60, 0x7500, 0x40), 1), success)mstore(0x7560, mload(0x7480)) mstore(0x7580, mload(0x74a0))mstore(0x75a0, mload(0x7500)) mstore(0x75c0, mload(0x7520))success := and(eq(staticcall(gas(), 0x6, 0x7560, 0x80, 0x7560, 0x40), 1), success)mstore(0x75e0, mload(0x420)) mstore(0x7600, mload(0x440))mstore(0x7620, mload(0x6140))success := and(eq(staticcall(gas(), 0x7, 0x75e0, 0x60, 0x75e0, 0x40), 1), success)mstore(0x7640, mload(0x7560)) mstore(0x7660, mload(0x7580))mstore(0x7680, mload(0x75e0)) mstore(0x76a0, mload(0x7600))success := and(eq(staticcall(gas(), 0x6, 0x7640, 0x80, 0x7640, 0x40), 1), success)mstore(0x76c0, 0x1efccb5f35c0ab008fa604178482fb235779612117d933e230bf21aed1936775) mstore(0x76e0, 0x1dab7bfc8c09774da8326adfa867ee15820c0e4a66068d086b2dee960993fc8a)mstore(0x7700, mload(0x6160))success := and(eq(staticcall(gas(), 0x7, 0x76c0, 0x60, 0x76c0, 0x40), 1), success)mstore(0x7720, mload(0x7640)) mstore(0x7740, mload(0x7660))mstore(0x7760, mload(0x76c0)) mstore(0x7780, mload(0x76e0))success := and(eq(staticcall(gas(), 0x6, 0x7720, 0x80, 0x7720, 0x40), 1), success)mstore(0x77a0, 0x04528ec7365a2881b7d3c8925570e06bb3b17f04f6a95384ac8ed19a30c12097) mstore(0x77c0, 0x28d1ef470a8a5278ad6d2eb9047ad7e93024113f543b06870f1bbea7177db404)mstore(0x77e0, mload(0x6180))success := and(eq(staticcall(gas(), 0x7, 0x77a0, 0x60, 0x77a0, 0x40), 1), success)mstore(0x7800, mload(0x7720)) mstore(0x7820, mload(0x7740))mstore(0x7840, mload(0x77a0)) mstore(0x7860, mload(0x77c0))success := and(eq(staticcall(gas(), 0x6, 0x7800, 0x80, 0x7800, 0x40), 1), success)mstore(0x7880, 0x04d043081f0d55eead6d8ad7b10d09a6ee2718f445d9bce454075a8a37bacaf3) mstore(0x78a0, 0x27d6bcbb02cd624ab80b5532a0a65fc6f88a0faf7cf3e0d106f4aa0aa25e758b)mstore(0x78c0, mload(0x61a0))success := and(eq(staticcall(gas(), 0x7, 0x7880, 0x60, 0x7880, 0x40), 1), success)mstore(0x78e0, mload(0x7800)) mstore(0x7900, mload(0x7820))mstore(0x7920, mload(0x7880)) mstore(0x7940, mload(0x78a0))success := and(eq(staticcall(gas(), 0x6, 0x78e0, 0x80, 0x78e0, 0x40), 1), success)mstore(0x7960, 0x2d07a1bca289cdb98b648a91cbb0809dfa3a06fe01047b291d1161ddf8d1732c) mstore(0x7980, 0x021d078d5869c57b3fe2413b517561205de5f297ac56c0e5ef0f1a7f4a31ee94)mstore(0x79a0, mload(0x61c0))success := and(eq(staticcall(gas(), 0x7, 0x7960, 0x60, 0x7960, 0x40), 1), success)mstore(0x79c0, mload(0x78e0)) mstore(0x79e0, mload(0x7900))mstore(0x7a00, mload(0x7960)) mstore(0x7a20, mload(0x7980))success := and(eq(staticcall(gas(), 0x6, 0x79c0, 0x80, 0x79c0, 0x40), 1), success)mstore(0x7a40, 0x0654df583cf4a624c7d28ab22c3d83ac654441d1773fa149a5d8dd93904aefa1) mstore(0x7a60, 0x03505462356097e7becf98acd86de36a336a2e9c149fe3ff073199ff414da34e)mstore(0x7a80, mload(0x61e0))success := and(eq(staticcall(gas(), 0x7, 0x7a40, 0x60, 0x7a40, 0x40), 1), success)mstore(0x7aa0, mload(0x79c0)) mstore(0x7ac0, mload(0x79e0))mstore(0x7ae0, mload(0x7a40)) mstore(0x7b00, mload(0x7a60))success := and(eq(staticcall(gas(), 0x6, 0x7aa0, 0x80, 0x7aa0, 0x40), 1), success)mstore(0x7b20, 0x013693bda5e1bad435fc7cc97749086b700dd0a10463a371be028ef9837494df) mstore(0x7b40, 0x219efc67984ad64348266fefcffc138d846b0c0d8f81a24ea3774609d3f5d724)mstore(0x7b60, mload(0x6200))success := and(eq(staticcall(gas(), 0x7, 0x7b20, 0x60, 0x7b20, 0x40), 1), success)mstore(0x7b80, mload(0x7aa0)) mstore(0x7ba0, mload(0x7ac0))mstore(0x7bc0, mload(0x7b20)) mstore(0x7be0, mload(0x7b40))success := and(eq(staticcall(gas(), 0x6, 0x7b80, 0x80, 0x7b80, 0x40), 1), success)mstore(0x7c00, 0x1a29044990ec839bb07d56ffa00618e186716d7366207f909fb44641a8845baf) mstore(0x7c20, 0x16b51f53337d9b353024e90d2b9870fe165ef601abd795707d318ba5bcb211d4)mstore(0x7c40, mload(0x6220))success := and(eq(staticcall(gas(), 0x7, 0x7c00, 0x60, 0x7c00, 0x40), 1), success)mstore(0x7c60, mload(0x7b80)) mstore(0x7c80, mload(0x7ba0))mstore(0x7ca0, mload(0x7c00)) mstore(0x7cc0, mload(0x7c20))success := and(eq(staticcall(gas(), 0x6, 0x7c60, 0x80, 0x7c60, 0x40), 1), success)mstore(0x7ce0, 0x0bd8cdd311a39512e0f4c79e046470751b4a0cf050a865eeca84b31cef672341) mstore(0x7d00, 0x2dc5df62b48f53a8f1ee964a28d2ae7d32fdccd4a18dfd38fed88c05a4d16149)mstore(0x7d20, mload(0x6240))success := and(eq(staticcall(gas(), 0x7, 0x7ce0, 0x60, 0x7ce0, 0x40), 1), success)mstore(0x7d40, mload(0x7c60)) mstore(0x7d60, mload(0x7c80))mstore(0x7d80, mload(0x7ce0)) mstore(0x7da0, mload(0x7d00))success := and(eq(staticcall(gas(), 0x6, 0x7d40, 0x80, 0x7d40, 0x40), 1), success)mstore(0x7dc0, 0x028aa90bb886253410dcea14a0fcc7ee2e66ce14b81aec4715614c41bf7563ab) mstore(0x7de0, 0x26991cf8ac328075cc8c3acb7aee7fd287ea7c81dcb26a16b4539140dfcab001)mstore(0x7e00, mload(0x6260))success := and(eq(staticcall(gas(), 0x7, 0x7dc0, 0x60, 0x7dc0, 0x40), 1), success)mstore(0x7e20, mload(0x7d40)) mstore(0x7e40, mload(0x7d60))mstore(0x7e60, mload(0x7dc0)) mstore(0x7e80, mload(0x7de0))success := and(eq(staticcall(gas(), 0x6, 0x7e20, 0x80, 0x7e20, 0x40), 1), success)mstore(0x7ea0, 0x1bbbe4bbdf53af228d548df2c7b85b4f0935416aa57f902f39ae96968788a2a8) mstore(0x7ec0, 0x2f646e1fb0c2cece92011a17632fcc535eb143eefd0e52482bfa217fc5051fa2)mstore(0x7ee0, mload(0x6280))success := and(eq(staticcall(gas(), 0x7, 0x7ea0, 0x60, 0x7ea0, 0x40), 1), success)mstore(0x7f00, mload(0x7e20)) mstore(0x7f20, mload(0x7e40))mstore(0x7f40, mload(0x7ea0)) mstore(0x7f60, mload(0x7ec0))success := and(eq(staticcall(gas(), 0x6, 0x7f00, 0x80, 0x7f00, 0x40), 1), success)mstore(0x7f80, 0x0060f9f754440a2092cc94fd2a241d1d15f8e5af1788283409e3001646c7a70b) mstore(0x7fa0, 0x2ea7ccc419b0aa9cef7da3553de9fb53ea36fcb8054d4d303fc5d256a16c303d)mstore(0x7fc0, mload(0x62a0))success := and(eq(staticcall(gas(), 0x7, 0x7f80, 0x60, 0x7f80, 0x40), 1), success)mstore(0x7fe0, mload(0x7f00)) mstore(0x8000, mload(0x7f20))mstore(0x8020, mload(0x7f80)) mstore(0x8040, mload(0x7fa0))success := and(eq(staticcall(gas(), 0x6, 0x7fe0, 0x80, 0x7fe0, 0x40), 1), success)mstore(0x8060, 0x2de0a9b9538e8ecb2cc0a759b7490940a0485aab4a713a55e02bc907e4faa0cc) mstore(0x8080, 0x2934963db676224679c8555b9986f3c29cac27b9bb1ad3815048ada5e43e543e)mstore(0x80a0, mload(0x62c0))success := and(eq(staticcall(gas(), 0x7, 0x8060, 0x60, 0x8060, 0x40), 1), success)mstore(0x80c0, mload(0x7fe0)) mstore(0x80e0, mload(0x8000))mstore(0x8100, mload(0x8060)) mstore(0x8120, mload(0x8080))success := and(eq(staticcall(gas(), 0x6, 0x80c0, 0x80, 0x80c0, 0x40), 1), success)mstore(0x8140, 0x28ca8f590655054ebeb49e650f3af25999caa8584a5edf6c364dc3aba911be48) mstore(0x8160, 0x24e1e6cd76bad1ad8e804f4586d997d268763b1200e849e6d0eae4b747f9cfce)mstore(0x8180, mload(0x62e0))success := and(eq(staticcall(gas(), 0x7, 0x8140, 0x60, 0x8140, 0x40), 1), success)mstore(0x81a0, mload(0x80c0)) mstore(0x81c0, mload(0x80e0))mstore(0x81e0, mload(0x8140)) mstore(0x8200, mload(0x8160))success := and(eq(staticcall(gas(), 0x6, 0x81a0, 0x80, 0x81a0, 0x40), 1), success)mstore(0x8220, 0x098c0774d8a6bb5e6f16db4af08273aaf352980e582eebd70323a69bec6e9569) mstore(0x8240, 0x1ae410753dde006f1c34513ddc66c05b17419a746dc468a32db541e4de6c3e5d)mstore(0x8260, mload(0x6300))success := and(eq(staticcall(gas(), 0x7, 0x8220, 0x60, 0x8220, 0x40), 1), success)mstore(0x8280, mload(0x81a0)) mstore(0x82a0, mload(0x81c0))mstore(0x82c0, mload(0x8220)) mstore(0x82e0, mload(0x8240))success := and(eq(staticcall(gas(), 0x6, 0x8280, 0x80, 0x8280, 0x40), 1), success)mstore(0x8300, 0x1c7529bd3f005de2d1319ef3faa0f2e301fdc4a80e5bc256297b7676875be33a) mstore(0x8320, 0x149f786b2355985e8a2aedb1974448bd312d1fda6231177f0915c35d96ac460f)mstore(0x8340, mload(0x6320))success := and(eq(staticcall(gas(), 0x7, 0x8300, 0x60, 0x8300, 0x40), 1), success)mstore(0x8360, mload(0x8280)) mstore(0x8380, mload(0x82a0))mstore(0x83a0, mload(0x8300)) mstore(0x83c0, mload(0x8320))success := and(eq(staticcall(gas(), 0x6, 0x8360, 0x80, 0x8360, 0x40), 1), success)mstore(0x83e0, 0x281dad8bf95d2164b0906c6b77b79bec3c5485308d8d619fe698994e6970af72) mstore(0x8400, 0x24e52f6a9fa13d1862a68e9cd782c0cfa63b5a75e83ff95ba33fad23bb13d304)mstore(0x8420, mload(0x6340))success := and(eq(staticcall(gas(), 0x7, 0x83e0, 0x60, 0x83e0, 0x40), 1), success)mstore(0x8440, mload(0x8360)) mstore(0x8460, mload(0x8380))mstore(0x8480, mload(0x83e0)) mstore(0x84a0, mload(0x8400))success := and(eq(staticcall(gas(), 0x6, 0x8440, 0x80, 0x8440, 0x40), 1), success)mstore(0x84c0, 0x02a6a0dd5a688e70421a982ab0423f2e8cd792584403faf20031c7201cab99d6) mstore(0x84e0, 0x13449960329af86b01e0e7db42d5695aec238c0cde6aa295a465ac4dee0bbbfb)mstore(0x8500, mload(0x6360))success := and(eq(staticcall(gas(), 0x7, 0x84c0, 0x60, 0x84c0, 0x40), 1), success)mstore(0x8520, mload(0x8440)) mstore(0x8540, mload(0x8460))mstore(0x8560, mload(0x84c0)) mstore(0x8580, mload(0x84e0))success := and(eq(staticcall(gas(), 0x6, 0x8520, 0x80, 0x8520, 0x40), 1), success)mstore(0x85a0, 0x2cd34994950e99bf30249c30f165e5f6d07c2931bf85ee28489ac460709f59c6) mstore(0x85c0, 0x1503e1c34b3727bf06ab73587a286fcce21b77276f589eb362fe45d4f4fbb0fd)mstore(0x85e0, mload(0x6380))success := and(eq(staticcall(gas(), 0x7, 0x85a0, 0x60, 0x85a0, 0x40), 1), success)mstore(0x8600, mload(0x8520)) mstore(0x8620, mload(0x8540))mstore(0x8640, mload(0x85a0)) mstore(0x8660, mload(0x85c0))success := and(eq(staticcall(gas(), 0x6, 0x8600, 0x80, 0x8600, 0x40), 1), success)mstore(0x8680, 0x29f2d18d1458a8e0371b324f2357f249a79f863da3194c805106d40d318ad200) mstore(0x86a0, 0x19212a5abbde043bbe6c9de52ae0eddb9c01680c09265e98f11985de5d68150e)mstore(0x86c0, mload(0x63a0))success := and(eq(staticcall(gas(), 0x7, 0x8680, 0x60, 0x8680, 0x40), 1), success)mstore(0x86e0, mload(0x8600)) mstore(0x8700, mload(0x8620))mstore(0x8720, mload(0x8680)) mstore(0x8740, mload(0x86a0))success := and(eq(staticcall(gas(), 0x6, 0x86e0, 0x80, 0x86e0, 0x40), 1), success)mstore(0x8760, 0x122b868ac9e3f68a6ba7b324605b557f355c0f93010aa23dba11015504c56e99) mstore(0x8780, 0x144c4b06d0eedff02f254f50a618cb72936bb697f7edeff21a9fbf5a37081525)mstore(0x87a0, mload(0x63c0))success := and(eq(staticcall(gas(), 0x7, 0x8760, 0x60, 0x8760, 0x40), 1), success)mstore(0x87c0, mload(0x86e0)) mstore(0x87e0, mload(0x8700))mstore(0x8800, mload(0x8760)) mstore(0x8820, mload(0x8780))success := and(eq(staticcall(gas(), 0x6, 0x87c0, 0x80, 0x87c0, 0x40), 1), success)mstore(0x8840, 0x271ae9e031116906c57f8210e97fd38b41a0edbf7ed4d9f31735ee7ca00f3baa) mstore(0x8860, 0x0a3bf4f9567fbf80f64d6addd43df68e2935a00997d3c767ced1ecbbd838c72c)mstore(0x8880, mload(0x63e0))success := and(eq(staticcall(gas(), 0x7, 0x8840, 0x60, 0x8840, 0x40), 1), success)mstore(0x88a0, mload(0x87c0)) mstore(0x88c0, mload(0x87e0))mstore(0x88e0, mload(0x8840)) mstore(0x8900, mload(0x8860))success := and(eq(staticcall(gas(), 0x6, 0x88a0, 0x80, 0x88a0, 0x40), 1), success)mstore(0x8920, mload(0x7c0)) mstore(0x8940, mload(0x7e0))mstore(0x8960, mload(0x6400))success := and(eq(staticcall(gas(), 0x7, 0x8920, 0x60, 0x8920, 0x40), 1), success)mstore(0x8980, mload(0x88a0)) mstore(0x89a0, mload(0x88c0))mstore(0x89c0, mload(0x8920)) mstore(0x89e0, mload(0x8940))success := and(eq(staticcall(gas(), 0x6, 0x8980, 0x80, 0x8980, 0x40), 1), success)mstore(0x8a00, mload(0x800)) mstore(0x8a20, mload(0x820))mstore(0x8a40, mload(0x6420))success := and(eq(staticcall(gas(), 0x7, 0x8a00, 0x60, 0x8a00, 0x40), 1), success)mstore(0x8a60, mload(0x8980)) mstore(0x8a80, mload(0x89a0))mstore(0x8aa0, mload(0x8a00)) mstore(0x8ac0, mload(0x8a20))success := and(eq(staticcall(gas(), 0x6, 0x8a60, 0x80, 0x8a60, 0x40), 1), success)mstore(0x8ae0, mload(0x840)) mstore(0x8b00, mload(0x860))mstore(0x8b20, mload(0x6440))success := and(eq(staticcall(gas(), 0x7, 0x8ae0, 0x60, 0x8ae0, 0x40), 1), success)mstore(0x8b40, mload(0x8a60)) mstore(0x8b60, mload(0x8a80))mstore(0x8b80, mload(0x8ae0)) mstore(0x8ba0, mload(0x8b00))success := and(eq(staticcall(gas(), 0x6, 0x8b40, 0x80, 0x8b40, 0x40), 1), success)mstore(0x8bc0, mload(0x720)) mstore(0x8be0, mload(0x740))mstore(0x8c00, mload(0x6460))success := and(eq(staticcall(gas(), 0x7, 0x8bc0, 0x60, 0x8bc0, 0x40), 1), success)mstore(0x8c20, mload(0x8b40)) mstore(0x8c40, mload(0x8b60))mstore(0x8c60, mload(0x8bc0)) mstore(0x8c80, mload(0x8be0))success := and(eq(staticcall(gas(), 0x6, 0x8c20, 0x80, 0x8c20, 0x40), 1), success)mstore(0x8ca0, mload(0x520)) mstore(0x8cc0, mload(0x540))mstore(0x8ce0, mload(0x67e0))success := and(eq(staticcall(gas(), 0x7, 0x8ca0, 0x60, 0x8ca0, 0x40), 1), success)mstore(0x8d00, mload(0x8c20)) mstore(0x8d20, mload(0x8c40))mstore(0x8d40, mload(0x8ca0)) mstore(0x8d60, mload(0x8cc0))success := and(eq(staticcall(gas(), 0x6, 0x8d00, 0x80, 0x8d00, 0x40), 1), success)mstore(0x8d80, mload(0x560)) mstore(0x8da0, mload(0x580))mstore(0x8dc0, mload(0x6800))success := and(eq(staticcall(gas(), 0x7, 0x8d80, 0x60, 0x8d80, 0x40), 1), success)mstore(0x8de0, mload(0x8d00)) mstore(0x8e00, mload(0x8d20))mstore(0x8e20, mload(0x8d80)) mstore(0x8e40, mload(0x8da0))success := and(eq(staticcall(gas(), 0x6, 0x8de0, 0x80, 0x8de0, 0x40), 1), success)mstore(0x8e60, mload(0x5a0)) mstore(0x8e80, mload(0x5c0))mstore(0x8ea0, mload(0x6820))success := and(eq(staticcall(gas(), 0x7, 0x8e60, 0x60, 0x8e60, 0x40), 1), success)mstore(0x8ec0, mload(0x8de0)) mstore(0x8ee0, mload(0x8e00))mstore(0x8f00, mload(0x8e60)) mstore(0x8f20, mload(0x8e80))success := and(eq(staticcall(gas(), 0x6, 0x8ec0, 0x80, 0x8ec0, 0x40), 1), success)mstore(0x8f40, mload(0x5e0)) mstore(0x8f60, mload(0x600))mstore(0x8f80, mload(0x6840))success := and(eq(staticcall(gas(), 0x7, 0x8f40, 0x60, 0x8f40, 0x40), 1), success)mstore(0x8fa0, mload(0x8ec0)) mstore(0x8fc0, mload(0x8ee0))mstore(0x8fe0, mload(0x8f40)) mstore(0x9000, mload(0x8f60))success := and(eq(staticcall(gas(), 0x6, 0x8fa0, 0x80, 0x8fa0, 0x40), 1), success)mstore(0x9020, mload(0x620)) mstore(0x9040, mload(0x640))mstore(0x9060, mload(0x6860))success := and(eq(staticcall(gas(), 0x7, 0x9020, 0x60, 0x9020, 0x40), 1), success)mstore(0x9080, mload(0x8fa0)) mstore(0x90a0, mload(0x8fc0))mstore(0x90c0, mload(0x9020)) mstore(0x90e0, mload(0x9040))success := and(eq(staticcall(gas(), 0x6, 0x9080, 0x80, 0x9080, 0x40), 1), success)mstore(0x9100, mload(0x660)) mstore(0x9120, mload(0x680))mstore(0x9140, mload(0x6aa0))success := and(eq(staticcall(gas(), 0x7, 0x9100, 0x60, 0x9100, 0x40), 1), success)mstore(0x9160, mload(0x9080)) mstore(0x9180, mload(0x90a0))mstore(0x91a0, mload(0x9100)) mstore(0x91c0, mload(0x9120))success := and(eq(staticcall(gas(), 0x6, 0x9160, 0x80, 0x9160, 0x40), 1), success)mstore(0x91e0, mload(0x6a0)) mstore(0x9200, mload(0x6c0))mstore(0x9220, mload(0x6ac0))success := and(eq(staticcall(gas(), 0x7, 0x91e0, 0x60, 0x91e0, 0x40), 1), success)mstore(0x9240, mload(0x9160)) mstore(0x9260, mload(0x9180))mstore(0x9280, mload(0x91e0)) mstore(0x92a0, mload(0x9200))success := and(eq(staticcall(gas(), 0x6, 0x9240, 0x80, 0x9240, 0x40), 1), success)mstore(0x92c0, mload(0x6e0)) mstore(0x92e0, mload(0x700))mstore(0x9300, mload(0x6ae0))success := and(eq(staticcall(gas(), 0x7, 0x92c0, 0x60, 0x92c0, 0x40), 1), success)mstore(0x9320, mload(0x9240)) mstore(0x9340, mload(0x9260))mstore(0x9360, mload(0x92c0)) mstore(0x9380, mload(0x92e0))success := and(eq(staticcall(gas(), 0x6, 0x9320, 0x80, 0x9320, 0x40), 1), success)mstore(0x93a0, mload(0x360)) mstore(0x93c0, mload(0x380))mstore(0x93e0, mload(0x6c80))success := and(eq(staticcall(gas(), 0x7, 0x93a0, 0x60, 0x93a0, 0x40), 1), success)mstore(0x9400, mload(0x9320)) mstore(0x9420, mload(0x9340))mstore(0x9440, mload(0x93a0)) mstore(0x9460, mload(0x93c0))success := and(eq(staticcall(gas(), 0x6, 0x9400, 0x80, 0x9400, 0x40), 1), success)mstore(0x9480, mload(0x3e0)) mstore(0x94a0, mload(0x400))mstore(0x94c0, mload(0x6ca0))success := and(eq(staticcall(gas(), 0x7, 0x9480, 0x60, 0x9480, 0x40), 1), success)mstore(0x94e0, mload(0x9400)) mstore(0x9500, mload(0x9420))mstore(0x9520, mload(0x9480)) mstore(0x9540, mload(0x94a0))success := and(eq(staticcall(gas(), 0x6, 0x94e0, 0x80, 0x94e0, 0x40), 1), success)mstore(0x9560, mload(0x1320)) mstore(0x9580, mload(0x1340))mstore(0x95a0, sub(f_q, mload(0x6ce0)))success := and(eq(staticcall(gas(), 0x7, 0x9560, 0x60, 0x9560, 0x40), 1), success)mstore(0x95c0, mload(0x94e0)) mstore(0x95e0, mload(0x9500))mstore(0x9600, mload(0x9560)) mstore(0x9620, mload(0x9580))success := and(eq(staticcall(gas(), 0x6, 0x95c0, 0x80, 0x95c0, 0x40), 1), success)mstore(0x9640, mload(0x13c0)) mstore(0x9660, mload(0x13e0))mstore(0x9680, mload(0x6d00))success := and(eq(staticcall(gas(), 0x7, 0x9640, 0x60, 0x9640, 0x40), 1), success)mstore(0x96a0, mload(0x95c0)) mstore(0x96c0, mload(0x95e0))mstore(0x96e0, mload(0x9640)) mstore(0x9700, mload(0x9660))success := and(eq(staticcall(gas(), 0x6, 0x96a0, 0x80, 0x96a0, 0x40), 1), success)mstore(0x9720, mload(0x96a0)) mstore(0x9740, mload(0x96c0))mstore(0x9760, 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2) mstore(0x9780, 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed) mstore(0x97a0, 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b) mstore(0x97c0, 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa)mstore(0x97e0, mload(0x13c0)) mstore(0x9800, mload(0x13e0))mstore(0x9820, 0x0181624e80f3d6ae28df7e01eaeab1c0e919877a3b8a6b7fbc69a6817d596ea2) mstore(0x9840, 0x1783d30dcb12d259bb89098addf6280fa4b653be7a152542a28f7b926e27e648) mstore(0x9860, 0x00ae44489d41a0d179e2dfdc03bddd883b7109f8b6ae316a59e815c1a6b35304) mstore(0x9880, 0x0b2147ab62a386bd63e6de1522109b8c9588ab466f5aadfde8c41ca3749423ee)success := and(eq(staticcall(gas(), 0x8, 0x9720, 0x180, 0x9720, 0x20), 1), success)success := and(eq(mload(0x9720), 1), success)} return success; } } + assembly { let f_p := 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47 let f_q := 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001 function validate_ec_point(x, y) -> valid { { let x_lt_p := lt(x, 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47) let y_lt_p := lt(y, 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47) valid := and(x_lt_p, y_lt_p) } { let y_square := mulmod(y, y, 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47) let x_square := mulmod(x, x, 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47) let x_cube := mulmod(x_square, x, 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47) let x_cube_plus_3 := addmod(x_cube, 3, 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47) let is_affine := eq(x_cube_plus_3, y_square) valid := and(valid, is_affine) } } mstore(0xa0, mod(calldataload(0x4), f_q))mstore(0x80, 4243859105650854234153099360242888532593913508399116289013388094561724186169) { let x := calldataload(0x64) mstore(0xc0, x) let y := calldataload(0x84) mstore(0xe0, y) success := and(validate_ec_point(x, y), success) } { let x := calldataload(0xa4) mstore(0x100, x) let y := calldataload(0xc4) mstore(0x120, y) success := and(validate_ec_point(x, y), success) } { let x := calldataload(0xe4) mstore(0x140, x) let y := calldataload(0x104) mstore(0x160, y) success := and(validate_ec_point(x, y), success) } { let x := calldataload(0x124) mstore(0x180, x) let y := calldataload(0x144) mstore(0x1a0, y) success := and(validate_ec_point(x, y), success) } { let x := calldataload(0x164) mstore(0x1c0, x) let y := calldataload(0x184) mstore(0x1e0, y) success := and(validate_ec_point(x, y), success) } { let x := calldataload(0x1a4) mstore(0x200, x) let y := calldataload(0x1c4) mstore(0x220, y) success := and(validate_ec_point(x, y), success) } { let x := calldataload(0x1e4) mstore(0x240, x) let y := calldataload(0x204) mstore(0x260, y) success := and(validate_ec_point(x, y), success) } { let x := calldataload(0x224) mstore(0x280, x) let y := calldataload(0x244) mstore(0x2a0, y) success := and(validate_ec_point(x, y), success) } { let x := calldataload(0x264) mstore(0x2c0, x) let y := calldataload(0x284) mstore(0x2e0, y) success := and(validate_ec_point(x, y), success) }mstore(0x300, keccak256(0x80, 640)){ let hash := mload(0x300) mstore(0x320, mod(hash, f_q)) mstore(0x340, hash) } { let x := calldataload(0x2a4) mstore(0x360, x) let y := calldataload(0x2c4) mstore(0x380, y) success := and(validate_ec_point(x, y), success) } { let x := calldataload(0x2e4) mstore(0x3a0, x) let y := calldataload(0x304) mstore(0x3c0, y) success := and(validate_ec_point(x, y), success) } { let x := calldataload(0x324) mstore(0x3e0, x) let y := calldataload(0x344) mstore(0x400, y) success := and(validate_ec_point(x, y), success) } { let x := calldataload(0x364) mstore(0x420, x) let y := calldataload(0x384) mstore(0x440, y) success := and(validate_ec_point(x, y), success) }mstore(0x460, keccak256(0x340, 288)){ let hash := mload(0x460) mstore(0x480, mod(hash, f_q)) mstore(0x4a0, hash) }mstore8(0x4c0, 1)mstore(0x4c0, keccak256(0x4a0, 33)){ let hash := mload(0x4c0) mstore(0x4e0, mod(hash, f_q)) mstore(0x500, hash) } { let x := calldataload(0x3a4) mstore(0x520, x) let y := calldataload(0x3c4) mstore(0x540, y) success := and(validate_ec_point(x, y), success) } { let x := calldataload(0x3e4) mstore(0x560, x) let y := calldataload(0x404) mstore(0x580, y) success := and(validate_ec_point(x, y), success) } { let x := calldataload(0x424) mstore(0x5a0, x) let y := calldataload(0x444) mstore(0x5c0, y) success := and(validate_ec_point(x, y), success) } { let x := calldataload(0x464) mstore(0x5e0, x) let y := calldataload(0x484) mstore(0x600, y) success := and(validate_ec_point(x, y), success) } { let x := calldataload(0x4a4) mstore(0x620, x) let y := calldataload(0x4c4) mstore(0x640, y) success := and(validate_ec_point(x, y), success) } { let x := calldataload(0x4e4) mstore(0x660, x) let y := calldataload(0x504) mstore(0x680, y) success := and(validate_ec_point(x, y), success) } { let x := calldataload(0x524) mstore(0x6a0, x) let y := calldataload(0x544) mstore(0x6c0, y) success := and(validate_ec_point(x, y), success) } { let x := calldataload(0x564) mstore(0x6e0, x) let y := calldataload(0x584) mstore(0x700, y) success := and(validate_ec_point(x, y), success) } { let x := calldataload(0x5a4) mstore(0x720, x) let y := calldataload(0x5c4) mstore(0x740, y) success := and(validate_ec_point(x, y), success) }mstore(0x760, keccak256(0x500, 608)){ let hash := mload(0x760) mstore(0x780, mod(hash, f_q)) mstore(0x7a0, hash) } { let x := calldataload(0x5e4) mstore(0x7c0, x) let y := calldataload(0x604) mstore(0x7e0, y) success := and(validate_ec_point(x, y), success) } { let x := calldataload(0x624) mstore(0x800, x) let y := calldataload(0x644) mstore(0x820, y) success := and(validate_ec_point(x, y), success) } { let x := calldataload(0x664) mstore(0x840, x) let y := calldataload(0x684) mstore(0x860, y) success := and(validate_ec_point(x, y), success) }mstore(0x880, keccak256(0x7a0, 224)){ let hash := mload(0x880) mstore(0x8a0, mod(hash, f_q)) mstore(0x8c0, hash) }mstore(0x8e0, mod(calldataload(0x6a4), f_q))mstore(0x900, mod(calldataload(0x6c4), f_q))mstore(0x920, mod(calldataload(0x6e4), f_q))mstore(0x940, mod(calldataload(0x704), f_q))mstore(0x960, mod(calldataload(0x724), f_q))mstore(0x980, mod(calldataload(0x744), f_q))mstore(0x9a0, mod(calldataload(0x764), f_q))mstore(0x9c0, mod(calldataload(0x784), f_q))mstore(0x9e0, mod(calldataload(0x7a4), f_q))mstore(0xa00, mod(calldataload(0x7c4), f_q))mstore(0xa20, mod(calldataload(0x7e4), f_q))mstore(0xa40, mod(calldataload(0x804), f_q))mstore(0xa60, mod(calldataload(0x824), f_q))mstore(0xa80, mod(calldataload(0x844), f_q))mstore(0xaa0, mod(calldataload(0x864), f_q))mstore(0xac0, mod(calldataload(0x884), f_q))mstore(0xae0, mod(calldataload(0x8a4), f_q))mstore(0xb00, mod(calldataload(0x8c4), f_q))mstore(0xb20, mod(calldataload(0x8e4), f_q))mstore(0xb40, mod(calldataload(0x904), f_q))mstore(0xb60, mod(calldataload(0x924), f_q))mstore(0xb80, mod(calldataload(0x944), f_q))mstore(0xba0, mod(calldataload(0x964), f_q))mstore(0xbc0, mod(calldataload(0x984), f_q))mstore(0xbe0, mod(calldataload(0x9a4), f_q))mstore(0xc00, mod(calldataload(0x9c4), f_q))mstore(0xc20, mod(calldataload(0x9e4), f_q))mstore(0xc40, mod(calldataload(0xa04), f_q))mstore(0xc60, mod(calldataload(0xa24), f_q))mstore(0xc80, mod(calldataload(0xa44), f_q))mstore(0xca0, mod(calldataload(0xa64), f_q))mstore(0xcc0, mod(calldataload(0xa84), f_q))mstore(0xce0, mod(calldataload(0xaa4), f_q))mstore(0xd00, mod(calldataload(0xac4), f_q))mstore(0xd20, mod(calldataload(0xae4), f_q))mstore(0xd40, mod(calldataload(0xb04), f_q))mstore(0xd60, mod(calldataload(0xb24), f_q))mstore(0xd80, mod(calldataload(0xb44), f_q))mstore(0xda0, mod(calldataload(0xb64), f_q))mstore(0xdc0, mod(calldataload(0xb84), f_q))mstore(0xde0, mod(calldataload(0xba4), f_q))mstore(0xe00, mod(calldataload(0xbc4), f_q))mstore(0xe20, mod(calldataload(0xbe4), f_q))mstore(0xe40, mod(calldataload(0xc04), f_q))mstore(0xe60, mod(calldataload(0xc24), f_q))mstore(0xe80, mod(calldataload(0xc44), f_q))mstore(0xea0, mod(calldataload(0xc64), f_q))mstore(0xec0, mod(calldataload(0xc84), f_q))mstore(0xee0, mod(calldataload(0xca4), f_q))mstore(0xf00, mod(calldataload(0xcc4), f_q))mstore(0xf20, mod(calldataload(0xce4), f_q))mstore(0xf40, mod(calldataload(0xd04), f_q))mstore(0xf60, mod(calldataload(0xd24), f_q))mstore(0xf80, mod(calldataload(0xd44), f_q))mstore(0xfa0, mod(calldataload(0xd64), f_q))mstore(0xfc0, mod(calldataload(0xd84), f_q))mstore(0xfe0, mod(calldataload(0xda4), f_q))mstore(0x1000, mod(calldataload(0xdc4), f_q))mstore(0x1020, mod(calldataload(0xde4), f_q))mstore(0x1040, mod(calldataload(0xe04), f_q))mstore(0x1060, mod(calldataload(0xe24), f_q))mstore(0x1080, mod(calldataload(0xe44), f_q))mstore(0x10a0, mod(calldataload(0xe64), f_q))mstore(0x10c0, mod(calldataload(0xe84), f_q))mstore(0x10e0, mod(calldataload(0xea4), f_q))mstore(0x1100, mod(calldataload(0xec4), f_q))mstore(0x1120, mod(calldataload(0xee4), f_q))mstore(0x1140, mod(calldataload(0xf04), f_q))mstore(0x1160, mod(calldataload(0xf24), f_q))mstore(0x1180, mod(calldataload(0xf44), f_q))mstore(0x11a0, mod(calldataload(0xf64), f_q))mstore(0x11c0, mod(calldataload(0xf84), f_q))mstore(0x11e0, mod(calldataload(0xfa4), f_q))mstore(0x1200, mod(calldataload(0xfc4), f_q))mstore(0x1220, mod(calldataload(0xfe4), f_q))mstore(0x1240, mod(calldataload(0x1004), f_q))mstore(0x1260, keccak256(0x8c0, 2464)){ let hash := mload(0x1260) mstore(0x1280, mod(hash, f_q)) mstore(0x12a0, hash) }mstore8(0x12c0, 1)mstore(0x12c0, keccak256(0x12a0, 33)){ let hash := mload(0x12c0) mstore(0x12e0, mod(hash, f_q)) mstore(0x1300, hash) } { let x := calldataload(0x1024) mstore(0x1320, x) let y := calldataload(0x1044) mstore(0x1340, y) success := and(validate_ec_point(x, y), success) }mstore(0x1360, keccak256(0x1300, 96)){ let hash := mload(0x1360) mstore(0x1380, mod(hash, f_q)) mstore(0x13a0, hash) } { let x := calldataload(0x1064) mstore(0x13c0, x) let y := calldataload(0x1084) mstore(0x13e0, y) success := and(validate_ec_point(x, y), success) }mstore(0x1400, mulmod(mload(0x8a0), mload(0x8a0), f_q))mstore(0x1420, mulmod(mload(0x1400), mload(0x1400), f_q))mstore(0x1440, mulmod(mload(0x1420), mload(0x1420), f_q))mstore(0x1460, mulmod(mload(0x1440), mload(0x1440), f_q))mstore(0x1480, mulmod(mload(0x1460), mload(0x1460), f_q))mstore(0x14a0, mulmod(mload(0x1480), mload(0x1480), f_q))mstore(0x14c0, mulmod(mload(0x14a0), mload(0x14a0), f_q))mstore(0x14e0, mulmod(mload(0x14c0), mload(0x14c0), f_q))mstore(0x1500, mulmod(mload(0x14e0), mload(0x14e0), f_q))mstore(0x1520, mulmod(mload(0x1500), mload(0x1500), f_q))mstore(0x1540, mulmod(mload(0x1520), mload(0x1520), f_q))mstore(0x1560, mulmod(mload(0x1540), mload(0x1540), f_q))mstore(0x1580, mulmod(mload(0x1560), mload(0x1560), f_q))mstore(0x15a0, mulmod(mload(0x1580), mload(0x1580), f_q))mstore(0x15c0, mulmod(mload(0x15a0), mload(0x15a0), f_q))mstore(0x15e0, mulmod(mload(0x15c0), mload(0x15c0), f_q))mstore(0x1600, mulmod(mload(0x15e0), mload(0x15e0), f_q))mstore(0x1620, mulmod(mload(0x1600), mload(0x1600), f_q))mstore(0x1640, mulmod(mload(0x1620), mload(0x1620), f_q))mstore(0x1660, mulmod(mload(0x1640), mload(0x1640), f_q))mstore(0x1680, mulmod(mload(0x1660), mload(0x1660), f_q))mstore(0x16a0, mulmod(mload(0x1680), mload(0x1680), f_q))mstore(0x16c0, addmod(mload(0x16a0), 21888242871839275222246405745257275088548364400416034343698204186575808495616, f_q))mstore(0x16e0, mulmod(mload(0x16c0), 21888237653275510688422624196183639687472264873923820041627027729598873448513, f_q))mstore(0x1700, mulmod(mload(0x16e0), 13225785879531581993054172815365636627224369411478295502904397545373139154045, f_q))mstore(0x1720, addmod(mload(0x8a0), 8662456992307693229192232929891638461323994988937738840793806641202669341572, f_q))mstore(0x1740, mulmod(mload(0x16e0), 10939663269433627367777756708678102241564365262857670666700619874077960926249, f_q))mstore(0x1760, addmod(mload(0x8a0), 10948579602405647854468649036579172846983999137558363676997584312497847569368, f_q))mstore(0x1780, mulmod(mload(0x16e0), 11016257578652593686382655500910603527869149377564754001549454008164059876499, f_q))mstore(0x17a0, addmod(mload(0x8a0), 10871985293186681535863750244346671560679215022851280342148750178411748619118, f_q))mstore(0x17c0, mulmod(mload(0x16e0), 15402826414547299628414612080036060696555554914079673875872749760617770134879, f_q))mstore(0x17e0, addmod(mload(0x8a0), 6485416457291975593831793665221214391992809486336360467825454425958038360738, f_q))mstore(0x1800, mulmod(mload(0x16e0), 21710372849001950800533397158415938114909991150039389063546734567764856596059, f_q))mstore(0x1820, addmod(mload(0x8a0), 177870022837324421713008586841336973638373250376645280151469618810951899558, f_q))mstore(0x1840, mulmod(mload(0x16e0), 2785514556381676080176937710880804108647911392478702105860685610379369825016, f_q))mstore(0x1860, addmod(mload(0x8a0), 19102728315457599142069468034376470979900453007937332237837518576196438670601, f_q))mstore(0x1880, mulmod(mload(0x16e0), 8734126352828345679573237859165904705806588461301144420590422589042130041188, f_q))mstore(0x18a0, addmod(mload(0x8a0), 13154116519010929542673167886091370382741775939114889923107781597533678454429, f_q))mstore(0x18c0, mulmod(mload(0x16e0), 1, f_q))mstore(0x18e0, addmod(mload(0x8a0), 21888242871839275222246405745257275088548364400416034343698204186575808495616, f_q)){ let prod := mload(0x1720) prod := mulmod(mload(0x1760), prod, f_q) mstore(0x1900, prod) prod := mulmod(mload(0x17a0), prod, f_q) mstore(0x1920, prod) prod := mulmod(mload(0x17e0), prod, f_q) mstore(0x1940, prod) prod := mulmod(mload(0x1820), prod, f_q) mstore(0x1960, prod) prod := mulmod(mload(0x1860), prod, f_q) mstore(0x1980, prod) prod := mulmod(mload(0x18a0), prod, f_q) mstore(0x19a0, prod) prod := mulmod(mload(0x18e0), prod, f_q) mstore(0x19c0, prod) prod := mulmod(mload(0x16c0), prod, f_q) mstore(0x19e0, prod) }mstore(0x1a20, 32)mstore(0x1a40, 32)mstore(0x1a60, 32)mstore(0x1a80, mload(0x19e0))mstore(0x1aa0, 21888242871839275222246405745257275088548364400416034343698204186575808495615)mstore(0x1ac0, 21888242871839275222246405745257275088548364400416034343698204186575808495617)success := and(eq(staticcall(gas(), 0x5, 0x1a20, 0xc0, 0x1a00, 0x20), 1), success){ let inv := mload(0x1a00) let v v := mload(0x16c0) mstore(0x16c0, mulmod(mload(0x19c0), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x18e0) mstore(0x18e0, mulmod(mload(0x19a0), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x18a0) mstore(0x18a0, mulmod(mload(0x1980), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x1860) mstore(0x1860, mulmod(mload(0x1960), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x1820) mstore(0x1820, mulmod(mload(0x1940), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x17e0) mstore(0x17e0, mulmod(mload(0x1920), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x17a0) mstore(0x17a0, mulmod(mload(0x1900), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x1760) mstore(0x1760, mulmod(mload(0x1720), inv, f_q)) inv := mulmod(v, inv, f_q) mstore(0x1720, inv) }mstore(0x1ae0, mulmod(mload(0x1700), mload(0x1720), f_q))mstore(0x1b00, mulmod(mload(0x1740), mload(0x1760), f_q))mstore(0x1b20, mulmod(mload(0x1780), mload(0x17a0), f_q))mstore(0x1b40, mulmod(mload(0x17c0), mload(0x17e0), f_q))mstore(0x1b60, mulmod(mload(0x1800), mload(0x1820), f_q))mstore(0x1b80, mulmod(mload(0x1840), mload(0x1860), f_q))mstore(0x1ba0, mulmod(mload(0x1880), mload(0x18a0), f_q))mstore(0x1bc0, mulmod(mload(0x18c0), mload(0x18e0), f_q)){ let result := mulmod(mload(0x1bc0), mload(0xa0), f_q)mstore(0x1be0, result) }mstore(0x1c00, mulmod(mload(0x920), mload(0x900), f_q))mstore(0x1c20, addmod(mload(0x8e0), mload(0x1c00), f_q))mstore(0x1c40, addmod(mload(0x1c20), sub(f_q, mload(0x940)), f_q))mstore(0x1c60, mulmod(mload(0x1c40), mload(0xcc0), f_q))mstore(0x1c80, mulmod(mload(0x780), mload(0x1c60), f_q))mstore(0x1ca0, mulmod(mload(0x9a0), mload(0x980), f_q))mstore(0x1cc0, addmod(mload(0x960), mload(0x1ca0), f_q))mstore(0x1ce0, addmod(mload(0x1cc0), sub(f_q, mload(0x9c0)), f_q))mstore(0x1d00, mulmod(mload(0x1ce0), mload(0xce0), f_q))mstore(0x1d20, addmod(mload(0x1c80), mload(0x1d00), f_q))mstore(0x1d40, mulmod(mload(0x780), mload(0x1d20), f_q))mstore(0x1d60, mulmod(mload(0xa20), mload(0xa00), f_q))mstore(0x1d80, addmod(mload(0x9e0), mload(0x1d60), f_q))mstore(0x1da0, addmod(mload(0x1d80), sub(f_q, mload(0xa40)), f_q))mstore(0x1dc0, mulmod(mload(0x1da0), mload(0xd00), f_q))mstore(0x1de0, addmod(mload(0x1d40), mload(0x1dc0), f_q))mstore(0x1e00, mulmod(mload(0x780), mload(0x1de0), f_q))mstore(0x1e20, mulmod(mload(0xaa0), mload(0xa80), f_q))mstore(0x1e40, addmod(mload(0xa60), mload(0x1e20), f_q))mstore(0x1e60, addmod(mload(0x1e40), sub(f_q, mload(0xac0)), f_q))mstore(0x1e80, mulmod(mload(0x1e60), mload(0xd20), f_q))mstore(0x1ea0, addmod(mload(0x1e00), mload(0x1e80), f_q))mstore(0x1ec0, mulmod(mload(0x780), mload(0x1ea0), f_q))mstore(0x1ee0, mulmod(mload(0xb20), mload(0xb00), f_q))mstore(0x1f00, addmod(mload(0xae0), mload(0x1ee0), f_q))mstore(0x1f20, addmod(mload(0x1f00), sub(f_q, mload(0xb40)), f_q))mstore(0x1f40, mulmod(mload(0x1f20), mload(0xd40), f_q))mstore(0x1f60, addmod(mload(0x1ec0), mload(0x1f40), f_q))mstore(0x1f80, mulmod(mload(0x780), mload(0x1f60), f_q))mstore(0x1fa0, mulmod(mload(0xba0), mload(0xb80), f_q))mstore(0x1fc0, addmod(mload(0xb60), mload(0x1fa0), f_q))mstore(0x1fe0, addmod(mload(0x1fc0), sub(f_q, mload(0xbc0)), f_q))mstore(0x2000, mulmod(mload(0x1fe0), mload(0xd60), f_q))mstore(0x2020, addmod(mload(0x1f80), mload(0x2000), f_q))mstore(0x2040, mulmod(mload(0x780), mload(0x2020), f_q))mstore(0x2060, addmod(1, sub(f_q, mload(0xf00)), f_q))mstore(0x2080, mulmod(mload(0x2060), mload(0x1bc0), f_q))mstore(0x20a0, addmod(mload(0x2040), mload(0x2080), f_q))mstore(0x20c0, mulmod(mload(0x780), mload(0x20a0), f_q))mstore(0x20e0, mulmod(mload(0x10e0), mload(0x10e0), f_q))mstore(0x2100, addmod(mload(0x20e0), sub(f_q, mload(0x10e0)), f_q))mstore(0x2120, mulmod(mload(0x2100), mload(0x1ae0), f_q))mstore(0x2140, addmod(mload(0x20c0), mload(0x2120), f_q))mstore(0x2160, mulmod(mload(0x780), mload(0x2140), f_q))mstore(0x2180, addmod(mload(0xf60), sub(f_q, mload(0xf40)), f_q))mstore(0x21a0, mulmod(mload(0x2180), mload(0x1bc0), f_q))mstore(0x21c0, addmod(mload(0x2160), mload(0x21a0), f_q))mstore(0x21e0, mulmod(mload(0x780), mload(0x21c0), f_q))mstore(0x2200, addmod(mload(0xfc0), sub(f_q, mload(0xfa0)), f_q))mstore(0x2220, mulmod(mload(0x2200), mload(0x1bc0), f_q))mstore(0x2240, addmod(mload(0x21e0), mload(0x2220), f_q))mstore(0x2260, mulmod(mload(0x780), mload(0x2240), f_q))mstore(0x2280, addmod(mload(0x1020), sub(f_q, mload(0x1000)), f_q))mstore(0x22a0, mulmod(mload(0x2280), mload(0x1bc0), f_q))mstore(0x22c0, addmod(mload(0x2260), mload(0x22a0), f_q))mstore(0x22e0, mulmod(mload(0x780), mload(0x22c0), f_q))mstore(0x2300, addmod(mload(0x1080), sub(f_q, mload(0x1060)), f_q))mstore(0x2320, mulmod(mload(0x2300), mload(0x1bc0), f_q))mstore(0x2340, addmod(mload(0x22e0), mload(0x2320), f_q))mstore(0x2360, mulmod(mload(0x780), mload(0x2340), f_q))mstore(0x2380, addmod(mload(0x10e0), sub(f_q, mload(0x10c0)), f_q))mstore(0x23a0, mulmod(mload(0x2380), mload(0x1bc0), f_q))mstore(0x23c0, addmod(mload(0x2360), mload(0x23a0), f_q))mstore(0x23e0, mulmod(mload(0x780), mload(0x23c0), f_q))mstore(0x2400, addmod(1, sub(f_q, mload(0x1ae0)), f_q))mstore(0x2420, addmod(mload(0x1b00), mload(0x1b20), f_q))mstore(0x2440, addmod(mload(0x2420), mload(0x1b40), f_q))mstore(0x2460, addmod(mload(0x2440), mload(0x1b60), f_q))mstore(0x2480, addmod(mload(0x2460), mload(0x1b80), f_q))mstore(0x24a0, addmod(mload(0x2480), mload(0x1ba0), f_q))mstore(0x24c0, addmod(mload(0x2400), sub(f_q, mload(0x24a0)), f_q))mstore(0x24e0, mulmod(mload(0xda0), mload(0x480), f_q))mstore(0x2500, addmod(mload(0xc40), mload(0x24e0), f_q))mstore(0x2520, addmod(mload(0x2500), mload(0x4e0), f_q))mstore(0x2540, mulmod(mload(0xdc0), mload(0x480), f_q))mstore(0x2560, addmod(mload(0x8e0), mload(0x2540), f_q))mstore(0x2580, addmod(mload(0x2560), mload(0x4e0), f_q))mstore(0x25a0, mulmod(mload(0x2580), mload(0x2520), f_q))mstore(0x25c0, mulmod(mload(0x25a0), mload(0xf20), f_q))mstore(0x25e0, mulmod(1, mload(0x480), f_q))mstore(0x2600, mulmod(mload(0x8a0), mload(0x25e0), f_q))mstore(0x2620, addmod(mload(0xc40), mload(0x2600), f_q))mstore(0x2640, addmod(mload(0x2620), mload(0x4e0), f_q))mstore(0x2660, mulmod(4131629893567559867359510883348571134090853742863529169391034518566172092834, mload(0x480), f_q))mstore(0x2680, mulmod(mload(0x8a0), mload(0x2660), f_q))mstore(0x26a0, addmod(mload(0x8e0), mload(0x2680), f_q))mstore(0x26c0, addmod(mload(0x26a0), mload(0x4e0), f_q))mstore(0x26e0, mulmod(mload(0x26c0), mload(0x2640), f_q))mstore(0x2700, mulmod(mload(0x26e0), mload(0xf00), f_q))mstore(0x2720, addmod(mload(0x25c0), sub(f_q, mload(0x2700)), f_q))mstore(0x2740, mulmod(mload(0x2720), mload(0x24c0), f_q))mstore(0x2760, addmod(mload(0x23e0), mload(0x2740), f_q))mstore(0x2780, mulmod(mload(0x780), mload(0x2760), f_q))mstore(0x27a0, mulmod(mload(0xde0), mload(0x480), f_q))mstore(0x27c0, addmod(mload(0x960), mload(0x27a0), f_q))mstore(0x27e0, addmod(mload(0x27c0), mload(0x4e0), f_q))mstore(0x2800, mulmod(mload(0xe00), mload(0x480), f_q))mstore(0x2820, addmod(mload(0x9e0), mload(0x2800), f_q))mstore(0x2840, addmod(mload(0x2820), mload(0x4e0), f_q))mstore(0x2860, mulmod(mload(0x2840), mload(0x27e0), f_q))mstore(0x2880, mulmod(mload(0x2860), mload(0xf80), f_q))mstore(0x28a0, mulmod(8910878055287538404433155982483128285667088683464058436815641868457422632747, mload(0x480), f_q))mstore(0x28c0, mulmod(mload(0x8a0), mload(0x28a0), f_q))mstore(0x28e0, addmod(mload(0x960), mload(0x28c0), f_q))mstore(0x2900, addmod(mload(0x28e0), mload(0x4e0), f_q))mstore(0x2920, mulmod(11166246659983828508719468090013646171463329086121580628794302409516816350802, mload(0x480), f_q))mstore(0x2940, mulmod(mload(0x8a0), mload(0x2920), f_q))mstore(0x2960, addmod(mload(0x9e0), mload(0x2940), f_q))mstore(0x2980, addmod(mload(0x2960), mload(0x4e0), f_q))mstore(0x29a0, mulmod(mload(0x2980), mload(0x2900), f_q))mstore(0x29c0, mulmod(mload(0x29a0), mload(0xf60), f_q))mstore(0x29e0, addmod(mload(0x2880), sub(f_q, mload(0x29c0)), f_q))mstore(0x2a00, mulmod(mload(0x29e0), mload(0x24c0), f_q))mstore(0x2a20, addmod(mload(0x2780), mload(0x2a00), f_q))mstore(0x2a40, mulmod(mload(0x780), mload(0x2a20), f_q))mstore(0x2a60, mulmod(mload(0xe20), mload(0x480), f_q))mstore(0x2a80, addmod(mload(0xa60), mload(0x2a60), f_q))mstore(0x2aa0, addmod(mload(0x2a80), mload(0x4e0), f_q))mstore(0x2ac0, mulmod(mload(0xe40), mload(0x480), f_q))mstore(0x2ae0, addmod(mload(0xae0), mload(0x2ac0), f_q))mstore(0x2b00, addmod(mload(0x2ae0), mload(0x4e0), f_q))mstore(0x2b20, mulmod(mload(0x2b00), mload(0x2aa0), f_q))mstore(0x2b40, mulmod(mload(0x2b20), mload(0xfe0), f_q))mstore(0x2b60, mulmod(284840088355319032285349970403338060113257071685626700086398481893096618818, mload(0x480), f_q))mstore(0x2b80, mulmod(mload(0x8a0), mload(0x2b60), f_q))mstore(0x2ba0, addmod(mload(0xa60), mload(0x2b80), f_q))mstore(0x2bc0, addmod(mload(0x2ba0), mload(0x4e0), f_q))mstore(0x2be0, mulmod(21134065618345176623193549882539580312263652408302468683943992798037078993309, mload(0x480), f_q))mstore(0x2c00, mulmod(mload(0x8a0), mload(0x2be0), f_q))mstore(0x2c20, addmod(mload(0xae0), mload(0x2c00), f_q))mstore(0x2c40, addmod(mload(0x2c20), mload(0x4e0), f_q))mstore(0x2c60, mulmod(mload(0x2c40), mload(0x2bc0), f_q))mstore(0x2c80, mulmod(mload(0x2c60), mload(0xfc0), f_q))mstore(0x2ca0, addmod(mload(0x2b40), sub(f_q, mload(0x2c80)), f_q))mstore(0x2cc0, mulmod(mload(0x2ca0), mload(0x24c0), f_q))mstore(0x2ce0, addmod(mload(0x2a40), mload(0x2cc0), f_q))mstore(0x2d00, mulmod(mload(0x780), mload(0x2ce0), f_q))mstore(0x2d20, mulmod(mload(0xe60), mload(0x480), f_q))mstore(0x2d40, addmod(mload(0xb60), mload(0x2d20), f_q))mstore(0x2d60, addmod(mload(0x2d40), mload(0x4e0), f_q))mstore(0x2d80, mulmod(mload(0xe80), mload(0x480), f_q))mstore(0x2da0, addmod(mload(0xbe0), mload(0x2d80), f_q))mstore(0x2dc0, addmod(mload(0x2da0), mload(0x4e0), f_q))mstore(0x2de0, mulmod(mload(0x2dc0), mload(0x2d60), f_q))mstore(0x2e00, mulmod(mload(0x2de0), mload(0x1040), f_q))mstore(0x2e20, mulmod(5625741653535312224677218588085279924365897425605943700675464992185016992283, mload(0x480), f_q))mstore(0x2e40, mulmod(mload(0x8a0), mload(0x2e20), f_q))mstore(0x2e60, addmod(mload(0xb60), mload(0x2e40), f_q))mstore(0x2e80, addmod(mload(0x2e60), mload(0x4e0), f_q))mstore(0x2ea0, mulmod(14704729814417906439424896605881467874595262020190401576785074330126828718155, mload(0x480), f_q))mstore(0x2ec0, mulmod(mload(0x8a0), mload(0x2ea0), f_q))mstore(0x2ee0, addmod(mload(0xbe0), mload(0x2ec0), f_q))mstore(0x2f00, addmod(mload(0x2ee0), mload(0x4e0), f_q))mstore(0x2f20, mulmod(mload(0x2f00), mload(0x2e80), f_q))mstore(0x2f40, mulmod(mload(0x2f20), mload(0x1020), f_q))mstore(0x2f60, addmod(mload(0x2e00), sub(f_q, mload(0x2f40)), f_q))mstore(0x2f80, mulmod(mload(0x2f60), mload(0x24c0), f_q))mstore(0x2fa0, addmod(mload(0x2d00), mload(0x2f80), f_q))mstore(0x2fc0, mulmod(mload(0x780), mload(0x2fa0), f_q))mstore(0x2fe0, mulmod(mload(0xea0), mload(0x480), f_q))mstore(0x3000, addmod(mload(0xc00), mload(0x2fe0), f_q))mstore(0x3020, addmod(mload(0x3000), mload(0x4e0), f_q))mstore(0x3040, mulmod(mload(0xec0), mload(0x480), f_q))mstore(0x3060, addmod(mload(0xc20), mload(0x3040), f_q))mstore(0x3080, addmod(mload(0x3060), mload(0x4e0), f_q))mstore(0x30a0, mulmod(mload(0x3080), mload(0x3020), f_q))mstore(0x30c0, mulmod(mload(0x30a0), mload(0x10a0), f_q))mstore(0x30e0, mulmod(8343274462013750416000956870576256937330525306073862550863787263304548803879, mload(0x480), f_q))mstore(0x3100, mulmod(mload(0x8a0), mload(0x30e0), f_q))mstore(0x3120, addmod(mload(0xc00), mload(0x3100), f_q))mstore(0x3140, addmod(mload(0x3120), mload(0x4e0), f_q))mstore(0x3160, mulmod(20928372310071051017340352686640453451620397549739756658327314209761852842004, mload(0x480), f_q))mstore(0x3180, mulmod(mload(0x8a0), mload(0x3160), f_q))mstore(0x31a0, addmod(mload(0xc20), mload(0x3180), f_q))mstore(0x31c0, addmod(mload(0x31a0), mload(0x4e0), f_q))mstore(0x31e0, mulmod(mload(0x31c0), mload(0x3140), f_q))mstore(0x3200, mulmod(mload(0x31e0), mload(0x1080), f_q))mstore(0x3220, addmod(mload(0x30c0), sub(f_q, mload(0x3200)), f_q))mstore(0x3240, mulmod(mload(0x3220), mload(0x24c0), f_q))mstore(0x3260, addmod(mload(0x2fc0), mload(0x3240), f_q))mstore(0x3280, mulmod(mload(0x780), mload(0x3260), f_q))mstore(0x32a0, mulmod(mload(0xee0), mload(0x480), f_q))mstore(0x32c0, addmod(mload(0x1be0), mload(0x32a0), f_q))mstore(0x32e0, addmod(mload(0x32c0), mload(0x4e0), f_q))mstore(0x3300, mulmod(mload(0x32e0), mload(0x1100), f_q))mstore(0x3320, mulmod(15845651941796975697993789271154426079663327509658641548785793587449119139335, mload(0x480), f_q))mstore(0x3340, mulmod(mload(0x8a0), mload(0x3320), f_q))mstore(0x3360, addmod(mload(0x1be0), mload(0x3340), f_q))mstore(0x3380, addmod(mload(0x3360), mload(0x4e0), f_q))mstore(0x33a0, mulmod(mload(0x3380), mload(0x10e0), f_q))mstore(0x33c0, addmod(mload(0x3300), sub(f_q, mload(0x33a0)), f_q))mstore(0x33e0, mulmod(mload(0x33c0), mload(0x24c0), f_q))mstore(0x3400, addmod(mload(0x3280), mload(0x33e0), f_q))mstore(0x3420, mulmod(mload(0x780), mload(0x3400), f_q))mstore(0x3440, addmod(1, sub(f_q, mload(0x1120)), f_q))mstore(0x3460, mulmod(mload(0x3440), mload(0x1bc0), f_q))mstore(0x3480, addmod(mload(0x3420), mload(0x3460), f_q))mstore(0x34a0, mulmod(mload(0x780), mload(0x3480), f_q))mstore(0x34c0, mulmod(mload(0x1120), mload(0x1120), f_q))mstore(0x34e0, addmod(mload(0x34c0), sub(f_q, mload(0x1120)), f_q))mstore(0x3500, mulmod(mload(0x34e0), mload(0x1ae0), f_q))mstore(0x3520, addmod(mload(0x34a0), mload(0x3500), f_q))mstore(0x3540, mulmod(mload(0x780), mload(0x3520), f_q))mstore(0x3560, addmod(mload(0x1160), mload(0x480), f_q))mstore(0x3580, mulmod(mload(0x3560), mload(0x1140), f_q))mstore(0x35a0, addmod(mload(0x11a0), mload(0x4e0), f_q))mstore(0x35c0, mulmod(mload(0x35a0), mload(0x3580), f_q))mstore(0x35e0, addmod(mload(0xbe0), mload(0x480), f_q))mstore(0x3600, mulmod(mload(0x35e0), mload(0x1120), f_q))mstore(0x3620, addmod(mload(0xc60), mload(0x4e0), f_q))mstore(0x3640, mulmod(mload(0x3620), mload(0x3600), f_q))mstore(0x3660, addmod(mload(0x35c0), sub(f_q, mload(0x3640)), f_q))mstore(0x3680, mulmod(mload(0x3660), mload(0x24c0), f_q))mstore(0x36a0, addmod(mload(0x3540), mload(0x3680), f_q))mstore(0x36c0, mulmod(mload(0x780), mload(0x36a0), f_q))mstore(0x36e0, addmod(mload(0x1160), sub(f_q, mload(0x11a0)), f_q))mstore(0x3700, mulmod(mload(0x36e0), mload(0x1bc0), f_q))mstore(0x3720, addmod(mload(0x36c0), mload(0x3700), f_q))mstore(0x3740, mulmod(mload(0x780), mload(0x3720), f_q))mstore(0x3760, mulmod(mload(0x36e0), mload(0x24c0), f_q))mstore(0x3780, addmod(mload(0x1160), sub(f_q, mload(0x1180)), f_q))mstore(0x37a0, mulmod(mload(0x3780), mload(0x3760), f_q))mstore(0x37c0, addmod(mload(0x3740), mload(0x37a0), f_q))mstore(0x37e0, mulmod(mload(0x780), mload(0x37c0), f_q))mstore(0x3800, addmod(1, sub(f_q, mload(0x11c0)), f_q))mstore(0x3820, mulmod(mload(0x3800), mload(0x1bc0), f_q))mstore(0x3840, addmod(mload(0x37e0), mload(0x3820), f_q))mstore(0x3860, mulmod(mload(0x780), mload(0x3840), f_q))mstore(0x3880, mulmod(mload(0x11c0), mload(0x11c0), f_q))mstore(0x38a0, addmod(mload(0x3880), sub(f_q, mload(0x11c0)), f_q))mstore(0x38c0, mulmod(mload(0x38a0), mload(0x1ae0), f_q))mstore(0x38e0, addmod(mload(0x3860), mload(0x38c0), f_q))mstore(0x3900, mulmod(mload(0x780), mload(0x38e0), f_q))mstore(0x3920, addmod(mload(0x1200), mload(0x480), f_q))mstore(0x3940, mulmod(mload(0x3920), mload(0x11e0), f_q))mstore(0x3960, addmod(mload(0x1240), mload(0x4e0), f_q))mstore(0x3980, mulmod(mload(0x3960), mload(0x3940), f_q))mstore(0x39a0, mulmod(mload(0x320), mload(0xc00), f_q))mstore(0x39c0, addmod(mload(0x39a0), mload(0xc20), f_q))mstore(0x39e0, addmod(mload(0x39c0), mload(0x480), f_q))mstore(0x3a00, mulmod(mload(0x39e0), mload(0x11c0), f_q))mstore(0x3a20, mulmod(mload(0x320), mload(0xc80), f_q))mstore(0x3a40, addmod(mload(0x3a20), mload(0xca0), f_q))mstore(0x3a60, addmod(mload(0x3a40), mload(0x4e0), f_q))mstore(0x3a80, mulmod(mload(0x3a60), mload(0x3a00), f_q))mstore(0x3aa0, addmod(mload(0x3980), sub(f_q, mload(0x3a80)), f_q))mstore(0x3ac0, mulmod(mload(0x3aa0), mload(0x24c0), f_q))mstore(0x3ae0, addmod(mload(0x3900), mload(0x3ac0), f_q))mstore(0x3b00, mulmod(mload(0x780), mload(0x3ae0), f_q))mstore(0x3b20, addmod(mload(0x1200), sub(f_q, mload(0x1240)), f_q))mstore(0x3b40, mulmod(mload(0x3b20), mload(0x1bc0), f_q))mstore(0x3b60, addmod(mload(0x3b00), mload(0x3b40), f_q))mstore(0x3b80, mulmod(mload(0x780), mload(0x3b60), f_q))mstore(0x3ba0, mulmod(mload(0x3b20), mload(0x24c0), f_q))mstore(0x3bc0, addmod(mload(0x1200), sub(f_q, mload(0x1220)), f_q))mstore(0x3be0, mulmod(mload(0x3bc0), mload(0x3ba0), f_q))mstore(0x3c00, addmod(mload(0x3b80), mload(0x3be0), f_q))mstore(0x3c20, mulmod(mload(0x16a0), mload(0x16a0), f_q))mstore(0x3c40, mulmod(mload(0x3c20), mload(0x16a0), f_q))mstore(0x3c60, mulmod(1, mload(0x16a0), f_q))mstore(0x3c80, mulmod(1, mload(0x3c20), f_q))mstore(0x3ca0, mulmod(mload(0x3c00), mload(0x16c0), f_q))mstore(0x3cc0, mulmod(mload(0x1400), mload(0x8a0), f_q))mstore(0x3ce0, mulmod(mload(0x3cc0), mload(0x8a0), f_q))mstore(0x3d00, mulmod(mload(0x8a0), 1, f_q))mstore(0x3d20, addmod(mload(0x1380), sub(f_q, mload(0x3d00)), f_q))mstore(0x3d40, mulmod(mload(0x8a0), 1426404432721484388505361748317961535523355871255605456897797744433766488507, f_q))mstore(0x3d60, addmod(mload(0x1380), sub(f_q, mload(0x3d40)), f_q))mstore(0x3d80, mulmod(mload(0x8a0), 8734126352828345679573237859165904705806588461301144420590422589042130041188, f_q))mstore(0x3da0, addmod(mload(0x1380), sub(f_q, mload(0x3d80)), f_q))mstore(0x3dc0, mulmod(mload(0x8a0), 11211301017135681023579411905410872569206244553457844956874280139879520583390, f_q))mstore(0x3de0, addmod(mload(0x1380), sub(f_q, mload(0x3dc0)), f_q))mstore(0x3e00, mulmod(mload(0x8a0), 12619617507853212586156872920672483948819476989779550311307282715684870266992, f_q))mstore(0x3e20, addmod(mload(0x1380), sub(f_q, mload(0x3e00)), f_q))mstore(0x3e40, mulmod(mload(0x8a0), 13225785879531581993054172815365636627224369411478295502904397545373139154045, f_q))mstore(0x3e60, addmod(mload(0x1380), sub(f_q, mload(0x3e40)), f_q)){ let result := mulmod(mload(0x1380), mulmod(mload(0x3cc0), 3544324119167359571073009690693121464267965232733679586767649244433889388945, f_q), f_q)result := addmod(mulmod(mload(0x8a0), mulmod(mload(0x3cc0), 18343918752671915651173396054564153624280399167682354756930554942141919106672, f_q), f_q), result, f_q)mstore(0x3e80, result) }{ let result := mulmod(mload(0x1380), mulmod(mload(0x3cc0), 3860370625838117017501327045244227871206764201116468958063324100051382735289, f_q), f_q)result := addmod(mulmod(mload(0x8a0), mulmod(mload(0x3cc0), 21616901807277407275624036604424346159916096890712898844034238973395610537327, f_q), f_q), result, f_q)mstore(0x3ea0, result) }{ let result := mulmod(mload(0x1380), mulmod(mload(0x3cc0), 21616901807277407275624036604424346159916096890712898844034238973395610537327, f_q), f_q)result := addmod(mulmod(mload(0x8a0), mulmod(mload(0x3cc0), 889236556954614024749610889108815341999962898269585485843658889664869519176, f_q), f_q), result, f_q)mstore(0x3ec0, result) }{ let result := mulmod(mload(0x1380), mulmod(mload(0x3cc0), 3209408481237076479025468386201293941554240476766691830436732310949352383503, f_q), f_q)result := addmod(mulmod(mload(0x8a0), mulmod(mload(0x3cc0), 12080394110851700286656425387058292751221637853580771255128961096834426654570, f_q), f_q), result, f_q)mstore(0x3ee0, result) }mstore(0x3f00, mulmod(1, mload(0x3d20), f_q))mstore(0x3f20, mulmod(mload(0x3f00), mload(0x3de0), f_q))mstore(0x3f40, mulmod(mload(0x3f20), mload(0x3d60), f_q))mstore(0x3f60, mulmod(mload(0x3f40), mload(0x3e20), f_q)){ let result := mulmod(mload(0x1380), 1, f_q)result := addmod(mulmod(mload(0x8a0), 21888242871839275222246405745257275088548364400416034343698204186575808495616, f_q), result, f_q)mstore(0x3f80, result) }{ let result := mulmod(mload(0x1380), mulmod(mload(0x1400), 8390819244605639573390577733158868133682115698337564550620146375401109684432, f_q), f_q)result := addmod(mulmod(mload(0x8a0), mulmod(mload(0x1400), 13497423627233635648855828012098406954866248702078469793078057811174698811185, f_q), f_q), result, f_q)mstore(0x3fa0, result) }{ let result := mulmod(mload(0x1380), mulmod(mload(0x1400), 14389468897523033212448771694851898440525479866834419679925499462425232628530, f_q), f_q)result := addmod(mulmod(mload(0x8a0), mulmod(mload(0x1400), 10771624105926513343199793365135253961557027396599172824137553349410803667382, f_q), f_q), result, f_q)mstore(0x3fc0, result) }{ let result := mulmod(mload(0x1380), mulmod(mload(0x1400), 8021781111580269725587432039983408559403601261632071736490564397134126857583, f_q), f_q)result := addmod(mulmod(mload(0x8a0), mulmod(mload(0x1400), 13263758384809315129424392494083758423780924407584659157289746760747196496964, f_q), f_q), result, f_q)mstore(0x3fe0, result) }mstore(0x4000, mulmod(mload(0x3f20), mload(0x3e60), f_q)){ let result := mulmod(mload(0x1380), mulmod(mload(0x8a0), 10676941854703594198666993839846402519342119846958189386823924046696287912228, f_q), f_q)result := addmod(mulmod(mload(0x8a0), mulmod(mload(0x8a0), 11211301017135681023579411905410872569206244553457844956874280139879520583389, f_q), f_q), result, f_q)mstore(0x4020, result) }{ let result := mulmod(mload(0x1380), mulmod(mload(0x8a0), 11211301017135681023579411905410872569206244553457844956874280139879520583389, f_q), f_q)result := addmod(mulmod(mload(0x8a0), mulmod(mload(0x8a0), 9784896584414196635074050157092911033682888682202239499976482395445754094883, f_q), f_q), result, f_q)mstore(0x4040, result) }{ let result := mulmod(mload(0x1380), mulmod(mload(0x8a0), 13154116519010929542673167886091370382741775939114889923107781597533678454430, f_q), f_q)result := addmod(mulmod(mload(0x8a0), mulmod(mload(0x8a0), 8734126352828345679573237859165904705806588461301144420590422589042130041187, f_q), f_q), result, f_q)mstore(0x4060, result) }{ let result := mulmod(mload(0x1380), mulmod(mload(0x8a0), 8734126352828345679573237859165904705806588461301144420590422589042130041187, f_q), f_q)result := addmod(mulmod(mload(0x8a0), mulmod(mload(0x8a0), 5948611796446669599396300148285100597158677068822442314729736978662760216172, f_q), f_q), result, f_q)mstore(0x4080, result) }mstore(0x40a0, mulmod(mload(0x3f00), mload(0x3da0), f_q)){ let prod := mload(0x3e80) prod := mulmod(mload(0x3ea0), prod, f_q) mstore(0x40c0, prod) prod := mulmod(mload(0x3ec0), prod, f_q) mstore(0x40e0, prod) prod := mulmod(mload(0x3ee0), prod, f_q) mstore(0x4100, prod) prod := mulmod(mload(0x3f80), prod, f_q) mstore(0x4120, prod) prod := mulmod(mload(0x3f00), prod, f_q) mstore(0x4140, prod) prod := mulmod(mload(0x3fa0), prod, f_q) mstore(0x4160, prod) prod := mulmod(mload(0x3fc0), prod, f_q) mstore(0x4180, prod) prod := mulmod(mload(0x3fe0), prod, f_q) mstore(0x41a0, prod) prod := mulmod(mload(0x4000), prod, f_q) mstore(0x41c0, prod) prod := mulmod(mload(0x4020), prod, f_q) mstore(0x41e0, prod) prod := mulmod(mload(0x4040), prod, f_q) mstore(0x4200, prod) prod := mulmod(mload(0x3f20), prod, f_q) mstore(0x4220, prod) prod := mulmod(mload(0x4060), prod, f_q) mstore(0x4240, prod) prod := mulmod(mload(0x4080), prod, f_q) mstore(0x4260, prod) prod := mulmod(mload(0x40a0), prod, f_q) mstore(0x4280, prod) }mstore(0x42c0, 32)mstore(0x42e0, 32)mstore(0x4300, 32)mstore(0x4320, mload(0x4280))mstore(0x4340, 21888242871839275222246405745257275088548364400416034343698204186575808495615)mstore(0x4360, 21888242871839275222246405745257275088548364400416034343698204186575808495617)success := and(eq(staticcall(gas(), 0x5, 0x42c0, 0xc0, 0x42a0, 0x20), 1), success){ let inv := mload(0x42a0) let v v := mload(0x40a0) mstore(0x40a0, mulmod(mload(0x4260), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x4080) mstore(0x4080, mulmod(mload(0x4240), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x4060) mstore(0x4060, mulmod(mload(0x4220), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x3f20) mstore(0x3f20, mulmod(mload(0x4200), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x4040) mstore(0x4040, mulmod(mload(0x41e0), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x4020) mstore(0x4020, mulmod(mload(0x41c0), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x4000) mstore(0x4000, mulmod(mload(0x41a0), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x3fe0) mstore(0x3fe0, mulmod(mload(0x4180), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x3fc0) mstore(0x3fc0, mulmod(mload(0x4160), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x3fa0) mstore(0x3fa0, mulmod(mload(0x4140), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x3f00) mstore(0x3f00, mulmod(mload(0x4120), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x3f80) mstore(0x3f80, mulmod(mload(0x4100), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x3ee0) mstore(0x3ee0, mulmod(mload(0x40e0), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x3ec0) mstore(0x3ec0, mulmod(mload(0x40c0), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x3ea0) mstore(0x3ea0, mulmod(mload(0x3e80), inv, f_q)) inv := mulmod(v, inv, f_q) mstore(0x3e80, inv) }{ let result := mload(0x3e80)result := addmod(mload(0x3ea0), result, f_q)result := addmod(mload(0x3ec0), result, f_q)result := addmod(mload(0x3ee0), result, f_q)mstore(0x4380, result) }mstore(0x43a0, mulmod(mload(0x3f60), mload(0x3f00), f_q)){ let result := mload(0x3f80)mstore(0x43c0, result) }mstore(0x43e0, mulmod(mload(0x3f60), mload(0x4000), f_q)){ let result := mload(0x3fa0)result := addmod(mload(0x3fc0), result, f_q)result := addmod(mload(0x3fe0), result, f_q)mstore(0x4400, result) }mstore(0x4420, mulmod(mload(0x3f60), mload(0x3f20), f_q)){ let result := mload(0x4020)result := addmod(mload(0x4040), result, f_q)mstore(0x4440, result) }mstore(0x4460, mulmod(mload(0x3f60), mload(0x40a0), f_q)){ let result := mload(0x4060)result := addmod(mload(0x4080), result, f_q)mstore(0x4480, result) }{ let prod := mload(0x4380) prod := mulmod(mload(0x43c0), prod, f_q) mstore(0x44a0, prod) prod := mulmod(mload(0x4400), prod, f_q) mstore(0x44c0, prod) prod := mulmod(mload(0x4440), prod, f_q) mstore(0x44e0, prod) prod := mulmod(mload(0x4480), prod, f_q) mstore(0x4500, prod) }mstore(0x4540, 32)mstore(0x4560, 32)mstore(0x4580, 32)mstore(0x45a0, mload(0x4500))mstore(0x45c0, 21888242871839275222246405745257275088548364400416034343698204186575808495615)mstore(0x45e0, 21888242871839275222246405745257275088548364400416034343698204186575808495617)success := and(eq(staticcall(gas(), 0x5, 0x4540, 0xc0, 0x4520, 0x20), 1), success){ let inv := mload(0x4520) let v v := mload(0x4480) mstore(0x4480, mulmod(mload(0x44e0), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x4440) mstore(0x4440, mulmod(mload(0x44c0), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x4400) mstore(0x4400, mulmod(mload(0x44a0), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(0x43c0) mstore(0x43c0, mulmod(mload(0x4380), inv, f_q)) inv := mulmod(v, inv, f_q) mstore(0x4380, inv) }mstore(0x4600, mulmod(mload(0x43a0), mload(0x43c0), f_q))mstore(0x4620, mulmod(mload(0x43e0), mload(0x4400), f_q))mstore(0x4640, mulmod(mload(0x4420), mload(0x4440), f_q))mstore(0x4660, mulmod(mload(0x4460), mload(0x4480), f_q))mstore(0x4680, mulmod(mload(0x1280), mload(0x1280), f_q))mstore(0x46a0, mulmod(mload(0x4680), mload(0x1280), f_q))mstore(0x46c0, mulmod(mload(0x46a0), mload(0x1280), f_q))mstore(0x46e0, mulmod(mload(0x46c0), mload(0x1280), f_q))mstore(0x4700, mulmod(mload(0x46e0), mload(0x1280), f_q))mstore(0x4720, mulmod(mload(0x4700), mload(0x1280), f_q))mstore(0x4740, mulmod(mload(0x4720), mload(0x1280), f_q))mstore(0x4760, mulmod(mload(0x4740), mload(0x1280), f_q))mstore(0x4780, mulmod(mload(0x4760), mload(0x1280), f_q))mstore(0x47a0, mulmod(mload(0x4780), mload(0x1280), f_q))mstore(0x47c0, mulmod(mload(0x47a0), mload(0x1280), f_q))mstore(0x47e0, mulmod(mload(0x47c0), mload(0x1280), f_q))mstore(0x4800, mulmod(mload(0x47e0), mload(0x1280), f_q))mstore(0x4820, mulmod(mload(0x4800), mload(0x1280), f_q))mstore(0x4840, mulmod(mload(0x4820), mload(0x1280), f_q))mstore(0x4860, mulmod(mload(0x4840), mload(0x1280), f_q))mstore(0x4880, mulmod(mload(0x4860), mload(0x1280), f_q))mstore(0x48a0, mulmod(mload(0x4880), mload(0x1280), f_q))mstore(0x48c0, mulmod(mload(0x48a0), mload(0x1280), f_q))mstore(0x48e0, mulmod(mload(0x48c0), mload(0x1280), f_q))mstore(0x4900, mulmod(mload(0x48e0), mload(0x1280), f_q))mstore(0x4920, mulmod(mload(0x4900), mload(0x1280), f_q))mstore(0x4940, mulmod(mload(0x4920), mload(0x1280), f_q))mstore(0x4960, mulmod(mload(0x4940), mload(0x1280), f_q))mstore(0x4980, mulmod(mload(0x4960), mload(0x1280), f_q))mstore(0x49a0, mulmod(mload(0x4980), mload(0x1280), f_q))mstore(0x49c0, mulmod(mload(0x49a0), mload(0x1280), f_q))mstore(0x49e0, mulmod(mload(0x12e0), mload(0x12e0), f_q))mstore(0x4a00, mulmod(mload(0x49e0), mload(0x12e0), f_q))mstore(0x4a20, mulmod(mload(0x4a00), mload(0x12e0), f_q))mstore(0x4a40, mulmod(mload(0x4a20), mload(0x12e0), f_q)){ let result := mulmod(mload(0x8e0), mload(0x3e80), f_q)result := addmod(mulmod(mload(0x900), mload(0x3ea0), f_q), result, f_q)result := addmod(mulmod(mload(0x920), mload(0x3ec0), f_q), result, f_q)result := addmod(mulmod(mload(0x940), mload(0x3ee0), f_q), result, f_q)mstore(0x4a60, result) }mstore(0x4a80, mulmod(mload(0x4a60), mload(0x4380), f_q))mstore(0x4aa0, mulmod(sub(f_q, mload(0x4a80)), 1, f_q)){ let result := mulmod(mload(0x960), mload(0x3e80), f_q)result := addmod(mulmod(mload(0x980), mload(0x3ea0), f_q), result, f_q)result := addmod(mulmod(mload(0x9a0), mload(0x3ec0), f_q), result, f_q)result := addmod(mulmod(mload(0x9c0), mload(0x3ee0), f_q), result, f_q)mstore(0x4ac0, result) }mstore(0x4ae0, mulmod(mload(0x4ac0), mload(0x4380), f_q))mstore(0x4b00, mulmod(sub(f_q, mload(0x4ae0)), mload(0x1280), f_q))mstore(0x4b20, mulmod(1, mload(0x1280), f_q))mstore(0x4b40, addmod(mload(0x4aa0), mload(0x4b00), f_q)){ let result := mulmod(mload(0x9e0), mload(0x3e80), f_q)result := addmod(mulmod(mload(0xa00), mload(0x3ea0), f_q), result, f_q)result := addmod(mulmod(mload(0xa20), mload(0x3ec0), f_q), result, f_q)result := addmod(mulmod(mload(0xa40), mload(0x3ee0), f_q), result, f_q)mstore(0x4b60, result) }mstore(0x4b80, mulmod(mload(0x4b60), mload(0x4380), f_q))mstore(0x4ba0, mulmod(sub(f_q, mload(0x4b80)), mload(0x4680), f_q))mstore(0x4bc0, mulmod(1, mload(0x4680), f_q))mstore(0x4be0, addmod(mload(0x4b40), mload(0x4ba0), f_q)){ let result := mulmod(mload(0xa60), mload(0x3e80), f_q)result := addmod(mulmod(mload(0xa80), mload(0x3ea0), f_q), result, f_q)result := addmod(mulmod(mload(0xaa0), mload(0x3ec0), f_q), result, f_q)result := addmod(mulmod(mload(0xac0), mload(0x3ee0), f_q), result, f_q)mstore(0x4c00, result) }mstore(0x4c20, mulmod(mload(0x4c00), mload(0x4380), f_q))mstore(0x4c40, mulmod(sub(f_q, mload(0x4c20)), mload(0x46a0), f_q))mstore(0x4c60, mulmod(1, mload(0x46a0), f_q))mstore(0x4c80, addmod(mload(0x4be0), mload(0x4c40), f_q)){ let result := mulmod(mload(0xae0), mload(0x3e80), f_q)result := addmod(mulmod(mload(0xb00), mload(0x3ea0), f_q), result, f_q)result := addmod(mulmod(mload(0xb20), mload(0x3ec0), f_q), result, f_q)result := addmod(mulmod(mload(0xb40), mload(0x3ee0), f_q), result, f_q)mstore(0x4ca0, result) }mstore(0x4cc0, mulmod(mload(0x4ca0), mload(0x4380), f_q))mstore(0x4ce0, mulmod(sub(f_q, mload(0x4cc0)), mload(0x46c0), f_q))mstore(0x4d00, mulmod(1, mload(0x46c0), f_q))mstore(0x4d20, addmod(mload(0x4c80), mload(0x4ce0), f_q)){ let result := mulmod(mload(0xb60), mload(0x3e80), f_q)result := addmod(mulmod(mload(0xb80), mload(0x3ea0), f_q), result, f_q)result := addmod(mulmod(mload(0xba0), mload(0x3ec0), f_q), result, f_q)result := addmod(mulmod(mload(0xbc0), mload(0x3ee0), f_q), result, f_q)mstore(0x4d40, result) }mstore(0x4d60, mulmod(mload(0x4d40), mload(0x4380), f_q))mstore(0x4d80, mulmod(sub(f_q, mload(0x4d60)), mload(0x46e0), f_q))mstore(0x4da0, mulmod(1, mload(0x46e0), f_q))mstore(0x4dc0, addmod(mload(0x4d20), mload(0x4d80), f_q))mstore(0x4de0, mulmod(mload(0x4dc0), 1, f_q))mstore(0x4e00, mulmod(mload(0x4b20), 1, f_q))mstore(0x4e20, mulmod(mload(0x4bc0), 1, f_q))mstore(0x4e40, mulmod(mload(0x4c60), 1, f_q))mstore(0x4e60, mulmod(mload(0x4d00), 1, f_q))mstore(0x4e80, mulmod(mload(0x4da0), 1, f_q))mstore(0x4ea0, mulmod(1, mload(0x43a0), f_q)){ let result := mulmod(mload(0xbe0), mload(0x3f80), f_q)mstore(0x4ec0, result) }mstore(0x4ee0, mulmod(mload(0x4ec0), mload(0x4600), f_q))mstore(0x4f00, mulmod(sub(f_q, mload(0x4ee0)), 1, f_q))mstore(0x4f20, mulmod(mload(0x4ea0), 1, f_q)){ let result := mulmod(mload(0xc00), mload(0x3f80), f_q)mstore(0x4f40, result) }mstore(0x4f60, mulmod(mload(0x4f40), mload(0x4600), f_q))mstore(0x4f80, mulmod(sub(f_q, mload(0x4f60)), mload(0x1280), f_q))mstore(0x4fa0, mulmod(mload(0x4ea0), mload(0x1280), f_q))mstore(0x4fc0, addmod(mload(0x4f00), mload(0x4f80), f_q)){ let result := mulmod(mload(0xc20), mload(0x3f80), f_q)mstore(0x4fe0, result) }mstore(0x5000, mulmod(mload(0x4fe0), mload(0x4600), f_q))mstore(0x5020, mulmod(sub(f_q, mload(0x5000)), mload(0x4680), f_q))mstore(0x5040, mulmod(mload(0x4ea0), mload(0x4680), f_q))mstore(0x5060, addmod(mload(0x4fc0), mload(0x5020), f_q)){ let result := mulmod(mload(0x11a0), mload(0x3f80), f_q)mstore(0x5080, result) }mstore(0x50a0, mulmod(mload(0x5080), mload(0x4600), f_q))mstore(0x50c0, mulmod(sub(f_q, mload(0x50a0)), mload(0x46a0), f_q))mstore(0x50e0, mulmod(mload(0x4ea0), mload(0x46a0), f_q))mstore(0x5100, addmod(mload(0x5060), mload(0x50c0), f_q)){ let result := mulmod(mload(0x1240), mload(0x3f80), f_q)mstore(0x5120, result) }mstore(0x5140, mulmod(mload(0x5120), mload(0x4600), f_q))mstore(0x5160, mulmod(sub(f_q, mload(0x5140)), mload(0x46c0), f_q))mstore(0x5180, mulmod(mload(0x4ea0), mload(0x46c0), f_q))mstore(0x51a0, addmod(mload(0x5100), mload(0x5160), f_q)){ let result := mulmod(mload(0xc40), mload(0x3f80), f_q)mstore(0x51c0, result) }mstore(0x51e0, mulmod(mload(0x51c0), mload(0x4600), f_q))mstore(0x5200, mulmod(sub(f_q, mload(0x51e0)), mload(0x46e0), f_q))mstore(0x5220, mulmod(mload(0x4ea0), mload(0x46e0), f_q))mstore(0x5240, addmod(mload(0x51a0), mload(0x5200), f_q)){ let result := mulmod(mload(0xc60), mload(0x3f80), f_q)mstore(0x5260, result) }mstore(0x5280, mulmod(mload(0x5260), mload(0x4600), f_q))mstore(0x52a0, mulmod(sub(f_q, mload(0x5280)), mload(0x4700), f_q))mstore(0x52c0, mulmod(mload(0x4ea0), mload(0x4700), f_q))mstore(0x52e0, addmod(mload(0x5240), mload(0x52a0), f_q)){ let result := mulmod(mload(0xc80), mload(0x3f80), f_q)mstore(0x5300, result) }mstore(0x5320, mulmod(mload(0x5300), mload(0x4600), f_q))mstore(0x5340, mulmod(sub(f_q, mload(0x5320)), mload(0x4720), f_q))mstore(0x5360, mulmod(mload(0x4ea0), mload(0x4720), f_q))mstore(0x5380, addmod(mload(0x52e0), mload(0x5340), f_q)){ let result := mulmod(mload(0xca0), mload(0x3f80), f_q)mstore(0x53a0, result) }mstore(0x53c0, mulmod(mload(0x53a0), mload(0x4600), f_q))mstore(0x53e0, mulmod(sub(f_q, mload(0x53c0)), mload(0x4740), f_q))mstore(0x5400, mulmod(mload(0x4ea0), mload(0x4740), f_q))mstore(0x5420, addmod(mload(0x5380), mload(0x53e0), f_q)){ let result := mulmod(mload(0xcc0), mload(0x3f80), f_q)mstore(0x5440, result) }mstore(0x5460, mulmod(mload(0x5440), mload(0x4600), f_q))mstore(0x5480, mulmod(sub(f_q, mload(0x5460)), mload(0x4760), f_q))mstore(0x54a0, mulmod(mload(0x4ea0), mload(0x4760), f_q))mstore(0x54c0, addmod(mload(0x5420), mload(0x5480), f_q)){ let result := mulmod(mload(0xce0), mload(0x3f80), f_q)mstore(0x54e0, result) }mstore(0x5500, mulmod(mload(0x54e0), mload(0x4600), f_q))mstore(0x5520, mulmod(sub(f_q, mload(0x5500)), mload(0x4780), f_q))mstore(0x5540, mulmod(mload(0x4ea0), mload(0x4780), f_q))mstore(0x5560, addmod(mload(0x54c0), mload(0x5520), f_q)){ let result := mulmod(mload(0xd00), mload(0x3f80), f_q)mstore(0x5580, result) }mstore(0x55a0, mulmod(mload(0x5580), mload(0x4600), f_q))mstore(0x55c0, mulmod(sub(f_q, mload(0x55a0)), mload(0x47a0), f_q))mstore(0x55e0, mulmod(mload(0x4ea0), mload(0x47a0), f_q))mstore(0x5600, addmod(mload(0x5560), mload(0x55c0), f_q)){ let result := mulmod(mload(0xd20), mload(0x3f80), f_q)mstore(0x5620, result) }mstore(0x5640, mulmod(mload(0x5620), mload(0x4600), f_q))mstore(0x5660, mulmod(sub(f_q, mload(0x5640)), mload(0x47c0), f_q))mstore(0x5680, mulmod(mload(0x4ea0), mload(0x47c0), f_q))mstore(0x56a0, addmod(mload(0x5600), mload(0x5660), f_q)){ let result := mulmod(mload(0xd40), mload(0x3f80), f_q)mstore(0x56c0, result) }mstore(0x56e0, mulmod(mload(0x56c0), mload(0x4600), f_q))mstore(0x5700, mulmod(sub(f_q, mload(0x56e0)), mload(0x47e0), f_q))mstore(0x5720, mulmod(mload(0x4ea0), mload(0x47e0), f_q))mstore(0x5740, addmod(mload(0x56a0), mload(0x5700), f_q)){ let result := mulmod(mload(0xd60), mload(0x3f80), f_q)mstore(0x5760, result) }mstore(0x5780, mulmod(mload(0x5760), mload(0x4600), f_q))mstore(0x57a0, mulmod(sub(f_q, mload(0x5780)), mload(0x4800), f_q))mstore(0x57c0, mulmod(mload(0x4ea0), mload(0x4800), f_q))mstore(0x57e0, addmod(mload(0x5740), mload(0x57a0), f_q)){ let result := mulmod(mload(0xda0), mload(0x3f80), f_q)mstore(0x5800, result) }mstore(0x5820, mulmod(mload(0x5800), mload(0x4600), f_q))mstore(0x5840, mulmod(sub(f_q, mload(0x5820)), mload(0x4820), f_q))mstore(0x5860, mulmod(mload(0x4ea0), mload(0x4820), f_q))mstore(0x5880, addmod(mload(0x57e0), mload(0x5840), f_q)){ let result := mulmod(mload(0xdc0), mload(0x3f80), f_q)mstore(0x58a0, result) }mstore(0x58c0, mulmod(mload(0x58a0), mload(0x4600), f_q))mstore(0x58e0, mulmod(sub(f_q, mload(0x58c0)), mload(0x4840), f_q))mstore(0x5900, mulmod(mload(0x4ea0), mload(0x4840), f_q))mstore(0x5920, addmod(mload(0x5880), mload(0x58e0), f_q)){ let result := mulmod(mload(0xde0), mload(0x3f80), f_q)mstore(0x5940, result) }mstore(0x5960, mulmod(mload(0x5940), mload(0x4600), f_q))mstore(0x5980, mulmod(sub(f_q, mload(0x5960)), mload(0x4860), f_q))mstore(0x59a0, mulmod(mload(0x4ea0), mload(0x4860), f_q))mstore(0x59c0, addmod(mload(0x5920), mload(0x5980), f_q)){ let result := mulmod(mload(0xe00), mload(0x3f80), f_q)mstore(0x59e0, result) }mstore(0x5a00, mulmod(mload(0x59e0), mload(0x4600), f_q))mstore(0x5a20, mulmod(sub(f_q, mload(0x5a00)), mload(0x4880), f_q))mstore(0x5a40, mulmod(mload(0x4ea0), mload(0x4880), f_q))mstore(0x5a60, addmod(mload(0x59c0), mload(0x5a20), f_q)){ let result := mulmod(mload(0xe20), mload(0x3f80), f_q)mstore(0x5a80, result) }mstore(0x5aa0, mulmod(mload(0x5a80), mload(0x4600), f_q))mstore(0x5ac0, mulmod(sub(f_q, mload(0x5aa0)), mload(0x48a0), f_q))mstore(0x5ae0, mulmod(mload(0x4ea0), mload(0x48a0), f_q))mstore(0x5b00, addmod(mload(0x5a60), mload(0x5ac0), f_q)){ let result := mulmod(mload(0xe40), mload(0x3f80), f_q)mstore(0x5b20, result) }mstore(0x5b40, mulmod(mload(0x5b20), mload(0x4600), f_q))mstore(0x5b60, mulmod(sub(f_q, mload(0x5b40)), mload(0x48c0), f_q))mstore(0x5b80, mulmod(mload(0x4ea0), mload(0x48c0), f_q))mstore(0x5ba0, addmod(mload(0x5b00), mload(0x5b60), f_q)){ let result := mulmod(mload(0xe60), mload(0x3f80), f_q)mstore(0x5bc0, result) }mstore(0x5be0, mulmod(mload(0x5bc0), mload(0x4600), f_q))mstore(0x5c00, mulmod(sub(f_q, mload(0x5be0)), mload(0x48e0), f_q))mstore(0x5c20, mulmod(mload(0x4ea0), mload(0x48e0), f_q))mstore(0x5c40, addmod(mload(0x5ba0), mload(0x5c00), f_q)){ let result := mulmod(mload(0xe80), mload(0x3f80), f_q)mstore(0x5c60, result) }mstore(0x5c80, mulmod(mload(0x5c60), mload(0x4600), f_q))mstore(0x5ca0, mulmod(sub(f_q, mload(0x5c80)), mload(0x4900), f_q))mstore(0x5cc0, mulmod(mload(0x4ea0), mload(0x4900), f_q))mstore(0x5ce0, addmod(mload(0x5c40), mload(0x5ca0), f_q)){ let result := mulmod(mload(0xea0), mload(0x3f80), f_q)mstore(0x5d00, result) }mstore(0x5d20, mulmod(mload(0x5d00), mload(0x4600), f_q))mstore(0x5d40, mulmod(sub(f_q, mload(0x5d20)), mload(0x4920), f_q))mstore(0x5d60, mulmod(mload(0x4ea0), mload(0x4920), f_q))mstore(0x5d80, addmod(mload(0x5ce0), mload(0x5d40), f_q)){ let result := mulmod(mload(0xec0), mload(0x3f80), f_q)mstore(0x5da0, result) }mstore(0x5dc0, mulmod(mload(0x5da0), mload(0x4600), f_q))mstore(0x5de0, mulmod(sub(f_q, mload(0x5dc0)), mload(0x4940), f_q))mstore(0x5e00, mulmod(mload(0x4ea0), mload(0x4940), f_q))mstore(0x5e20, addmod(mload(0x5d80), mload(0x5de0), f_q)){ let result := mulmod(mload(0xee0), mload(0x3f80), f_q)mstore(0x5e40, result) }mstore(0x5e60, mulmod(mload(0x5e40), mload(0x4600), f_q))mstore(0x5e80, mulmod(sub(f_q, mload(0x5e60)), mload(0x4960), f_q))mstore(0x5ea0, mulmod(mload(0x4ea0), mload(0x4960), f_q))mstore(0x5ec0, addmod(mload(0x5e20), mload(0x5e80), f_q))mstore(0x5ee0, mulmod(mload(0x3c60), mload(0x43a0), f_q))mstore(0x5f00, mulmod(mload(0x3c80), mload(0x43a0), f_q)){ let result := mulmod(mload(0x3ca0), mload(0x3f80), f_q)mstore(0x5f20, result) }mstore(0x5f40, mulmod(mload(0x5f20), mload(0x4600), f_q))mstore(0x5f60, mulmod(sub(f_q, mload(0x5f40)), mload(0x4980), f_q))mstore(0x5f80, mulmod(mload(0x4ea0), mload(0x4980), f_q))mstore(0x5fa0, mulmod(mload(0x5ee0), mload(0x4980), f_q))mstore(0x5fc0, mulmod(mload(0x5f00), mload(0x4980), f_q))mstore(0x5fe0, addmod(mload(0x5ec0), mload(0x5f60), f_q)){ let result := mulmod(mload(0xd80), mload(0x3f80), f_q)mstore(0x6000, result) }mstore(0x6020, mulmod(mload(0x6000), mload(0x4600), f_q))mstore(0x6040, mulmod(sub(f_q, mload(0x6020)), mload(0x49a0), f_q))mstore(0x6060, mulmod(mload(0x4ea0), mload(0x49a0), f_q))mstore(0x6080, addmod(mload(0x5fe0), mload(0x6040), f_q))mstore(0x60a0, mulmod(mload(0x6080), mload(0x12e0), f_q))mstore(0x60c0, mulmod(mload(0x4f20), mload(0x12e0), f_q))mstore(0x60e0, mulmod(mload(0x4fa0), mload(0x12e0), f_q))mstore(0x6100, mulmod(mload(0x5040), mload(0x12e0), f_q))mstore(0x6120, mulmod(mload(0x50e0), mload(0x12e0), f_q))mstore(0x6140, mulmod(mload(0x5180), mload(0x12e0), f_q))mstore(0x6160, mulmod(mload(0x5220), mload(0x12e0), f_q))mstore(0x6180, mulmod(mload(0x52c0), mload(0x12e0), f_q))mstore(0x61a0, mulmod(mload(0x5360), mload(0x12e0), f_q))mstore(0x61c0, mulmod(mload(0x5400), mload(0x12e0), f_q))mstore(0x61e0, mulmod(mload(0x54a0), mload(0x12e0), f_q))mstore(0x6200, mulmod(mload(0x5540), mload(0x12e0), f_q))mstore(0x6220, mulmod(mload(0x55e0), mload(0x12e0), f_q))mstore(0x6240, mulmod(mload(0x5680), mload(0x12e0), f_q))mstore(0x6260, mulmod(mload(0x5720), mload(0x12e0), f_q))mstore(0x6280, mulmod(mload(0x57c0), mload(0x12e0), f_q))mstore(0x62a0, mulmod(mload(0x5860), mload(0x12e0), f_q))mstore(0x62c0, mulmod(mload(0x5900), mload(0x12e0), f_q))mstore(0x62e0, mulmod(mload(0x59a0), mload(0x12e0), f_q))mstore(0x6300, mulmod(mload(0x5a40), mload(0x12e0), f_q))mstore(0x6320, mulmod(mload(0x5ae0), mload(0x12e0), f_q))mstore(0x6340, mulmod(mload(0x5b80), mload(0x12e0), f_q))mstore(0x6360, mulmod(mload(0x5c20), mload(0x12e0), f_q))mstore(0x6380, mulmod(mload(0x5cc0), mload(0x12e0), f_q))mstore(0x63a0, mulmod(mload(0x5d60), mload(0x12e0), f_q))mstore(0x63c0, mulmod(mload(0x5e00), mload(0x12e0), f_q))mstore(0x63e0, mulmod(mload(0x5ea0), mload(0x12e0), f_q))mstore(0x6400, mulmod(mload(0x5f80), mload(0x12e0), f_q))mstore(0x6420, mulmod(mload(0x5fa0), mload(0x12e0), f_q))mstore(0x6440, mulmod(mload(0x5fc0), mload(0x12e0), f_q))mstore(0x6460, mulmod(mload(0x6060), mload(0x12e0), f_q))mstore(0x6480, addmod(mload(0x4de0), mload(0x60a0), f_q))mstore(0x64a0, mulmod(1, mload(0x43e0), f_q)){ let result := mulmod(mload(0xf00), mload(0x3fa0), f_q)result := addmod(mulmod(mload(0xf20), mload(0x3fc0), f_q), result, f_q)result := addmod(mulmod(mload(0xf40), mload(0x3fe0), f_q), result, f_q)mstore(0x64c0, result) }mstore(0x64e0, mulmod(mload(0x64c0), mload(0x4620), f_q))mstore(0x6500, mulmod(sub(f_q, mload(0x64e0)), 1, f_q))mstore(0x6520, mulmod(mload(0x64a0), 1, f_q)){ let result := mulmod(mload(0xf60), mload(0x3fa0), f_q)result := addmod(mulmod(mload(0xf80), mload(0x3fc0), f_q), result, f_q)result := addmod(mulmod(mload(0xfa0), mload(0x3fe0), f_q), result, f_q)mstore(0x6540, result) }mstore(0x6560, mulmod(mload(0x6540), mload(0x4620), f_q))mstore(0x6580, mulmod(sub(f_q, mload(0x6560)), mload(0x1280), f_q))mstore(0x65a0, mulmod(mload(0x64a0), mload(0x1280), f_q))mstore(0x65c0, addmod(mload(0x6500), mload(0x6580), f_q)){ let result := mulmod(mload(0xfc0), mload(0x3fa0), f_q)result := addmod(mulmod(mload(0xfe0), mload(0x3fc0), f_q), result, f_q)result := addmod(mulmod(mload(0x1000), mload(0x3fe0), f_q), result, f_q)mstore(0x65e0, result) }mstore(0x6600, mulmod(mload(0x65e0), mload(0x4620), f_q))mstore(0x6620, mulmod(sub(f_q, mload(0x6600)), mload(0x4680), f_q))mstore(0x6640, mulmod(mload(0x64a0), mload(0x4680), f_q))mstore(0x6660, addmod(mload(0x65c0), mload(0x6620), f_q)){ let result := mulmod(mload(0x1020), mload(0x3fa0), f_q)result := addmod(mulmod(mload(0x1040), mload(0x3fc0), f_q), result, f_q)result := addmod(mulmod(mload(0x1060), mload(0x3fe0), f_q), result, f_q)mstore(0x6680, result) }mstore(0x66a0, mulmod(mload(0x6680), mload(0x4620), f_q))mstore(0x66c0, mulmod(sub(f_q, mload(0x66a0)), mload(0x46a0), f_q))mstore(0x66e0, mulmod(mload(0x64a0), mload(0x46a0), f_q))mstore(0x6700, addmod(mload(0x6660), mload(0x66c0), f_q)){ let result := mulmod(mload(0x1080), mload(0x3fa0), f_q)result := addmod(mulmod(mload(0x10a0), mload(0x3fc0), f_q), result, f_q)result := addmod(mulmod(mload(0x10c0), mload(0x3fe0), f_q), result, f_q)mstore(0x6720, result) }mstore(0x6740, mulmod(mload(0x6720), mload(0x4620), f_q))mstore(0x6760, mulmod(sub(f_q, mload(0x6740)), mload(0x46c0), f_q))mstore(0x6780, mulmod(mload(0x64a0), mload(0x46c0), f_q))mstore(0x67a0, addmod(mload(0x6700), mload(0x6760), f_q))mstore(0x67c0, mulmod(mload(0x67a0), mload(0x49e0), f_q))mstore(0x67e0, mulmod(mload(0x6520), mload(0x49e0), f_q))mstore(0x6800, mulmod(mload(0x65a0), mload(0x49e0), f_q))mstore(0x6820, mulmod(mload(0x6640), mload(0x49e0), f_q))mstore(0x6840, mulmod(mload(0x66e0), mload(0x49e0), f_q))mstore(0x6860, mulmod(mload(0x6780), mload(0x49e0), f_q))mstore(0x6880, addmod(mload(0x6480), mload(0x67c0), f_q))mstore(0x68a0, mulmod(1, mload(0x4420), f_q)){ let result := mulmod(mload(0x10e0), mload(0x4020), f_q)result := addmod(mulmod(mload(0x1100), mload(0x4040), f_q), result, f_q)mstore(0x68c0, result) }mstore(0x68e0, mulmod(mload(0x68c0), mload(0x4640), f_q))mstore(0x6900, mulmod(sub(f_q, mload(0x68e0)), 1, f_q))mstore(0x6920, mulmod(mload(0x68a0), 1, f_q)){ let result := mulmod(mload(0x1120), mload(0x4020), f_q)result := addmod(mulmod(mload(0x1140), mload(0x4040), f_q), result, f_q)mstore(0x6940, result) }mstore(0x6960, mulmod(mload(0x6940), mload(0x4640), f_q))mstore(0x6980, mulmod(sub(f_q, mload(0x6960)), mload(0x1280), f_q))mstore(0x69a0, mulmod(mload(0x68a0), mload(0x1280), f_q))mstore(0x69c0, addmod(mload(0x6900), mload(0x6980), f_q)){ let result := mulmod(mload(0x11c0), mload(0x4020), f_q)result := addmod(mulmod(mload(0x11e0), mload(0x4040), f_q), result, f_q)mstore(0x69e0, result) }mstore(0x6a00, mulmod(mload(0x69e0), mload(0x4640), f_q))mstore(0x6a20, mulmod(sub(f_q, mload(0x6a00)), mload(0x4680), f_q))mstore(0x6a40, mulmod(mload(0x68a0), mload(0x4680), f_q))mstore(0x6a60, addmod(mload(0x69c0), mload(0x6a20), f_q))mstore(0x6a80, mulmod(mload(0x6a60), mload(0x4a00), f_q))mstore(0x6aa0, mulmod(mload(0x6920), mload(0x4a00), f_q))mstore(0x6ac0, mulmod(mload(0x69a0), mload(0x4a00), f_q))mstore(0x6ae0, mulmod(mload(0x6a40), mload(0x4a00), f_q))mstore(0x6b00, addmod(mload(0x6880), mload(0x6a80), f_q))mstore(0x6b20, mulmod(1, mload(0x4460), f_q)){ let result := mulmod(mload(0x1160), mload(0x4060), f_q)result := addmod(mulmod(mload(0x1180), mload(0x4080), f_q), result, f_q)mstore(0x6b40, result) }mstore(0x6b60, mulmod(mload(0x6b40), mload(0x4660), f_q))mstore(0x6b80, mulmod(sub(f_q, mload(0x6b60)), 1, f_q))mstore(0x6ba0, mulmod(mload(0x6b20), 1, f_q)){ let result := mulmod(mload(0x1200), mload(0x4060), f_q)result := addmod(mulmod(mload(0x1220), mload(0x4080), f_q), result, f_q)mstore(0x6bc0, result) }mstore(0x6be0, mulmod(mload(0x6bc0), mload(0x4660), f_q))mstore(0x6c00, mulmod(sub(f_q, mload(0x6be0)), mload(0x1280), f_q))mstore(0x6c20, mulmod(mload(0x6b20), mload(0x1280), f_q))mstore(0x6c40, addmod(mload(0x6b80), mload(0x6c00), f_q))mstore(0x6c60, mulmod(mload(0x6c40), mload(0x4a20), f_q))mstore(0x6c80, mulmod(mload(0x6ba0), mload(0x4a20), f_q))mstore(0x6ca0, mulmod(mload(0x6c20), mload(0x4a20), f_q))mstore(0x6cc0, addmod(mload(0x6b00), mload(0x6c60), f_q))mstore(0x6ce0, mulmod(1, mload(0x3f60), f_q))mstore(0x6d00, mulmod(1, mload(0x1380), f_q))mstore(0x6d20, 0x0000000000000000000000000000000000000000000000000000000000000001) mstore(0x6d40, 0x0000000000000000000000000000000000000000000000000000000000000002)mstore(0x6d60, mload(0x6cc0))success := and(eq(staticcall(gas(), 0x7, 0x6d20, 0x60, 0x6d20, 0x40), 1), success)mstore(0x6d80, mload(0x6d20)) mstore(0x6da0, mload(0x6d40))mstore(0x6dc0, mload(0xc0)) mstore(0x6de0, mload(0xe0))success := and(eq(staticcall(gas(), 0x6, 0x6d80, 0x80, 0x6d80, 0x40), 1), success)mstore(0x6e00, mload(0x100)) mstore(0x6e20, mload(0x120))mstore(0x6e40, mload(0x4e00))success := and(eq(staticcall(gas(), 0x7, 0x6e00, 0x60, 0x6e00, 0x40), 1), success)mstore(0x6e60, mload(0x6d80)) mstore(0x6e80, mload(0x6da0))mstore(0x6ea0, mload(0x6e00)) mstore(0x6ec0, mload(0x6e20))success := and(eq(staticcall(gas(), 0x6, 0x6e60, 0x80, 0x6e60, 0x40), 1), success)mstore(0x6ee0, mload(0x140)) mstore(0x6f00, mload(0x160))mstore(0x6f20, mload(0x4e20))success := and(eq(staticcall(gas(), 0x7, 0x6ee0, 0x60, 0x6ee0, 0x40), 1), success)mstore(0x6f40, mload(0x6e60)) mstore(0x6f60, mload(0x6e80))mstore(0x6f80, mload(0x6ee0)) mstore(0x6fa0, mload(0x6f00))success := and(eq(staticcall(gas(), 0x6, 0x6f40, 0x80, 0x6f40, 0x40), 1), success)mstore(0x6fc0, mload(0x180)) mstore(0x6fe0, mload(0x1a0))mstore(0x7000, mload(0x4e40))success := and(eq(staticcall(gas(), 0x7, 0x6fc0, 0x60, 0x6fc0, 0x40), 1), success)mstore(0x7020, mload(0x6f40)) mstore(0x7040, mload(0x6f60))mstore(0x7060, mload(0x6fc0)) mstore(0x7080, mload(0x6fe0))success := and(eq(staticcall(gas(), 0x6, 0x7020, 0x80, 0x7020, 0x40), 1), success)mstore(0x70a0, mload(0x1c0)) mstore(0x70c0, mload(0x1e0))mstore(0x70e0, mload(0x4e60))success := and(eq(staticcall(gas(), 0x7, 0x70a0, 0x60, 0x70a0, 0x40), 1), success)mstore(0x7100, mload(0x7020)) mstore(0x7120, mload(0x7040))mstore(0x7140, mload(0x70a0)) mstore(0x7160, mload(0x70c0))success := and(eq(staticcall(gas(), 0x6, 0x7100, 0x80, 0x7100, 0x40), 1), success)mstore(0x7180, mload(0x200)) mstore(0x71a0, mload(0x220))mstore(0x71c0, mload(0x4e80))success := and(eq(staticcall(gas(), 0x7, 0x7180, 0x60, 0x7180, 0x40), 1), success)mstore(0x71e0, mload(0x7100)) mstore(0x7200, mload(0x7120))mstore(0x7220, mload(0x7180)) mstore(0x7240, mload(0x71a0))success := and(eq(staticcall(gas(), 0x6, 0x71e0, 0x80, 0x71e0, 0x40), 1), success)mstore(0x7260, mload(0x240)) mstore(0x7280, mload(0x260))mstore(0x72a0, mload(0x60c0))success := and(eq(staticcall(gas(), 0x7, 0x7260, 0x60, 0x7260, 0x40), 1), success)mstore(0x72c0, mload(0x71e0)) mstore(0x72e0, mload(0x7200))mstore(0x7300, mload(0x7260)) mstore(0x7320, mload(0x7280))success := and(eq(staticcall(gas(), 0x6, 0x72c0, 0x80, 0x72c0, 0x40), 1), success)mstore(0x7340, mload(0x280)) mstore(0x7360, mload(0x2a0))mstore(0x7380, mload(0x60e0))success := and(eq(staticcall(gas(), 0x7, 0x7340, 0x60, 0x7340, 0x40), 1), success)mstore(0x73a0, mload(0x72c0)) mstore(0x73c0, mload(0x72e0))mstore(0x73e0, mload(0x7340)) mstore(0x7400, mload(0x7360))success := and(eq(staticcall(gas(), 0x6, 0x73a0, 0x80, 0x73a0, 0x40), 1), success)mstore(0x7420, mload(0x2c0)) mstore(0x7440, mload(0x2e0))mstore(0x7460, mload(0x6100))success := and(eq(staticcall(gas(), 0x7, 0x7420, 0x60, 0x7420, 0x40), 1), success)mstore(0x7480, mload(0x73a0)) mstore(0x74a0, mload(0x73c0))mstore(0x74c0, mload(0x7420)) mstore(0x74e0, mload(0x7440))success := and(eq(staticcall(gas(), 0x6, 0x7480, 0x80, 0x7480, 0x40), 1), success)mstore(0x7500, mload(0x3a0)) mstore(0x7520, mload(0x3c0))mstore(0x7540, mload(0x6120))success := and(eq(staticcall(gas(), 0x7, 0x7500, 0x60, 0x7500, 0x40), 1), success)mstore(0x7560, mload(0x7480)) mstore(0x7580, mload(0x74a0))mstore(0x75a0, mload(0x7500)) mstore(0x75c0, mload(0x7520))success := and(eq(staticcall(gas(), 0x6, 0x7560, 0x80, 0x7560, 0x40), 1), success)mstore(0x75e0, mload(0x420)) mstore(0x7600, mload(0x440))mstore(0x7620, mload(0x6140))success := and(eq(staticcall(gas(), 0x7, 0x75e0, 0x60, 0x75e0, 0x40), 1), success)mstore(0x7640, mload(0x7560)) mstore(0x7660, mload(0x7580))mstore(0x7680, mload(0x75e0)) mstore(0x76a0, mload(0x7600))success := and(eq(staticcall(gas(), 0x6, 0x7640, 0x80, 0x7640, 0x40), 1), success)mstore(0x76c0, 0x1efccb5f35c0ab008fa604178482fb235779612117d933e230bf21aed1936775) mstore(0x76e0, 0x1dab7bfc8c09774da8326adfa867ee15820c0e4a66068d086b2dee960993fc8a)mstore(0x7700, mload(0x6160))success := and(eq(staticcall(gas(), 0x7, 0x76c0, 0x60, 0x76c0, 0x40), 1), success)mstore(0x7720, mload(0x7640)) mstore(0x7740, mload(0x7660))mstore(0x7760, mload(0x76c0)) mstore(0x7780, mload(0x76e0))success := and(eq(staticcall(gas(), 0x6, 0x7720, 0x80, 0x7720, 0x40), 1), success)mstore(0x77a0, 0x04528ec7365a2881b7d3c8925570e06bb3b17f04f6a95384ac8ed19a30c12097) mstore(0x77c0, 0x28d1ef470a8a5278ad6d2eb9047ad7e93024113f543b06870f1bbea7177db404)mstore(0x77e0, mload(0x6180))success := and(eq(staticcall(gas(), 0x7, 0x77a0, 0x60, 0x77a0, 0x40), 1), success)mstore(0x7800, mload(0x7720)) mstore(0x7820, mload(0x7740))mstore(0x7840, mload(0x77a0)) mstore(0x7860, mload(0x77c0))success := and(eq(staticcall(gas(), 0x6, 0x7800, 0x80, 0x7800, 0x40), 1), success)mstore(0x7880, 0x04d043081f0d55eead6d8ad7b10d09a6ee2718f445d9bce454075a8a37bacaf3) mstore(0x78a0, 0x27d6bcbb02cd624ab80b5532a0a65fc6f88a0faf7cf3e0d106f4aa0aa25e758b)mstore(0x78c0, mload(0x61a0))success := and(eq(staticcall(gas(), 0x7, 0x7880, 0x60, 0x7880, 0x40), 1), success)mstore(0x78e0, mload(0x7800)) mstore(0x7900, mload(0x7820))mstore(0x7920, mload(0x7880)) mstore(0x7940, mload(0x78a0))success := and(eq(staticcall(gas(), 0x6, 0x78e0, 0x80, 0x78e0, 0x40), 1), success)mstore(0x7960, 0x2d07a1bca289cdb98b648a91cbb0809dfa3a06fe01047b291d1161ddf8d1732c) mstore(0x7980, 0x021d078d5869c57b3fe2413b517561205de5f297ac56c0e5ef0f1a7f4a31ee94)mstore(0x79a0, mload(0x61c0))success := and(eq(staticcall(gas(), 0x7, 0x7960, 0x60, 0x7960, 0x40), 1), success)mstore(0x79c0, mload(0x78e0)) mstore(0x79e0, mload(0x7900))mstore(0x7a00, mload(0x7960)) mstore(0x7a20, mload(0x7980))success := and(eq(staticcall(gas(), 0x6, 0x79c0, 0x80, 0x79c0, 0x40), 1), success)mstore(0x7a40, 0x0654df583cf4a624c7d28ab22c3d83ac654441d1773fa149a5d8dd93904aefa1) mstore(0x7a60, 0x03505462356097e7becf98acd86de36a336a2e9c149fe3ff073199ff414da34e)mstore(0x7a80, mload(0x61e0))success := and(eq(staticcall(gas(), 0x7, 0x7a40, 0x60, 0x7a40, 0x40), 1), success)mstore(0x7aa0, mload(0x79c0)) mstore(0x7ac0, mload(0x79e0))mstore(0x7ae0, mload(0x7a40)) mstore(0x7b00, mload(0x7a60))success := and(eq(staticcall(gas(), 0x6, 0x7aa0, 0x80, 0x7aa0, 0x40), 1), success)mstore(0x7b20, 0x013693bda5e1bad435fc7cc97749086b700dd0a10463a371be028ef9837494df) mstore(0x7b40, 0x219efc67984ad64348266fefcffc138d846b0c0d8f81a24ea3774609d3f5d724)mstore(0x7b60, mload(0x6200))success := and(eq(staticcall(gas(), 0x7, 0x7b20, 0x60, 0x7b20, 0x40), 1), success)mstore(0x7b80, mload(0x7aa0)) mstore(0x7ba0, mload(0x7ac0))mstore(0x7bc0, mload(0x7b20)) mstore(0x7be0, mload(0x7b40))success := and(eq(staticcall(gas(), 0x6, 0x7b80, 0x80, 0x7b80, 0x40), 1), success)mstore(0x7c00, 0x1a29044990ec839bb07d56ffa00618e186716d7366207f909fb44641a8845baf) mstore(0x7c20, 0x16b51f53337d9b353024e90d2b9870fe165ef601abd795707d318ba5bcb211d4)mstore(0x7c40, mload(0x6220))success := and(eq(staticcall(gas(), 0x7, 0x7c00, 0x60, 0x7c00, 0x40), 1), success)mstore(0x7c60, mload(0x7b80)) mstore(0x7c80, mload(0x7ba0))mstore(0x7ca0, mload(0x7c00)) mstore(0x7cc0, mload(0x7c20))success := and(eq(staticcall(gas(), 0x6, 0x7c60, 0x80, 0x7c60, 0x40), 1), success)mstore(0x7ce0, 0x0bd8cdd311a39512e0f4c79e046470751b4a0cf050a865eeca84b31cef672341) mstore(0x7d00, 0x2dc5df62b48f53a8f1ee964a28d2ae7d32fdccd4a18dfd38fed88c05a4d16149)mstore(0x7d20, mload(0x6240))success := and(eq(staticcall(gas(), 0x7, 0x7ce0, 0x60, 0x7ce0, 0x40), 1), success)mstore(0x7d40, mload(0x7c60)) mstore(0x7d60, mload(0x7c80))mstore(0x7d80, mload(0x7ce0)) mstore(0x7da0, mload(0x7d00))success := and(eq(staticcall(gas(), 0x6, 0x7d40, 0x80, 0x7d40, 0x40), 1), success)mstore(0x7dc0, 0x028aa90bb886253410dcea14a0fcc7ee2e66ce14b81aec4715614c41bf7563ab) mstore(0x7de0, 0x26991cf8ac328075cc8c3acb7aee7fd287ea7c81dcb26a16b4539140dfcab001)mstore(0x7e00, mload(0x6260))success := and(eq(staticcall(gas(), 0x7, 0x7dc0, 0x60, 0x7dc0, 0x40), 1), success)mstore(0x7e20, mload(0x7d40)) mstore(0x7e40, mload(0x7d60))mstore(0x7e60, mload(0x7dc0)) mstore(0x7e80, mload(0x7de0))success := and(eq(staticcall(gas(), 0x6, 0x7e20, 0x80, 0x7e20, 0x40), 1), success)mstore(0x7ea0, 0x037d1f9567a4b280e0fa0a464db3f588dcbca2d083da31da1820f3686c61b04c) mstore(0x7ec0, 0x16a2ae3f6a18fc2f7b9bcb04c2f09795840bb5c60dfef3178a9cf5812e950d50)mstore(0x7ee0, mload(0x6280))success := and(eq(staticcall(gas(), 0x7, 0x7ea0, 0x60, 0x7ea0, 0x40), 1), success)mstore(0x7f00, mload(0x7e20)) mstore(0x7f20, mload(0x7e40))mstore(0x7f40, mload(0x7ea0)) mstore(0x7f60, mload(0x7ec0))success := and(eq(staticcall(gas(), 0x6, 0x7f00, 0x80, 0x7f00, 0x40), 1), success)mstore(0x7f80, 0x0ec0c922ea73b30b8180de1bc071f10ad77ace6def7b54caae1d4cc0095c1f78) mstore(0x7fa0, 0x146049ebdf9fb52936d6a4bed7148261823d713cdc7b897eb71feb3a5d0cef74)mstore(0x7fc0, mload(0x62a0))success := and(eq(staticcall(gas(), 0x7, 0x7f80, 0x60, 0x7f80, 0x40), 1), success)mstore(0x7fe0, mload(0x7f00)) mstore(0x8000, mload(0x7f20))mstore(0x8020, mload(0x7f80)) mstore(0x8040, mload(0x7fa0))success := and(eq(staticcall(gas(), 0x6, 0x7fe0, 0x80, 0x7fe0, 0x40), 1), success)mstore(0x8060, 0x080c6980a51e47dc74652e5e2ce4777899fa99c9dedb1cdb5aa63a21faf54236) mstore(0x8080, 0x1b6967f189da082c60d7b916d0cf4303334c9a2e615a2b094a0fd896b37bab9e)mstore(0x80a0, mload(0x62c0))success := and(eq(staticcall(gas(), 0x7, 0x8060, 0x60, 0x8060, 0x40), 1), success)mstore(0x80c0, mload(0x7fe0)) mstore(0x80e0, mload(0x8000))mstore(0x8100, mload(0x8060)) mstore(0x8120, mload(0x8080))success := and(eq(staticcall(gas(), 0x6, 0x80c0, 0x80, 0x80c0, 0x40), 1), success)mstore(0x8140, 0x280be2ede448f837f163473438439007cad788067f27fa14e53b7dc177ef2a59) mstore(0x8160, 0x00804c408a2860ec4020b49299dc4f2a96f27c6586da719d2907e2b0248edd24)mstore(0x8180, mload(0x62e0))success := and(eq(staticcall(gas(), 0x7, 0x8140, 0x60, 0x8140, 0x40), 1), success)mstore(0x81a0, mload(0x80c0)) mstore(0x81c0, mload(0x80e0))mstore(0x81e0, mload(0x8140)) mstore(0x8200, mload(0x8160))success := and(eq(staticcall(gas(), 0x6, 0x81a0, 0x80, 0x81a0, 0x40), 1), success)mstore(0x8220, 0x098c0774d8a6bb5e6f16db4af08273aaf352980e582eebd70323a69bec6e9569) mstore(0x8240, 0x1ae410753dde006f1c34513ddc66c05b17419a746dc468a32db541e4de6c3e5d)mstore(0x8260, mload(0x6300))success := and(eq(staticcall(gas(), 0x7, 0x8220, 0x60, 0x8220, 0x40), 1), success)mstore(0x8280, mload(0x81a0)) mstore(0x82a0, mload(0x81c0))mstore(0x82c0, mload(0x8220)) mstore(0x82e0, mload(0x8240))success := and(eq(staticcall(gas(), 0x6, 0x8280, 0x80, 0x8280, 0x40), 1), success)mstore(0x8300, 0x1c7529bd3f005de2d1319ef3faa0f2e301fdc4a80e5bc256297b7676875be33a) mstore(0x8320, 0x149f786b2355985e8a2aedb1974448bd312d1fda6231177f0915c35d96ac460f)mstore(0x8340, mload(0x6320))success := and(eq(staticcall(gas(), 0x7, 0x8300, 0x60, 0x8300, 0x40), 1), success)mstore(0x8360, mload(0x8280)) mstore(0x8380, mload(0x82a0))mstore(0x83a0, mload(0x8300)) mstore(0x83c0, mload(0x8320))success := and(eq(staticcall(gas(), 0x6, 0x8360, 0x80, 0x8360, 0x40), 1), success)mstore(0x83e0, 0x281dad8bf95d2164b0906c6b77b79bec3c5485308d8d619fe698994e6970af72) mstore(0x8400, 0x24e52f6a9fa13d1862a68e9cd782c0cfa63b5a75e83ff95ba33fad23bb13d304)mstore(0x8420, mload(0x6340))success := and(eq(staticcall(gas(), 0x7, 0x83e0, 0x60, 0x83e0, 0x40), 1), success)mstore(0x8440, mload(0x8360)) mstore(0x8460, mload(0x8380))mstore(0x8480, mload(0x83e0)) mstore(0x84a0, mload(0x8400))success := and(eq(staticcall(gas(), 0x6, 0x8440, 0x80, 0x8440, 0x40), 1), success)mstore(0x84c0, 0x213c15ae99dd2f66174198cd5dc8874c17b276b63e8d27509c6750b24b9d444d) mstore(0x84e0, 0x0fb99c21eca080d626c2a71695f3168c9fb9291719277f7fa85f535f5ac89fee)mstore(0x8500, mload(0x6360))success := and(eq(staticcall(gas(), 0x7, 0x84c0, 0x60, 0x84c0, 0x40), 1), success)mstore(0x8520, mload(0x8440)) mstore(0x8540, mload(0x8460))mstore(0x8560, mload(0x84c0)) mstore(0x8580, mload(0x84e0))success := and(eq(staticcall(gas(), 0x6, 0x8520, 0x80, 0x8520, 0x40), 1), success)mstore(0x85a0, 0x191a9edf71e99c534b6a65892a2be90b6d7ea46ef42633144570cb070927edf1) mstore(0x85c0, 0x097fcf51b5b4c39dcfbc97e1c34467d6bcfe994d3e74008a23056c623e36963d)mstore(0x85e0, mload(0x6380))success := and(eq(staticcall(gas(), 0x7, 0x85a0, 0x60, 0x85a0, 0x40), 1), success)mstore(0x8600, mload(0x8520)) mstore(0x8620, mload(0x8540))mstore(0x8640, mload(0x85a0)) mstore(0x8660, mload(0x85c0))success := and(eq(staticcall(gas(), 0x6, 0x8600, 0x80, 0x8600, 0x40), 1), success)mstore(0x8680, 0x1ea58e7845fc49f2556e2bc6ceb43fe04c18c0e0bd9be401b2e9649aae82deea) mstore(0x86a0, 0x08868558da7b21bb3d936d87405a4c08ca582ad92164580a95aa62323e7266d2)mstore(0x86c0, mload(0x63a0))success := and(eq(staticcall(gas(), 0x7, 0x8680, 0x60, 0x8680, 0x40), 1), success)mstore(0x86e0, mload(0x8600)) mstore(0x8700, mload(0x8620))mstore(0x8720, mload(0x8680)) mstore(0x8740, mload(0x86a0))success := and(eq(staticcall(gas(), 0x6, 0x86e0, 0x80, 0x86e0, 0x40), 1), success)mstore(0x8760, 0x06d289011bf01c484da50f75171f091dda7a75d61defc9a091065969c6ba0b41) mstore(0x8780, 0x1853b673466e1fdbc8afeb7953a1e5817cff8d36be59bd76cf6bf480d2bf6f9d)mstore(0x87a0, mload(0x63c0))success := and(eq(staticcall(gas(), 0x7, 0x8760, 0x60, 0x8760, 0x40), 1), success)mstore(0x87c0, mload(0x86e0)) mstore(0x87e0, mload(0x8700))mstore(0x8800, mload(0x8760)) mstore(0x8820, mload(0x8780))success := and(eq(staticcall(gas(), 0x6, 0x87c0, 0x80, 0x87c0, 0x40), 1), success)mstore(0x8840, 0x09a7332c71ded2c1a03ea2f167d117e3051805f6f5008a26c718ebb85d2b1bf7) mstore(0x8860, 0x2928a92fa94da957f331277462d426534a31c5c30e7702735d12c4a7155b6f09)mstore(0x8880, mload(0x63e0))success := and(eq(staticcall(gas(), 0x7, 0x8840, 0x60, 0x8840, 0x40), 1), success)mstore(0x88a0, mload(0x87c0)) mstore(0x88c0, mload(0x87e0))mstore(0x88e0, mload(0x8840)) mstore(0x8900, mload(0x8860))success := and(eq(staticcall(gas(), 0x6, 0x88a0, 0x80, 0x88a0, 0x40), 1), success)mstore(0x8920, mload(0x7c0)) mstore(0x8940, mload(0x7e0))mstore(0x8960, mload(0x6400))success := and(eq(staticcall(gas(), 0x7, 0x8920, 0x60, 0x8920, 0x40), 1), success)mstore(0x8980, mload(0x88a0)) mstore(0x89a0, mload(0x88c0))mstore(0x89c0, mload(0x8920)) mstore(0x89e0, mload(0x8940))success := and(eq(staticcall(gas(), 0x6, 0x8980, 0x80, 0x8980, 0x40), 1), success)mstore(0x8a00, mload(0x800)) mstore(0x8a20, mload(0x820))mstore(0x8a40, mload(0x6420))success := and(eq(staticcall(gas(), 0x7, 0x8a00, 0x60, 0x8a00, 0x40), 1), success)mstore(0x8a60, mload(0x8980)) mstore(0x8a80, mload(0x89a0))mstore(0x8aa0, mload(0x8a00)) mstore(0x8ac0, mload(0x8a20))success := and(eq(staticcall(gas(), 0x6, 0x8a60, 0x80, 0x8a60, 0x40), 1), success)mstore(0x8ae0, mload(0x840)) mstore(0x8b00, mload(0x860))mstore(0x8b20, mload(0x6440))success := and(eq(staticcall(gas(), 0x7, 0x8ae0, 0x60, 0x8ae0, 0x40), 1), success)mstore(0x8b40, mload(0x8a60)) mstore(0x8b60, mload(0x8a80))mstore(0x8b80, mload(0x8ae0)) mstore(0x8ba0, mload(0x8b00))success := and(eq(staticcall(gas(), 0x6, 0x8b40, 0x80, 0x8b40, 0x40), 1), success)mstore(0x8bc0, mload(0x720)) mstore(0x8be0, mload(0x740))mstore(0x8c00, mload(0x6460))success := and(eq(staticcall(gas(), 0x7, 0x8bc0, 0x60, 0x8bc0, 0x40), 1), success)mstore(0x8c20, mload(0x8b40)) mstore(0x8c40, mload(0x8b60))mstore(0x8c60, mload(0x8bc0)) mstore(0x8c80, mload(0x8be0))success := and(eq(staticcall(gas(), 0x6, 0x8c20, 0x80, 0x8c20, 0x40), 1), success)mstore(0x8ca0, mload(0x520)) mstore(0x8cc0, mload(0x540))mstore(0x8ce0, mload(0x67e0))success := and(eq(staticcall(gas(), 0x7, 0x8ca0, 0x60, 0x8ca0, 0x40), 1), success)mstore(0x8d00, mload(0x8c20)) mstore(0x8d20, mload(0x8c40))mstore(0x8d40, mload(0x8ca0)) mstore(0x8d60, mload(0x8cc0))success := and(eq(staticcall(gas(), 0x6, 0x8d00, 0x80, 0x8d00, 0x40), 1), success)mstore(0x8d80, mload(0x560)) mstore(0x8da0, mload(0x580))mstore(0x8dc0, mload(0x6800))success := and(eq(staticcall(gas(), 0x7, 0x8d80, 0x60, 0x8d80, 0x40), 1), success)mstore(0x8de0, mload(0x8d00)) mstore(0x8e00, mload(0x8d20))mstore(0x8e20, mload(0x8d80)) mstore(0x8e40, mload(0x8da0))success := and(eq(staticcall(gas(), 0x6, 0x8de0, 0x80, 0x8de0, 0x40), 1), success)mstore(0x8e60, mload(0x5a0)) mstore(0x8e80, mload(0x5c0))mstore(0x8ea0, mload(0x6820))success := and(eq(staticcall(gas(), 0x7, 0x8e60, 0x60, 0x8e60, 0x40), 1), success)mstore(0x8ec0, mload(0x8de0)) mstore(0x8ee0, mload(0x8e00))mstore(0x8f00, mload(0x8e60)) mstore(0x8f20, mload(0x8e80))success := and(eq(staticcall(gas(), 0x6, 0x8ec0, 0x80, 0x8ec0, 0x40), 1), success)mstore(0x8f40, mload(0x5e0)) mstore(0x8f60, mload(0x600))mstore(0x8f80, mload(0x6840))success := and(eq(staticcall(gas(), 0x7, 0x8f40, 0x60, 0x8f40, 0x40), 1), success)mstore(0x8fa0, mload(0x8ec0)) mstore(0x8fc0, mload(0x8ee0))mstore(0x8fe0, mload(0x8f40)) mstore(0x9000, mload(0x8f60))success := and(eq(staticcall(gas(), 0x6, 0x8fa0, 0x80, 0x8fa0, 0x40), 1), success)mstore(0x9020, mload(0x620)) mstore(0x9040, mload(0x640))mstore(0x9060, mload(0x6860))success := and(eq(staticcall(gas(), 0x7, 0x9020, 0x60, 0x9020, 0x40), 1), success)mstore(0x9080, mload(0x8fa0)) mstore(0x90a0, mload(0x8fc0))mstore(0x90c0, mload(0x9020)) mstore(0x90e0, mload(0x9040))success := and(eq(staticcall(gas(), 0x6, 0x9080, 0x80, 0x9080, 0x40), 1), success)mstore(0x9100, mload(0x660)) mstore(0x9120, mload(0x680))mstore(0x9140, mload(0x6aa0))success := and(eq(staticcall(gas(), 0x7, 0x9100, 0x60, 0x9100, 0x40), 1), success)mstore(0x9160, mload(0x9080)) mstore(0x9180, mload(0x90a0))mstore(0x91a0, mload(0x9100)) mstore(0x91c0, mload(0x9120))success := and(eq(staticcall(gas(), 0x6, 0x9160, 0x80, 0x9160, 0x40), 1), success)mstore(0x91e0, mload(0x6a0)) mstore(0x9200, mload(0x6c0))mstore(0x9220, mload(0x6ac0))success := and(eq(staticcall(gas(), 0x7, 0x91e0, 0x60, 0x91e0, 0x40), 1), success)mstore(0x9240, mload(0x9160)) mstore(0x9260, mload(0x9180))mstore(0x9280, mload(0x91e0)) mstore(0x92a0, mload(0x9200))success := and(eq(staticcall(gas(), 0x6, 0x9240, 0x80, 0x9240, 0x40), 1), success)mstore(0x92c0, mload(0x6e0)) mstore(0x92e0, mload(0x700))mstore(0x9300, mload(0x6ae0))success := and(eq(staticcall(gas(), 0x7, 0x92c0, 0x60, 0x92c0, 0x40), 1), success)mstore(0x9320, mload(0x9240)) mstore(0x9340, mload(0x9260))mstore(0x9360, mload(0x92c0)) mstore(0x9380, mload(0x92e0))success := and(eq(staticcall(gas(), 0x6, 0x9320, 0x80, 0x9320, 0x40), 1), success)mstore(0x93a0, mload(0x360)) mstore(0x93c0, mload(0x380))mstore(0x93e0, mload(0x6c80))success := and(eq(staticcall(gas(), 0x7, 0x93a0, 0x60, 0x93a0, 0x40), 1), success)mstore(0x9400, mload(0x9320)) mstore(0x9420, mload(0x9340))mstore(0x9440, mload(0x93a0)) mstore(0x9460, mload(0x93c0))success := and(eq(staticcall(gas(), 0x6, 0x9400, 0x80, 0x9400, 0x40), 1), success)mstore(0x9480, mload(0x3e0)) mstore(0x94a0, mload(0x400))mstore(0x94c0, mload(0x6ca0))success := and(eq(staticcall(gas(), 0x7, 0x9480, 0x60, 0x9480, 0x40), 1), success)mstore(0x94e0, mload(0x9400)) mstore(0x9500, mload(0x9420))mstore(0x9520, mload(0x9480)) mstore(0x9540, mload(0x94a0))success := and(eq(staticcall(gas(), 0x6, 0x94e0, 0x80, 0x94e0, 0x40), 1), success)mstore(0x9560, mload(0x1320)) mstore(0x9580, mload(0x1340))mstore(0x95a0, sub(f_q, mload(0x6ce0)))success := and(eq(staticcall(gas(), 0x7, 0x9560, 0x60, 0x9560, 0x40), 1), success)mstore(0x95c0, mload(0x94e0)) mstore(0x95e0, mload(0x9500))mstore(0x9600, mload(0x9560)) mstore(0x9620, mload(0x9580))success := and(eq(staticcall(gas(), 0x6, 0x95c0, 0x80, 0x95c0, 0x40), 1), success)mstore(0x9640, mload(0x13c0)) mstore(0x9660, mload(0x13e0))mstore(0x9680, mload(0x6d00))success := and(eq(staticcall(gas(), 0x7, 0x9640, 0x60, 0x9640, 0x40), 1), success)mstore(0x96a0, mload(0x95c0)) mstore(0x96c0, mload(0x95e0))mstore(0x96e0, mload(0x9640)) mstore(0x9700, mload(0x9660))success := and(eq(staticcall(gas(), 0x6, 0x96a0, 0x80, 0x96a0, 0x40), 1), success)mstore(0x9720, mload(0x96a0)) mstore(0x9740, mload(0x96c0))mstore(0x9760, 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2) mstore(0x9780, 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed) mstore(0x97a0, 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b) mstore(0x97c0, 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa)mstore(0x97e0, mload(0x13c0)) mstore(0x9800, mload(0x13e0))mstore(0x9820, 0x0181624e80f3d6ae28df7e01eaeab1c0e919877a3b8a6b7fbc69a6817d596ea2) mstore(0x9840, 0x1783d30dcb12d259bb89098addf6280fa4b653be7a152542a28f7b926e27e648) mstore(0x9860, 0x00ae44489d41a0d179e2dfdc03bddd883b7109f8b6ae316a59e815c1a6b35304) mstore(0x9880, 0x0b2147ab62a386bd63e6de1522109b8c9588ab466f5aadfde8c41ca3749423ee)success := and(eq(staticcall(gas(), 0x8, 0x9720, 0x180, 0x9720, 0x20), 1), success)success := and(eq(mload(0x9720), 1), success)} return success; } } diff --git a/eth-types/src/spec.rs b/eth-types/src/spec.rs index 7ade5713..eeb406c3 100644 --- a/eth-types/src/spec.rs +++ b/eth-types/src/spec.rs @@ -12,6 +12,8 @@ pub trait Spec: 'static + Sized + Copy + Default + Debug { const EXECUTION_STATE_ROOT_DEPTH: usize; const FINALIZED_HEADER_INDEX: usize; const FINALIZED_HEADER_DEPTH: usize; + const BYTES_PER_LOGS_BLOOM: usize = 256; + const MAX_EXTRA_DATA_BYTES: usize = 32; } #[derive(Copy, Clone, PartialEq, Eq, Debug, Default)] diff --git a/justfile b/justfile index 507879ab..3773303f 100644 --- a/justfile +++ b/justfile @@ -14,19 +14,19 @@ lint: fmt cargo clippy --all-targets --all-features --workspace setup-step-circuit: - cargo run -r -- sync-step -c ./lightclient-circuits/config/sync_step.json -o artifacts -k 22 + cargo run -r -- circuit sync-step -c ./lightclient-circuits/config/sync_step.json -o artifacts -k 22 setup-rotation-circuit: - cargo run -r -- committee-update -c ./lightclient-circuits/config/committee_update.json -o artifacts -k 18 + cargo run -r -- circuit committee-update -c ./lightclient-circuits/config/committee_update.json -o artifacts -k 18 # TODO: generate committee-update snark - cargo run -r -- aggregation -c ./lightclient-circuits/config/aggregation.json --app-pk-path \ + cargo run -r -- circuit aggregation -c ./lightclient-circuits/config/aggregation.json --app-pk-path \ ./build/committee_update.pkey --app-config-path ./lightclient-circuits/config/committee_update.json -i ./rotation -o artifacts -k 22 gen-step-evm-verifier: - cargo run -r -- sync-step -c ./lightclient-circuits/config/sync_step.json -o evm-verifier ./contracts/snark-verifiers/sync_step.yul + cargo run -r -- circuit sync-step -c ./lightclient-circuits/config/sync_step.json -o evm-verifier ./contracts/snark-verifiers/sync_step.yul gen-rotation-evm-verifier: - cargo run -r -- aggregation -c ./lightclient-circuits/config/aggregation.json --app-pk-path ./build/committee_update.pkey --app-config-path ./lightclient-circuits/config/committee_update.json -i ./rotation -o evm-verifier ./contracts/snark-verifiers/committee_update_aggregated.yul + cargo run -r -- circuit aggregation -c ./lightclient-circuits/config/aggregation.json --app-pk-path ./build/committee_update.pkey --app-config-path ./lightclient-circuits/config/committee_update.json -i ./rotation -o evm-verifier ./contracts/snark-verifiers/committee_update_aggregated.yul build-contracts: cd contracts && forge build diff --git a/lightclient-circuits/Cargo.toml b/lightclient-circuits/Cargo.toml index 663aa18c..3167d5ff 100644 --- a/lightclient-circuits/Cargo.toml +++ b/lightclient-circuits/Cargo.toml @@ -22,8 +22,8 @@ pasta_curves.workspace = true ff.workspace = true sha2.workspace = true # ethereum -sync-committee-primitives.workspace = true -ssz-rs.workspace = true +ssz_rs = { workspace = true, features = ["serde"] } +ethereum-consensus-types ={ workspace = true, features = ["serde"] } # local eth-types.workspace = true diff --git a/lightclient-circuits/src/committee_update_circuit.rs b/lightclient-circuits/src/committee_update_circuit.rs index 58fe8d57..e3713052 100644 --- a/lightclient-circuits/src/committee_update_circuit.rs +++ b/lightclient-circuits/src/committee_update_circuit.rs @@ -53,7 +53,6 @@ use pasta_curves::group::{ff, GroupEncoding}; use poseidon::PoseidonChip; use snark_verifier_sdk::CircuitExt; use ssz_rs::{Merkleized, Vector}; -use sync_committee_primitives::consensus_types::BeaconBlockHeader; #[allow(type_alias_bounds)] #[derive(Clone, Debug, Default)] @@ -162,8 +161,13 @@ impl CommitteeUpdateCircuit { let finalized_header_root = args.finalized_header.clone().hash_tree_root().unwrap(); let instance_vec = iter::once(poseidon_commitment) - .chain(ssz_root.0.map(|b| bn256::Fr::from(b as u64))) - .chain(finalized_header_root.0.map(|b| bn256::Fr::from(b as u64))) + .chain(ssz_root.as_ref().iter().map(|b| bn256::Fr::from(*b as u64))) + .chain( + finalized_header_root + .as_ref() + .iter() + .map(|b| bn256::Fr::from(*b as u64)), + ) .collect(); vec![instance_vec] @@ -277,6 +281,7 @@ mod tests { use super::*; use ark_std::{end_timer, start_timer}; use eth_types::Testnet; + use ethereum_consensus_types::BeaconBlockHeader; use halo2_base::{ gates::{ builder::{CircuitBuilderStage, FlexGateConfigParams}, @@ -325,7 +330,7 @@ mod tests { pubkeys_compressed, randomness: constant_randomness(), _spec: PhantomData, - finalized_header: finalized_header, + finalized_header, sync_committee_branch: committee_root_branch, } } @@ -432,7 +437,7 @@ mod tests { let agg_circuit = AggregationCircuit::create_circuit( CircuitBuilderStage::Prover, Some(agg_config), - &vec![snark.clone()], + &vec![snark], AGG_K, ) .unwrap(); @@ -551,7 +556,7 @@ mod tests { Some(pinning.break_points), lookup_bits, &p1, - iter::once(l0_snark.clone()), + iter::once(l0_snark), ); circuit.expose_previous_instances(false); @@ -584,7 +589,7 @@ mod tests { ); let mut circuit = AggregationCircuit::prover::( &p2, - iter::once(l1_snark.clone()), + iter::once(l1_snark), pinning.break_points, ); circuit.expose_previous_instances(true); diff --git a/lightclient-circuits/src/poseidon.rs b/lightclient-circuits/src/poseidon.rs index a3403716..4a3d9067 100644 --- a/lightclient-circuits/src/poseidon.rs +++ b/lightclient-circuits/src/poseidon.rs @@ -1,11 +1,13 @@ use crate::gadget::crypto::G1Point; use eth_types::{AppCurveExt, Field, Spec}; +use group::UncompressedEncoding; use halo2_base::safe_types::ScalarField; use halo2_base::{safe_types::GateInstructions, AssignedValue, Context}; use halo2_ecc::bigint::{ProperCrtUint, ProperUint}; use halo2_proofs::plonk::Error; use halo2curves::bls12_381::G1; use halo2curves::bls12_381::{self, G1Affine}; +use halo2curves::bn256; use itertools::Itertools; use poseidon::PoseidonChip; use poseidon_native::Poseidon as PoseidonNative; @@ -86,3 +88,32 @@ pub fn poseidon_sponge( Ok(current_poseidon_hash.unwrap()) } + +pub fn poseidon_committee_commitment_from_uncompressed( + pubkeys_uncompressed: &[Vec], +) -> Result<[u8; 32], Error> { + let pubkey_affines = pubkeys_uncompressed + .iter() + .cloned() + .map(|bytes| { + halo2curves::bls12_381::G1Affine::from_uncompressed_unchecked( + &bytes.as_slice().try_into().unwrap(), + ) + .unwrap() + }) + .collect_vec(); + let poseidon_commitment = + fq_array_poseidon_native::(pubkey_affines.iter().map(|p| p.x)).unwrap(); + Ok(poseidon_commitment.to_bytes_le().try_into().unwrap()) +} + +pub fn poseidon_committee_commitment_from_compressed( + pubkeys_compressed: &[Vec], +) -> Result<[u8; 32], Error> { + let pubkeys_x = pubkeys_compressed.iter().cloned().map(|mut bytes| { + bytes[47] &= 0b00011111; + bls12_381::Fq::from_bytes_le(&bytes) + }); + let poseidon_commitment = fq_array_poseidon_native::(pubkeys_x).unwrap(); + Ok(poseidon_commitment.to_bytes_le().try_into().unwrap()) +} diff --git a/lightclient-circuits/src/sync_step_circuit.rs b/lightclient-circuits/src/sync_step_circuit.rs index cacc6b2d..6f3999a9 100644 --- a/lightclient-circuits/src/sync_step_circuit.rs +++ b/lightclient-circuits/src/sync_step_circuit.rs @@ -289,7 +289,7 @@ impl SyncStepCircuit { .clone() .hash_tree_root() .unwrap() - .as_bytes() + .as_ref() .try_into() .unwrap(); diff --git a/lightclient-circuits/src/witness/rotation.rs b/lightclient-circuits/src/witness/rotation.rs index 2cb65422..43e06476 100644 --- a/lightclient-circuits/src/witness/rotation.rs +++ b/lightclient-circuits/src/witness/rotation.rs @@ -6,17 +6,16 @@ use crate::gadget::crypto::constant_randomness; use super::HashInput; use eth_types::AppCurveExt; use eth_types::{Field, Spec}; -use sync_committee_primitives::consensus_types::{BeaconBlockHeader, BeaconState}; - +use ethereum_consensus_types::BeaconBlockHeader; use halo2curves::bls12_381::Fq; use halo2curves::bls12_381::G1; use itertools::Itertools; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; use ssz_rs::Merkleized; use ssz_rs::Node; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct CommitteeRotationArgs { pub pubkeys_compressed: Vec>, diff --git a/lightclient-circuits/src/witness/sync.rs b/lightclient-circuits/src/witness/sync.rs index 575c2988..d09551be 100644 --- a/lightclient-circuits/src/witness/sync.rs +++ b/lightclient-circuits/src/witness/sync.rs @@ -3,13 +3,15 @@ use std::marker::PhantomData; use super::HashInput; use eth_types::{Field, Spec}; +use ethereum_consensus_types; use itertools::Itertools; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; use ssz_rs::{Merkleized, Node}; -use sync_committee_primitives::consensus_types::{BeaconBlockHeader, BeaconState}; +// use sync_committee_primitives::consensus_types::{BeaconBlockHeader, BeaconState}; +use ethereum_consensus_types::BeaconBlockHeader; -#[derive(Debug, Clone, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct SyncStepArgs { pub signature_compressed: Vec, @@ -57,7 +59,7 @@ impl Default for SyncStepArgs { compute_root(execution_state_root.clone(), &state_merkle_branch); let mut finalized_block = BeaconBlockHeader { - body_root: Node::from_bytes(beacon_block_body_root.try_into().unwrap()), + body_root: Node::try_from(beacon_block_body_root.as_slice()).unwrap(), ..Default::default() }; let finalized_header = finalized_block.hash_tree_root().unwrap().as_ref().to_vec(); diff --git a/preprocessor/Cargo.toml b/preprocessor/Cargo.toml index 8feb3a00..88a7feb1 100644 --- a/preprocessor/Cargo.toml +++ b/preprocessor/Cargo.toml @@ -4,11 +4,10 @@ version = "0.1.0" edition = "2021" [dependencies] -ssz-rs.workspace = true +ssz_rs.workspace = true halo2curves.workspace = true -sync-committee-prover.workspace = true -sync-committee-primitives.workspace = true group.workspace = true +beacon-api-client.workspace = true # misc eyre = "0.6" @@ -19,10 +18,12 @@ log.workspace = true itertools.workspace = true serde_json.workspace = true serde.workspace = true - +ethereum-consensus-types.workspace = true +zipline-cryptography = { workspace = true, features = ["serde"] } # local eth-types.workspace = true lightclient-circuits.workspace = true +reqwest = "0.11.22" [dev-dependencies] halo2_proofs.workspace = true diff --git a/preprocessor/src/lib.rs b/preprocessor/src/lib.rs index 2d38face..a4e2098c 100644 --- a/preprocessor/src/lib.rs +++ b/preprocessor/src/lib.rs @@ -1,4 +1,206 @@ +#![feature(generic_const_exprs)] + mod sync; +use std::ops::Deref; + +use beacon_api_client::{BlockId, Client, ClientTypes, Value, VersionedValue}; +use eth_types::Spec; +use ethereum_consensus_types::{ + BeaconBlockHeader, ByteVector, LightClientBootstrap, LightClientFinalityUpdate, + LightClientUpdateCapella, Root, +}; +use halo2curves::bn256::Fr; +use itertools::Itertools; +use lightclient_circuits::witness::{CommitteeRotationArgs, SyncStepArgs}; +use serde::{Deserialize, Serialize}; +use ssz_rs::{Node, Vector}; pub use sync::*; mod rotation; pub use rotation::*; +use zipline_cryptography::bls::BlsPublicKey; +use zipline_cryptography::bls::BlsSignature; +pub async fn light_client_update_to_args( + update: &mut LightClientUpdateCapella< + { S::SYNC_COMMITTEE_SIZE }, + { S::SYNC_COMMITTEE_ROOT_INDEX }, + { S::SYNC_COMMITTEE_DEPTH }, + { S::FINALIZED_HEADER_INDEX }, + { S::FINALIZED_HEADER_DEPTH }, + { S::BYTES_PER_LOGS_BLOOM }, + { S::MAX_EXTRA_DATA_BYTES }, + >, + pubkeys_compressed: Vector, + domain: [u8; 32], +) -> eyre::Result<(SyncStepArgs, CommitteeRotationArgs)> +where + [(); S::SYNC_COMMITTEE_SIZE]:, + [(); S::FINALIZED_HEADER_DEPTH]:, + [(); S::BYTES_PER_LOGS_BLOOM]:, + [(); S::MAX_EXTRA_DATA_BYTES]:, + [(); S::SYNC_COMMITTEE_ROOT_INDEX]:, + [(); S::SYNC_COMMITTEE_DEPTH]:, + [(); S::FINALIZED_HEADER_INDEX]:, +{ + let finality_update = LightClientFinalityUpdate { + attested_header: update.attested_header.clone(), + finalized_header: update.finalized_header.clone(), + finality_branch: Vector::try_from( + update + .finality_branch + .iter() + .map(|v| ByteVector(Vector::try_from(v.deref().to_vec()).unwrap())) + .collect_vec(), + ) + .unwrap(), + sync_aggregate: update.sync_aggregate.clone(), + signature_slot: update.signature_slot, + }; + + let rotation_args = rotation::rotation_args_from_update(update).await?; + + let sync_args = + sync::step_args_from_finality_update(finality_update, pubkeys_compressed, domain).await?; + + Ok((sync_args, rotation_args)) +} + +pub async fn get_block_header( + client: &Client, + id: BlockId, +) -> eyre::Result { + // TODO: Once the ethereum beacon_api_client is updated, we can avoid this struct definition + #[derive(Serialize, Deserialize)] + struct BeaconHeaderSummary { + pub root: Root, + pub canonical: bool, + pub header: SignedBeaconBlockHeader, + } + #[derive(Serialize, Deserialize)] + struct SignedBeaconBlockHeader { + pub message: BeaconBlockHeader, + pub signature: BlsSignature, + } + + let route = format!("eth/v1/beacon/headers/{id}"); + let block: BeaconHeaderSummary = client.get::>(&route).await?.data; + Ok(block.header.message) +} + +pub async fn get_light_client_update_at_current_period( + client: &Client, +) -> eyre::Result< + LightClientUpdateCapella< + { S::SYNC_COMMITTEE_SIZE }, + { S::SYNC_COMMITTEE_ROOT_INDEX }, + { S::SYNC_COMMITTEE_DEPTH }, + { S::FINALIZED_HEADER_INDEX }, + { S::FINALIZED_HEADER_DEPTH }, + { S::BYTES_PER_LOGS_BLOOM }, + { S::MAX_EXTRA_DATA_BYTES }, + >, +> +where + [(); S::SYNC_COMMITTEE_SIZE]:, + [(); S::FINALIZED_HEADER_DEPTH]:, + [(); S::BYTES_PER_LOGS_BLOOM]:, + [(); S::MAX_EXTRA_DATA_BYTES]:, + [(); S::SYNC_COMMITTEE_ROOT_INDEX]:, + [(); S::SYNC_COMMITTEE_DEPTH]:, + [(); S::FINALIZED_HEADER_INDEX]:, +{ + let block = get_block_header(client, BlockId::Head).await?; + let slot = block.slot; + let period = slot / (32 * 256); + + let route = format!("eth/v1/beacon/light_client/updates"); + let mut updates: Vec> = client + .http + .get(client.endpoint.join(&route)?) + .query(&[("start_period", period), ("count", 1)]) + .send() + .await? + .json() + .await?; + assert!(updates.len() == 1, "should only get one update"); + Ok(updates.pop().unwrap().data) +} + +pub async fn get_light_client_update_at_period( + client: &Client, + period: u64, +) -> eyre::Result< + LightClientUpdateCapella< + { S::SYNC_COMMITTEE_SIZE }, + { S::SYNC_COMMITTEE_ROOT_INDEX }, + { S::SYNC_COMMITTEE_DEPTH }, + { S::FINALIZED_HEADER_INDEX }, + { S::FINALIZED_HEADER_DEPTH }, + { S::BYTES_PER_LOGS_BLOOM }, + { S::MAX_EXTRA_DATA_BYTES }, + >, +> +where + [(); S::SYNC_COMMITTEE_SIZE]:, + [(); S::FINALIZED_HEADER_DEPTH]:, + [(); S::BYTES_PER_LOGS_BLOOM]:, + [(); S::MAX_EXTRA_DATA_BYTES]:, + [(); S::SYNC_COMMITTEE_ROOT_INDEX]:, + [(); S::SYNC_COMMITTEE_DEPTH]:, + [(); S::FINALIZED_HEADER_INDEX]:, +{ + let route = format!("eth/v1/beacon/light_client/updates"); + let mut updates: Vec> = client + .http + .get(client.endpoint.join(&route)?) + .query(&[("start_period", period), ("count", 1)]) + .send() + .await? + .json() + .await?; + assert!(updates.len() == 1, "should only get one update"); + Ok(updates.pop().unwrap().data) +} + +pub async fn get_light_client_bootstrap( + client: &Client, + block_root: Node, +) -> eyre::Result< + LightClientBootstrap< + { S::SYNC_COMMITTEE_SIZE }, + { S::SYNC_COMMITTEE_DEPTH }, + { S::BYTES_PER_LOGS_BLOOM }, + { S::MAX_EXTRA_DATA_BYTES }, + >, +> +where + [(); S::SYNC_COMMITTEE_SIZE]:, + [(); S::BYTES_PER_LOGS_BLOOM]:, + [(); S::MAX_EXTRA_DATA_BYTES]:, + [(); S::SYNC_COMMITTEE_DEPTH]:, +{ + let route = format!("eth/v1/beacon/light_client/bootstrap/{block_root:?}"); + let bootstrap = client.get::>(&route).await?.data; + Ok(bootstrap) +} + +pub async fn get_light_client_finality_update( + client: &Client, +) -> eyre::Result< + LightClientFinalityUpdate< + { S::SYNC_COMMITTEE_SIZE }, + { S::FINALIZED_HEADER_DEPTH }, + { S::BYTES_PER_LOGS_BLOOM }, + { S::MAX_EXTRA_DATA_BYTES }, + >, +> +where + [(); S::SYNC_COMMITTEE_SIZE]:, + [(); S::BYTES_PER_LOGS_BLOOM]:, + [(); S::MAX_EXTRA_DATA_BYTES]:, + [(); S::FINALIZED_HEADER_DEPTH]:, +{ + Ok(client + .get::>("eth/v1/beacon/light_client/finality_update") + .await? + .data) +} diff --git a/preprocessor/src/rotation.rs b/preprocessor/src/rotation.rs index f68518c4..7c7d7af6 100644 --- a/preprocessor/src/rotation.rs +++ b/preprocessor/src/rotation.rs @@ -1,65 +1,103 @@ use std::marker::PhantomData; +use beacon_api_client::{BlockId, Client, ClientTypes}; use eth_types::Spec; +use ethereum_consensus_types::{BeaconBlockHeader, LightClientUpdateCapella}; use halo2curves::bn256::Fr; use itertools::Itertools; use lightclient_circuits::{gadget::crypto, witness::CommitteeRotationArgs}; +use log::debug; use ssz_rs::Merkleized; -use sync_committee_primitives::consensus_types::BeaconBlockHeader; -use sync_committee_prover::SyncCommitteeProver; use tokio::fs; -pub async fn fetch_rotation_args( - node_url: String, -) -> eyre::Result> { - let client = SyncCommitteeProver::new(node_url); - let finalized_header = client.fetch_header("finalized").await.unwrap(); - let mut finalized_state = client - .fetch_beacon_state(&finalized_header.state_root.to_string()) - .await - .unwrap(); - let pubkeys_compressed = finalized_state +use crate::{get_block_header, get_light_client_update_at_period}; + +pub async fn fetch_rotation_args( + client: &Client, +) -> eyre::Result> +where + [(); S::SYNC_COMMITTEE_SIZE]:, + [(); S::FINALIZED_HEADER_DEPTH]:, + [(); S::BYTES_PER_LOGS_BLOOM]:, + [(); S::MAX_EXTRA_DATA_BYTES]:, + [(); S::SYNC_COMMITTEE_ROOT_INDEX]:, + [(); S::SYNC_COMMITTEE_DEPTH]:, + [(); S::FINALIZED_HEADER_INDEX]:, +{ + let block = get_block_header(client, BlockId::Head).await?; + let slot = block.slot; + let period = slot / (32 * 256); + debug!( + "Fetching light client update at current Slot: {} at Period: {}", + slot, period + ); + + let mut update = get_light_client_update_at_period(client, period).await?; + rotation_args_from_update(&mut update).await +} + +pub async fn rotation_args_from_update( + update: &mut LightClientUpdateCapella< + { S::SYNC_COMMITTEE_SIZE }, + { S::SYNC_COMMITTEE_ROOT_INDEX }, + { S::SYNC_COMMITTEE_DEPTH }, + { S::FINALIZED_HEADER_INDEX }, + { S::FINALIZED_HEADER_DEPTH }, + { S::BYTES_PER_LOGS_BLOOM }, + { S::MAX_EXTRA_DATA_BYTES }, + >, +) -> eyre::Result> +where + [(); S::SYNC_COMMITTEE_SIZE]:, + [(); S::FINALIZED_HEADER_DEPTH]:, + [(); S::BYTES_PER_LOGS_BLOOM]:, + [(); S::MAX_EXTRA_DATA_BYTES]:, + [(); S::SYNC_COMMITTEE_ROOT_INDEX]:, + [(); S::SYNC_COMMITTEE_DEPTH]:, + [(); S::FINALIZED_HEADER_INDEX]:, +{ + let pubkeys_compressed = update .next_sync_committee - .public_keys + .pubkeys .iter() - .map(|pk| pk.to_vec()) + .map(|pk| pk.to_bytes().to_vec()) .collect_vec(); - - let mut sync_committee_branch = - ssz_rs::generate_proof(&mut finalized_state, &[S::SYNC_COMMITTEE_ROOT_INDEX * 2]).unwrap(); + let mut sync_committee_branch = update.next_sync_committee_branch.as_ref().to_vec(); sync_committee_branch.insert( 0, - finalized_state + update .next_sync_committee - .aggregate_public_key + .aggregate_pubkey .hash_tree_root() .unwrap(), ); + assert!( - ssz_rs::verify_merkle_proof( - &finalized_state - .next_sync_committee - .public_keys - .hash_tree_root() - .unwrap(), - &sync_committee_branch, - &ssz_rs::GeneralizedIndex(S::SYNC_COMMITTEE_ROOT_INDEX * 2), - &finalized_header.state_root, - ), + ssz_rs::is_valid_merkle_branch( + update.next_sync_committee.pubkeys.hash_tree_root().unwrap(), + &sync_committee_branch + .iter() + .map(|n| n.as_ref()) + .collect_vec(), + S::SYNC_COMMITTEE_PUBKEYS_DEPTH, + S::SYNC_COMMITTEE_PUBKEYS_ROOT_INDEX, + update.attested_header.beacon.state_root, + ) + .is_ok(), "Execution payload merkle proof verification failed" ); + let args = CommitteeRotationArgs:: { pubkeys_compressed, randomness: crypto::constant_randomness(), - finalized_header, + finalized_header: update.attested_header.beacon.clone(), sync_committee_branch: sync_committee_branch - .iter() - .map(|n| n.as_bytes().to_vec()) + .into_iter() + .map(|n| n.to_vec()) .collect_vec(), _spec: PhantomData, }; - Ok(args) } @@ -95,6 +133,8 @@ pub async fn read_rotation_args( #[cfg(test)] mod tests { + use super::*; + use beacon_api_client::mainnet::Client as MainnetClient; use eth_types::Testnet; use halo2_base::gates::builder::CircuitBuilderStage; use halo2_proofs::dev::MockProver; @@ -103,18 +143,15 @@ mod tests { committee_update_circuit::CommitteeUpdateCircuit, util::{gen_srs, AppCircuit, Eth2ConfigPinning, Halo2ConfigPinning}, }; + use reqwest::Url; use snark_verifier_sdk::CircuitExt; - use super::*; - #[tokio::test] async fn test_rotation_circuit_sepolia() { const CONFIG_PATH: &str = "../lightclient-circuits/config/committee_update.json"; const K: u32 = 21; - - let witness = fetch_rotation_args::("http://3.128.78.74:5052".to_string()) - .await - .unwrap(); + let client = MainnetClient::new(Url::parse("http://65.109.55.120:9596").unwrap()); + let witness = fetch_rotation_args::(&client).await.unwrap(); let pinning = Eth2ConfigPinning::from_path(CONFIG_PATH); let circuit = CommitteeUpdateCircuit::::create_circuit( @@ -142,10 +179,8 @@ mod tests { false, &CommitteeRotationArgs::::default(), ); - - let witness = fetch_rotation_args::("http://3.128.78.74:5052".to_string()) - .await - .unwrap(); + let client = MainnetClient::new(Url::parse("http://65.109.55.120:9596").unwrap()); + let witness = fetch_rotation_args::(&client).await.unwrap(); CommitteeUpdateCircuit::::gen_snark_shplonk( ¶ms, diff --git a/preprocessor/src/sync.rs b/preprocessor/src/sync.rs index 6906cbd5..12961901 100644 --- a/preprocessor/src/sync.rs +++ b/preprocessor/src/sync.rs @@ -1,171 +1,168 @@ use std::marker::PhantomData; +use beacon_api_client::Client; +use beacon_api_client::{BlockId, ClientTypes, StateId}; use eth_types::Spec; -use halo2curves::{bls12_381::G1Affine, group::GroupEncoding, group::UncompressedEncoding}; +use ethereum_consensus_types::signing::{compute_domain, DomainType}; +use ethereum_consensus_types::{ForkData, LightClientBootstrap, LightClientFinalityUpdate}; use itertools::Itertools; use lightclient_circuits::witness::SyncStepArgs; -use ssz_rs::Merkleized; -use sync_committee_primitives::{ - consensus_types::BeaconBlockHeader, domains::DomainType, types::LightClientState, - util::compute_domain, -}; -use sync_committee_prover::SyncCommitteeProver; +use ssz_rs::Vector; +use ssz_rs::{Merkleized, Node}; use tokio::fs; - -pub async fn fetch_step_args(node_url: String) -> eyre::Result> { - let client = SyncCommitteeProver::new(node_url); - let state_id = "head"; - let state = client - .fetch_beacon_state(state_id) +use zipline_cryptography::bls::BlsPublicKey; + +use crate::{get_light_client_bootstrap, get_light_client_finality_update}; + +pub async fn fetch_step_args( + client: &Client, +) -> eyre::Result> +where + [(); S::SYNC_COMMITTEE_SIZE]:, + [(); S::FINALIZED_HEADER_DEPTH]:, + [(); S::SYNC_COMMITTEE_DEPTH]:, + [(); S::BYTES_PER_LOGS_BLOOM]:, + [(); S::MAX_EXTRA_DATA_BYTES]:, +{ + let finality_update = get_light_client_finality_update(client).await?; + let block_root = client + .get_beacon_block_root(BlockId::Slot(finality_update.finalized_header.beacon.slot)) .await - .map_err(|e| eyre::eyre!("Error fetching state from node. Error: {}", e))?; - - let mut finalized_block = client.fetch_block("finalized").await.unwrap(); - - let finalized_header = BeaconBlockHeader { - slot: finalized_block.slot, - proposer_index: finalized_block.proposer_index, - parent_root: finalized_block.parent_root, - state_root: finalized_block.state_root, - body_root: finalized_block.body.hash_tree_root().unwrap(), - }; - - let client_state = LightClientState { - finalized_header: finalized_header.clone(), - latest_finalized_epoch: 0, - current_sync_committee: state.current_sync_committee.clone(), - next_sync_committee: state.next_sync_committee.clone(), + .unwrap(); + let bootstrap: LightClientBootstrap< + { S::SYNC_COMMITTEE_SIZE }, + { S::SYNC_COMMITTEE_DEPTH }, + { S::BYTES_PER_LOGS_BLOOM }, + { S::MAX_EXTRA_DATA_BYTES }, + > = get_light_client_bootstrap(client, block_root).await?; + + let pubkeys_compressed = bootstrap.current_sync_committee.pubkeys; + + let attested_state_id = finality_update.attested_header.beacon.state_root; + + let fork_version = client + .get_fork(StateId::Root(attested_state_id)) + .await? + .current_version; + let genesis_validators_root = client.get_genesis_details().await?.genesis_validators_root; + let fork_data = ForkData { + genesis_validators_root, + fork_version, }; + let domain = compute_domain(DomainType::SyncCommittee, &fork_data)?; - let mut light_client_update = client - .fetch_light_client_update( - client_state.clone(), - state.finalized_checkpoint.clone(), - "prover", - ) - .await - .map_err(|e| eyre::eyre!("Error fetching light client update. Error: {}", e))? - .expect("Light client update should be present"); - - let mut state = client - .fetch_beacon_state(&light_client_update.attested_header.slot.to_string()) - .await - .map_err(|e| eyre::eyre!("Error fetching state from node. Error: {}", e))?; + step_args_from_finality_update(finality_update, pubkeys_compressed, domain).await +} - let pubkeys_uncompressed = client_state - .current_sync_committee - .public_keys +pub async fn step_args_from_finality_update( + finality_update: LightClientFinalityUpdate< + { S::SYNC_COMMITTEE_SIZE }, + { S::FINALIZED_HEADER_DEPTH }, + { S::BYTES_PER_LOGS_BLOOM }, + { S::MAX_EXTRA_DATA_BYTES }, + >, + pubkeys_compressed: Vector, + domain: [u8; 32], +) -> eyre::Result> { + let pubkeys_uncompressed = pubkeys_compressed .iter() - .take(S::SYNC_COMMITTEE_SIZE) .map(|pk| { - let pk_rev = pk.iter().copied().rev().collect_vec(); - G1Affine::from_bytes_unchecked(&pk_rev.try_into().unwrap()) - .unwrap() - .to_uncompressed() - .as_ref() - .to_vec() + let p = pk.decompressed_bytes(); + let mut x = p[0..48].to_vec(); + let mut y = p[48..96].to_vec(); + x.reverse(); + y.reverse(); + let mut res = vec![]; + res.append(&mut x); + res.append(&mut y); + res }) .collect_vec(); - let domain = compute_domain( - DomainType::SyncCommittee, - Some(state.fork.current_version), - Some(state.genesis_validators_root), - [0u8; 4], - ) - .map_err(|e| eyre::eyre!("domain computation error: {:?}", e))?; - - let state_root = state - .hash_tree_root() - .map_err(|e| eyre::eyre!("merkleization error: {:?}", e))?; - - let mut finality_branch = - ssz_rs::generate_proof(&mut state, &[S::FINALIZED_HEADER_INDEX]).unwrap(); - - // FIXME: `ssz_rs::generate_proof` generates branch without `finalized_checkpoint.epoch` leaf. Why? - finality_branch.insert( - 0, - state.finalized_checkpoint.epoch.hash_tree_root().unwrap(), - ); - - let finalized_header_root = light_client_update + let execution_payload_root = finality_update .finalized_header - .hash_tree_root() - .unwrap(); + .execution + .clone() + .hash_tree_root()? + .to_vec(); + let execution_payload_branch = finality_update + .finalized_header + .execution_branch + .iter() + .map(|n| n.0.to_vec()) + .collect_vec(); assert!( - ssz_rs::verify_merkle_proof( - &finalized_header_root, - &finality_branch, - &ssz_rs::GeneralizedIndex(S::FINALIZED_HEADER_INDEX), - &state_root, - ), + ssz_rs::is_valid_merkle_branch( + Node::try_from(execution_payload_root.as_slice())?, + &execution_payload_branch, + S::EXECUTION_STATE_ROOT_DEPTH, + S::EXECUTION_STATE_ROOT_INDEX, + finality_update.finalized_header.beacon.body_root, + ) + .is_ok(), "Execution payload merkle proof verification failed" ); - - // TODO: why execution payload proof from `light_client_update` is invalid for S::EXECUTION_STATE_ROOT_INDEX? - // let execution_payload_branch = light_client_update - // .execution_payload - // .execution_payload_branch - // .iter() - // .take(4) - // .map(|n| n.as_bytes().to_vec()) - // .collect_vec(); - - let execution_payload_branch = - ssz_rs::generate_proof(&mut finalized_block.body, &[S::EXECUTION_STATE_ROOT_INDEX]) - .unwrap(); - - let execution_payload_root = finalized_block - .body - .execution_payload - .hash_tree_root() - .unwrap(); - assert!( - ssz_rs::verify_merkle_proof( - &execution_payload_root, - &execution_payload_branch, - &ssz_rs::GeneralizedIndex(S::EXECUTION_STATE_ROOT_INDEX), - &finalized_header.body_root, - ), - "Execution payload merkle proof verification failed" + ssz_rs::is_valid_merkle_branch( + finality_update + .finalized_header + .beacon + .clone() + .hash_tree_root() + .unwrap(), + &finality_update + .finality_branch + .iter() + .map(|n| n.as_ref()) + .collect_vec(), + S::FINALIZED_HEADER_DEPTH, + S::FINALIZED_HEADER_INDEX, + finality_update.attested_header.beacon.state_root, + ) + .is_ok(), + "Finality merkle proof verification failed" ); - let mut signature_compressed = light_client_update - .sync_aggregate - .sync_committee_signature - .to_vec(); - - // reverse beacouse it's big endian - signature_compressed.reverse(); - - let args = SyncStepArgs:: { - signature_compressed, + Ok(SyncStepArgs { + signature_compressed: finality_update + .sync_aggregate + .sync_committee_signature + .to_bytes() + .iter() + .copied() + .rev() + .collect_vec(), pubkeys_uncompressed, - pariticipation_bits: light_client_update + pariticipation_bits: finality_update .sync_aggregate .sync_committee_bits .iter() .by_vals() - .take(S::SYNC_COMMITTEE_SIZE) .collect_vec(), - attested_header: light_client_update.attested_header, - finalized_header: light_client_update.finalized_header, - domain, - execution_payload_branch: execution_payload_branch + attested_header: finality_update.attested_header.beacon, + finalized_header: finality_update.finalized_header.beacon, + finality_branch: finality_update + .finality_branch .iter() - .map(|n| n.as_bytes().to_vec()) + .map(|n| n.0.to_vec()) .collect_vec(), - execution_payload_root: execution_payload_root.0.to_vec(), - finality_branch: finality_branch + execution_payload_root: finality_update + .finalized_header + .execution + .clone() + .hash_tree_root() + .unwrap() + .to_vec(), + execution_payload_branch: finality_update + .finalized_header + .execution_branch .iter() - .map(|n| n.as_bytes().to_vec()) + .map(|n| n.0.to_vec()) .collect_vec(), + domain, _spec: PhantomData, - }; - - Ok(args) + }) } pub async fn read_step_args(path: String) -> eyre::Result> { @@ -190,15 +187,16 @@ mod tests { use snark_verifier_sdk::CircuitExt; use super::*; + use beacon_api_client::mainnet::Client as MainnetClient; + use reqwest::Url; #[tokio::test] async fn test_sync_circuit_sepolia() { const CONFIG_PATH: &str = "../lightclient-circuits/config/sync_step.json"; const K: u32 = 21; + let client = MainnetClient::new(Url::parse("http://65.109.55.120:9596").unwrap()); - let witness = fetch_step_args::("http://3.128.78.74:5052".to_string()) - .await - .unwrap(); + let witness = fetch_step_args::(&client).await.unwrap(); let pinning = Eth2ConfigPinning::from_path(CONFIG_PATH); let circuit = SyncStepCircuit::::create_circuit( @@ -226,10 +224,8 @@ mod tests { false, &SyncStepArgs::::default(), ); - - let witness = fetch_step_args::("http://3.128.78.74:5052".to_string()) - .await - .unwrap(); + let client = MainnetClient::new(Url::parse("http://65.109.55.120:9596").unwrap()); + let witness = fetch_step_args::(&client).await.unwrap(); SyncStepCircuit::::gen_snark_shplonk( ¶ms, diff --git a/prover/Cargo.toml b/prover/Cargo.toml index 97698b5f..834125a3 100644 --- a/prover/Cargo.toml +++ b/prover/Cargo.toml @@ -3,9 +3,13 @@ name = "spectre-prover" version = "0.1.0" edition = "2021" +[[bin]] +name = "spectre-prover-cli" +path = "src/main.rs" + [dependencies] # cli -clap = { version = "=4.2", features = ["derive"] } +clap = { version = "4.2", features = ["derive"] } strum = { version = "=0.25", features = ["derive"] } cli-batteries = "=0.4" eyre = "0.6" @@ -42,6 +46,9 @@ ethers = { version = "2.0.7", default_features = false, features = [ ] } primitive-types = "0.12.2" reqwest = "0.11.22" +beacon-api-client.workspace = true +ethereum-consensus-types.workspace = true +futures = "0.3.29" [build-dependencies] cli-batteries = "=0.4" diff --git a/prover/contractyul b/prover/contractyul new file mode 100644 index 00000000..e69de29b diff --git a/prover/src/cli.rs b/prover/src/cli.rs index d162a7f9..f7fc79ff 100644 --- a/prover/src/cli.rs +++ b/prover/src/cli.rs @@ -1,5 +1,7 @@ use crate::args::{Args, Out, Proof}; +use beacon_api_client::Client; +use beacon_api_client::ClientTypes; use ethers::abi::Address; use ethers::providers::{Http, Provider}; use halo2curves::bn256::{Bn256, Fr, G1Affine}; @@ -9,6 +11,7 @@ use lightclient_circuits::{ sync_step_circuit::SyncStepCircuit, util::{gen_srs, AppCircuit}, }; +use preprocessor::{fetch_rotation_args, fetch_step_args}; use primitive_types::U256; use snark_verifier::{ loader::halo2::halo2_ecc::halo2_base::halo2_proofs::{ @@ -25,35 +28,66 @@ use std::{ path::Path, sync::Arc, }; - -use preprocessor::{fetch_rotation_args, fetch_step_args}; +use url::Url; ethers::contract::abigen!( SnarkVerifierSol, r#"[ function verify(uint256[1] calldata pubInputs,bytes calldata proof) public view returns (bool) ]"#, ); -pub(crate) async fn spec_app(proof: &Proof) -> eyre::Result<()> { + +pub trait FetchFn: FnOnce(Arg) -> >::Fut { + type Fut: Future>::Output>; + type Output; +} + +impl FetchFn for F +where + F: FnOnce(Arg) -> Fut, + Fut: Future, +{ + type Fut = Fut; + type Output = Fut::Output; +} + +pub(crate) async fn spec_app(proof: &Proof) -> eyre::Result<()> +where + [(); S::SYNC_COMMITTEE_SIZE]:, + [(); S::FINALIZED_HEADER_DEPTH]:, + [(); S::BYTES_PER_LOGS_BLOOM]:, + [(); S::MAX_EXTRA_DATA_BYTES]:, + [(); S::SYNC_COMMITTEE_ROOT_INDEX]:, + [(); S::SYNC_COMMITTEE_DEPTH]:, + [(); S::FINALIZED_HEADER_INDEX]:, +{ match proof { Proof::CommitteeUpdate(args) => { - generic_circuit_cli::, _, _>( + let client: Client = Client::new(Url::parse(&args.beacon_api_url).unwrap()); + + generic_circuit_cli::, C, _>( args, + client, fetch_rotation_args, "committee_update", as AppCircuit>::Witness::default(), ) - .await + .await?; } Proof::SyncStep(args) => { - generic_circuit_cli::, _, _>( + let client: Client = Client::new(Url::parse(&args.beacon_api_url).unwrap()); + + generic_circuit_cli::, C, _>( args, + client, fetch_step_args, - "sync_step", + "step_circuit_testnet", as AppCircuit>::Witness::default(), ) - .await + .await?; } Proof::Aggregation(args) => { + let client: Client = Client::new(Url::parse("").unwrap()); + let params = gen_srs(CommitteeUpdateCircuit::::get_degree( &args.app_config_path, )); @@ -72,34 +106,37 @@ pub(crate) async fn spec_app(proof: &Proof) -> eyre::Result< .as_ref() .expect("path to SNARK is required"), )?; - - generic_circuit_cli::( + let snark_clone = snark.clone(); + let get_args = move |_client: &Client| async { Ok(vec![snark_clone]) }; + generic_circuit_cli::( &args.aggregation, - |_| async { Ok(vec![snark.clone()]) }, + client, + get_args, "aggregation", - vec![snark.clone()], + vec![snark], ) - .await + .await?; } } + Ok(()) } -pub(crate) async fn generic_circuit_cli< - Circuit: AppCircuit, - FnFetch: FnOnce(String) -> Fut, - Fut: Future>, ->( +pub(crate) async fn generic_circuit_cli( args: &Args, + client: Client, fetch: FnFetch, name: &str, default_witness: Circuit::Witness, -) -> eyre::Result<()> { +) -> eyre::Result<()> +where + for<'a> FnFetch: FetchFn<&'a Client, Output = eyre::Result>, +{ let k = args .k .unwrap_or_else(|| Circuit::get_degree(&args.config_path)); let params = gen_srs(k); let pk_filename = format!("{}.pkey", name); - + // let client = Client::new(Url::parse(&args.beacon_api_url).unwrap()); match args.out { Out::Snark => { let pk = Circuit::read_or_create_pk( @@ -109,7 +146,7 @@ pub(crate) async fn generic_circuit_cli< true, &default_witness, ); - let witness = fetch(args.beacon_api_url.clone()).await?; + let witness = fetch(&client).await?; Circuit::gen_snark_shplonk( ¶ms, &pk, @@ -159,7 +196,8 @@ pub(crate) async fn generic_circuit_cli< true, &default_witness, ); - let witness = fetch(args.beacon_api_url.clone()).await?; + + let witness = fetch(&client).await?; let deplyment_code = Circuit::gen_evm_verifier_shplonk(¶ms, &pk, None::<&Path>, &default_witness) @@ -187,7 +225,7 @@ pub(crate) async fn generic_circuit_cli< true, &default_witness, ); - let witness = fetch(args.beacon_api_url.clone()).await?; + let witness = fetch(&client).await?; let (proof, instances) = Circuit::gen_evm_proof_shplonk(¶ms, &pk, &args.config_path, None, &witness) diff --git a/prover/src/lib.rs b/prover/src/lib.rs new file mode 100644 index 00000000..0628a7b9 --- /dev/null +++ b/prover/src/lib.rs @@ -0,0 +1,5 @@ +#![feature(generic_const_exprs)] +pub mod rpc_api; +pub mod rpc_client; + +pub mod args; diff --git a/prover/src/main.rs b/prover/src/main.rs index e049e2b9..94d6c454 100644 --- a/prover/src/main.rs +++ b/prover/src/main.rs @@ -1,5 +1,6 @@ #![allow(incomplete_features)] #![feature(associated_type_bounds)] +#![feature(generic_const_exprs)] mod cli; mod rpc; mod rpc_api; @@ -7,6 +8,7 @@ pub mod rpc_client; use args::Cli; use axum::{response::IntoResponse, routing::post, Router}; +use beacon_api_client::{mainnet::MainnetClientTypes, minimal::MinimalClientTypes}; use cli_batteries::version; use http::StatusCode; @@ -41,9 +43,15 @@ async fn app(options: Cli) -> eyre::Result<()> { log::info!("Stopped accepting RPC connections"); } args::Subcommands::Circuit(op) => match op.spec { - args::Spec::Minimal => spec_app::(&op.proof).await.unwrap(), - args::Spec::Testnet => spec_app::(&op.proof).await.unwrap(), - args::Spec::Mainnet => spec_app::(&op.proof).await.unwrap(), + args::Spec::Minimal => spec_app::(&op.proof) + .await + .unwrap(), + args::Spec::Testnet => spec_app::(&op.proof) + .await + .unwrap(), + args::Spec::Mainnet => spec_app::(&op.proof) + .await + .unwrap(), }, } Ok(()) diff --git a/prover/src/rpc.rs b/prover/src/rpc.rs index c1107b1d..6a5b5fa0 100644 --- a/prover/src/rpc.rs +++ b/prover/src/rpc.rs @@ -3,6 +3,7 @@ use super::args::Spec; use ethers::prelude::*; use halo2curves::bn256::Fr; +use itertools::Itertools; use jsonrpc_v2::{Error as JsonRpcError, Params}; use lightclient_circuits::{ aggregation::AggregationConfigPinning, @@ -10,20 +11,27 @@ use lightclient_circuits::{ sync_step_circuit::SyncStepCircuit, util::{gen_srs, AppCircuit, Halo2ConfigPinning}, }; -use preprocessor::{fetch_rotation_args, fetch_step_args}; +use preprocessor::{ + fetch_rotation_args, fetch_step_args, rotation_args_from_update, step_args_from_finality_update, +}; use jsonrpc_v2::{MapRouter as JsonRpcMapRouter, Server as JsonRpcServer}; use snark_verifier::loader::halo2::halo2_ecc::halo2_base::gates::builder::CircuitBuilderStage; use snark_verifier_sdk::{ + evm::evm_verify, gen_pk, halo2::{aggregation::AggregationCircuit, gen_snark_shplonk}, CircuitExt, Snark, SHPLONK, }; use std::path::PathBuf; +use url::Url; use crate::rpc_api::{ - EvmProofResult, GenProofRotationParams, GenProofStepParams, EVM_PROOF_ROTATION_CIRCUIT, - EVM_PROOF_STEP_CIRCUIT, + EvmProofResult, GenProofRotationParams, GenProofRotationWithWitnessParams, GenProofStepParams, + GenProofStepWithWitnessParams, SyncCommitteePoseidonParams, SyncCommitteePoseidonResult, + EVM_PROOF_ROTATION_CIRCUIT, EVM_PROOF_ROTATION_CIRCUIT_WITH_WITNESS, EVM_PROOF_STEP_CIRCUIT, + EVM_PROOF_STEP_CIRCUIT_WITH_WITNESS, SYNC_COMMITTEE_POSEIDON_COMPRESSED, + SYNC_COMMITTEE_POSEIDON_UNCOMPRESSED, }; fn gen_app_snark( @@ -67,6 +75,12 @@ fn gen_evm_proof( .map_err(|e| eyre::eyre!("Failed to generate calldata: {}", e)) .unwrap(); + println!("Proof size: {}", proof.len()); + let deployment_code = + C::gen_evm_verifier_shplonk(¶ms, &pk, Some("contractyul"), &witness).unwrap(); + println!("deployment_code size: {}", deployment_code.len()); + evm_verify(deployment_code, instances.clone(), proof.clone()); + println!("Gen evm proof done"); (proof, instances) } @@ -92,18 +106,24 @@ pub(crate) async fn gen_evm_proof_rotation_circuit_handler( let l0_snark = match spec { Spec::Minimal => { - let witness = fetch_rotation_args(beacon_api).await?; let app_pk_path = PathBuf::from("./build/committee_update_circuit_minimal.pkey"); + let client = beacon_api_client::minimal::Client::new(Url::parse(&beacon_api)?); + let witness: lightclient_circuits::witness::CommitteeRotationArgs< + eth_types::Minimal, + Fr, + > = fetch_rotation_args(&client).await?; gen_app_snark::(app_config_path, app_pk_path, witness)? } Spec::Testnet => { - let witness = fetch_rotation_args(beacon_api).await?; let app_pk_path = PathBuf::from("./build/committee_update_circuit_testnet.pkey"); + let client = beacon_api_client::mainnet::Client::new(Url::parse(&beacon_api)?); + let witness = fetch_rotation_args(&client).await?; gen_app_snark::(app_config_path, app_pk_path, witness)? } Spec::Mainnet => { - let witness = fetch_rotation_args(beacon_api).await?; let app_pk_path = PathBuf::from("./build/committee_update_circuit_mainnet.pkey"); + let client = beacon_api_client::mainnet::Client::new(Url::parse(&beacon_api)?); + let witness = fetch_rotation_args(&client).await?; gen_app_snark::(app_config_path, app_pk_path, witness)? } }; @@ -170,6 +190,123 @@ pub(crate) async fn gen_evm_proof_rotation_circuit_handler( }) } +pub(crate) async fn gen_evm_proof_rotation_circuit_with_witness_handler( + Params(params): Params, +) -> Result { + let GenProofRotationWithWitnessParams { + spec, + k, + light_client_update, + } = params; + + // TODO: use config/build paths from CLI flags + let app_config_path = PathBuf::from("../lightclient-circuits/config/committee_update.json"); + let agg_l2_pk_path = PathBuf::from("./build/step_agg_l2.pkey"); + let agg_l1_pk_path = PathBuf::from("./build/step_agg_l1.pkey"); + + let agg_l2_config_path = + PathBuf::from("../lightclient-circuits/config/committee_update_aggregation_2.json"); + let agg_l1_config_path = + PathBuf::from("../lightclient-circuits/config/committee_update_aggregation_1.json"); + let _build_dir = PathBuf::from("./build"); + + let (l0_snark, _pk_filename) = match spec { + Spec::Minimal => { + let mut update = serde_json::from_slice(&light_client_update).unwrap(); + let app_pk_path = PathBuf::from("./build/committee_update_circuit_minimal.pkey"); + + let witness = rotation_args_from_update(&mut update).await.unwrap(); + ( + gen_app_snark::(app_config_path, app_pk_path, witness)?, + "agg_rotation_circuit_minimal.pkey", + ) + } + Spec::Testnet => { + let mut update = serde_json::from_slice(&light_client_update).unwrap(); + let app_pk_path = PathBuf::from("./build/committee_update_circuit_testnet.pkey"); + + let witness = rotation_args_from_update(&mut update).await.unwrap(); + + ( + gen_app_snark::(app_config_path, app_pk_path, witness)?, + "agg_rotation_circuit_testnet.pkey", + ) + } + Spec::Mainnet => { + let mut update = serde_json::from_slice(&light_client_update).unwrap(); + let app_pk_path = PathBuf::from("./build/committee_update_circuit_mainnet.pkey"); + + let witness = rotation_args_from_update(&mut update).await.unwrap(); + + ( + gen_app_snark::(app_config_path, app_pk_path, witness)?, + "agg_rotation_circuit_mainnet.pkey", + ) + } + }; + + let l1_snark = { + let k = k.unwrap_or(24); + let p1 = gen_srs(k); + let mut circuit = AggregationCircuit::keygen::(&p1, vec![l0_snark.clone()]); + circuit.expose_previous_instances(false); + + println!("L1 Keygen num_instances: {:?}", circuit.num_instance()); + + let pk_l1 = gen_pk(&p1, &circuit, Some(&agg_l1_pk_path)); + let pinning = AggregationConfigPinning::from_path(agg_l1_config_path); + let lookup_bits = k as usize - 1; + let mut circuit = AggregationCircuit::new::( + CircuitBuilderStage::Prover, + Some(pinning.break_points), + lookup_bits, + &p1, + std::iter::once(l0_snark), + ); + circuit.expose_previous_instances(false); + + println!("L1 Prover num_instances: {:?}", circuit.num_instance()); + let snark = gen_snark_shplonk(&p1, &pk_l1, circuit, None::); + println!("L1 snark size: {}", snark.proof.len()); + + snark + }; + + let (proof, instances) = { + let k = k.unwrap_or(24); + let p2 = gen_srs(k); + let mut circuit = + AggregationCircuit::keygen::(&p2, std::iter::once(l1_snark.clone())); + circuit.expose_previous_instances(true); + + let pk_l2 = gen_pk(&p2, &circuit, Some(&agg_l2_pk_path)); + let pinning = AggregationConfigPinning::from_path(agg_l2_config_path); + + let mut circuit = AggregationCircuit::prover::( + &p2, + std::iter::once(l1_snark), + pinning.break_points, + ); + circuit.expose_previous_instances(true); + + let instances = circuit.instances(); + + let proof = + snark_verifier_sdk::evm::gen_evm_proof_shplonk(&p2, &pk_l2, circuit, instances.clone()); + + (proof, instances) + }; + + let public_inputs = instances[0] + .iter() + .map(|pi| U256::from_little_endian(&pi.to_bytes())) + .collect(); + Ok(EvmProofResult { + proof, + public_inputs, + }) +} + pub(crate) async fn gen_evm_proof_step_circuit_handler( Params(params): Params, ) -> Result { @@ -185,7 +322,81 @@ pub(crate) async fn gen_evm_proof_step_circuit_handler( let (proof, instances) = match spec { Spec::Minimal => { let pk_filename = format!("step_circuit_minimal.pkey"); - let witness = fetch_step_args(beacon_api).await.unwrap(); + let client = beacon_api_client::minimal::Client::new(Url::parse(&beacon_api)?); + + let witness = fetch_step_args(&client).await.unwrap(); + gen_evm_proof::>( + k, + build_dir, + pk_filename, + config_path, + witness, + ) + } + Spec::Testnet => { + let pk_filename = format!("step_circuit_testnet.pkey"); + let client = beacon_api_client::mainnet::Client::new(Url::parse(&beacon_api)?); + + let witness = fetch_step_args(&client).await.unwrap(); + + gen_evm_proof::>( + k, + build_dir, + pk_filename, + config_path, + witness, + ) + } + Spec::Mainnet => { + let pk_filename = format!("step_circuit_mainnet.pkey"); + let client = beacon_api_client::mainnet::Client::new(Url::parse(&beacon_api)?); + + let witness = fetch_step_args(&client).await.unwrap(); + + gen_evm_proof::>( + k, + build_dir, + pk_filename, + config_path, + witness, + ) + } + }; + + let public_inputs = instances[0] + .iter() + .map(|pi| U256::from_little_endian(&pi.to_bytes())) + .collect(); + Ok(EvmProofResult { + proof, + public_inputs, + }) +} + +pub(crate) async fn gen_evm_proof_step_circuit_with_witness_handler( + Params(params): Params, +) -> Result { + let GenProofStepWithWitnessParams { + spec, + k, + light_client_finality_update, + domain, + pubkeys, + } = params; + + let config_path = PathBuf::from("../lightclient-circuits/config/sync_step.json"); + let build_dir = PathBuf::from("./build"); + + let (proof, instances) = match spec { + Spec::Minimal => { + let pk_filename = format!("step_circuit_minimal.pkey"); + + let update = serde_json::from_slice(&light_client_finality_update).unwrap(); + let pubkeys = serde_json::from_slice(&pubkeys).unwrap(); + + let witness = step_args_from_finality_update(update, pubkeys, domain) + .await + .unwrap(); gen_evm_proof::>( k, build_dir, @@ -196,8 +407,12 @@ pub(crate) async fn gen_evm_proof_step_circuit_handler( } Spec::Testnet => { let pk_filename = format!("step_circuit_testnet.pkey"); - let witness = fetch_step_args(beacon_api).await.unwrap(); + let update = serde_json::from_slice(&light_client_finality_update).unwrap(); + let pubkeys = serde_json::from_slice(&pubkeys).unwrap(); + let witness = step_args_from_finality_update(update, pubkeys, domain) + .await + .unwrap(); gen_evm_proof::>( k, build_dir, @@ -208,8 +423,12 @@ pub(crate) async fn gen_evm_proof_step_circuit_handler( } Spec::Mainnet => { let pk_filename = format!("step_circuit_mainnet.pkey"); - let witness = fetch_step_args(beacon_api).await.unwrap(); + let update = serde_json::from_slice(&light_client_finality_update).unwrap(); + let pubkeys = serde_json::from_slice(&pubkeys).unwrap(); + let witness = step_args_from_finality_update(update, pubkeys, domain) + .await + .unwrap(); gen_evm_proof::>( k, build_dir, @@ -230,6 +449,46 @@ pub(crate) async fn gen_evm_proof_step_circuit_handler( }) } +pub(crate) async fn sync_committee_poseidon_compressed_handler( + Params(params): Params, +) -> Result { + let SyncCommitteePoseidonParams { pubkeys } = params; + + let pubkeys = pubkeys + .into_iter() + .map(|mut b| { + b.reverse(); + b + }) + .collect_vec(); + + let commitment = lightclient_circuits::poseidon::poseidon_committee_commitment_from_compressed( + pubkeys.as_slice(), + )?; + + Ok(SyncCommitteePoseidonResult { commitment }) +} +pub(crate) async fn sync_committee_poseidon_uncompressed_handler( + Params(params): Params, +) -> Result { + let SyncCommitteePoseidonParams { pubkeys } = params; + + let pubkeys = pubkeys + .into_iter() + .map(|mut b| { + b.reverse(); + b + }) + .collect_vec(); + + let commitment = + lightclient_circuits::poseidon::poseidon_committee_commitment_from_uncompressed( + pubkeys.as_slice(), + )?; + + Ok(SyncCommitteePoseidonResult { commitment }) +} + pub(crate) fn jsonrpc_server() -> JsonRpcServer { JsonRpcServer::new() .with_method(EVM_PROOF_STEP_CIRCUIT, gen_evm_proof_step_circuit_handler) @@ -237,5 +496,21 @@ pub(crate) fn jsonrpc_server() -> JsonRpcServer { EVM_PROOF_ROTATION_CIRCUIT, gen_evm_proof_rotation_circuit_handler, ) + .with_method( + EVM_PROOF_ROTATION_CIRCUIT_WITH_WITNESS, + gen_evm_proof_rotation_circuit_with_witness_handler, + ) + .with_method( + EVM_PROOF_STEP_CIRCUIT_WITH_WITNESS, + gen_evm_proof_step_circuit_with_witness_handler, + ) + .with_method( + SYNC_COMMITTEE_POSEIDON_UNCOMPRESSED, + sync_committee_poseidon_uncompressed_handler, + ) + .with_method( + SYNC_COMMITTEE_POSEIDON_COMPRESSED, + sync_committee_poseidon_compressed_handler, + ) .finish_unwrapped() } diff --git a/prover/src/rpc_api.rs b/prover/src/rpc_api.rs index 7526af57..be02958a 100644 --- a/prover/src/rpc_api.rs +++ b/prover/src/rpc_api.rs @@ -20,15 +20,56 @@ pub struct GenProofStepParams { pub beacon_api: String, } +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct GenProofStepWithWitnessParams { + pub spec: args::Spec, + + pub k: Option, + + // Serializing as Vec so that we can differentiate between Mainnet, Testnet, Minimal at runtime + pub light_client_finality_update: Vec, + pub pubkeys: Vec, + + pub domain: [u8; 32], +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct GenProofRotationWithWitnessParams { + pub spec: args::Spec, + + pub k: Option, + + // Serializing as Vec so that we can differentiate between Mainnet, Testnet, Minimal at runtime + pub light_client_update: Vec, +} + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct EvmProofResult { pub proof: Vec, pub public_inputs: Vec, } +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct SyncCommitteePoseidonParams { + pub pubkeys: Vec>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct SyncCommitteePoseidonResult { + pub commitment: [u8; 32], +} + fn default_beacon_api() -> String { String::from("http://127.0.0.1:5052") } pub const EVM_PROOF_STEP_CIRCUIT: &str = "genEvmProofAndInstancesStepSyncCircuit"; pub const EVM_PROOF_ROTATION_CIRCUIT: &str = "genEvmProofAndInstancesRotationCircuit"; + +pub const EVM_PROOF_STEP_CIRCUIT_WITH_WITNESS: &str = + "genEvmProofAndInstancesStepSyncCircuitWithWitness"; +pub const EVM_PROOF_ROTATION_CIRCUIT_WITH_WITNESS: &str = + "genEvmProofAndInstancesRotationCircuitWithWitness"; + +pub const SYNC_COMMITTEE_POSEIDON_COMPRESSED: &str = "syncCommitteePoseidonCompressed"; +pub const SYNC_COMMITTEE_POSEIDON_UNCOMPRESSED: &str = "syncCommitteePoseidonUncompressed"; diff --git a/prover/src/rpc_client.rs b/prover/src/rpc_client.rs index 97c6b881..4b85c4ba 100644 --- a/prover/src/rpc_client.rs +++ b/prover/src/rpc_client.rs @@ -1,11 +1,15 @@ +pub use jsonrpc_v2; use jsonrpc_v2::{Error, Id, RequestObject, V2}; use reqwest::IntoUrl; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use url::Url; use crate::rpc_api::{ - EvmProofResult, GenProofRotationParams, GenProofStepParams, EVM_PROOF_ROTATION_CIRCUIT, - EVM_PROOF_STEP_CIRCUIT, + EvmProofResult, GenProofRotationParams, GenProofRotationWithWitnessParams, GenProofStepParams, + GenProofStepWithWitnessParams, SyncCommitteePoseidonParams, SyncCommitteePoseidonResult, + EVM_PROOF_ROTATION_CIRCUIT, EVM_PROOF_ROTATION_CIRCUIT_WITH_WITNESS, EVM_PROOF_STEP_CIRCUIT, + EVM_PROOF_STEP_CIRCUIT_WITH_WITNESS, SYNC_COMMITTEE_POSEIDON_COMPRESSED, + SYNC_COMMITTEE_POSEIDON_UNCOMPRESSED, }; /// Error object in a response @@ -59,6 +63,38 @@ impl Client { self.call(EVM_PROOF_STEP_CIRCUIT, params).await } + /// Generates a proof along with instance values for committee Rotation circuit + pub async fn gen_evm_proof_rotation_circuit_with_witness( + &self, + params: GenProofRotationWithWitnessParams, + ) -> Result { + self.call(EVM_PROOF_ROTATION_CIRCUIT_WITH_WITNESS, params) + .await + } + + /// Generates a proof along with instance values for Step circuit + pub async fn gen_evm_proof_step_circuit_with_witness( + &self, + params: GenProofStepWithWitnessParams, + ) -> Result { + self.call(EVM_PROOF_STEP_CIRCUIT_WITH_WITNESS, params).await + } + + pub async fn sync_committee_poseidon_compressed( + &self, + params: SyncCommitteePoseidonParams, + ) -> Result { + self.call(SYNC_COMMITTEE_POSEIDON_COMPRESSED, params).await + } + + pub async fn sync_committee_poseidon_uncompressed( + &self, + params: SyncCommitteePoseidonParams, + ) -> Result { + self.call(SYNC_COMMITTEE_POSEIDON_UNCOMPRESSED, params) + .await + } + /// Utility method for sending RPC requests over HTTP async fn call(&self, method_name: &str, params: P) -> Result where diff --git a/test-utils/Cargo.toml b/test-utils/Cargo.toml index e78c28b2..96038aee 100644 --- a/test-utils/Cargo.toml +++ b/test-utils/Cargo.toml @@ -8,10 +8,9 @@ lightclient-circuits = { workspace = true } ethereum-consensus-types = { workspace = true } eth-types = { workspace = true } contracts = { workspace = true } -ssz-rs = { workspace = true } +ssz_rs = { workspace = true } zipline-test-utils = { workspace = true } light-client-verifier = { workspace = true } -sync-committee-primitives = { workspace = true } halo2curves = { workspace = true } halo2-base = { workspace = true } diff --git a/test-utils/src/conversions.rs b/test-utils/src/conversions.rs index d2fef8d1..2751a23e 100644 --- a/test-utils/src/conversions.rs +++ b/test-utils/src/conversions.rs @@ -3,6 +3,7 @@ use contracts::{RotateInput, SyncStepInput}; use itertools::Itertools; use lightclient_circuits::witness::{CommitteeRotationArgs, SyncStepArgs}; use ssz_rs::prelude::*; +use std::ops::Deref; pub fn sync_input_from_args(args: SyncStepArgs) -> SyncStepInput { let participation = args @@ -16,7 +17,7 @@ pub fn sync_input_from_args(args: SyncStepArgs) -> .clone() .hash_tree_root() .unwrap() - .as_bytes() + .deref() .try_into() .unwrap(); @@ -25,7 +26,7 @@ pub fn sync_input_from_args(args: SyncStepArgs) -> SyncStepInput { attested_slot: args.attested_header.slot, finalized_slot: args.finalized_header.slot, - participation: participation, + participation, finalized_header_root, execution_payload_root, } @@ -62,7 +63,7 @@ where let sync_committee_ssz = pk_vector .hash_tree_root() .unwrap() - .as_bytes() + .deref() .try_into() .unwrap(); diff --git a/test-utils/src/execution_payload_header.rs b/test-utils/src/execution_payload_header.rs index 619d88c1..48c42d9b 100644 --- a/test-utils/src/execution_payload_header.rs +++ b/test-utils/src/execution_payload_header.rs @@ -1,6 +1,9 @@ -use crate::test_types::{ByteList, ByteVector, ExecutionAddress}; +use crate::{ + test_types::{ByteList, ByteVector, ExecutionAddress}, + U256_BYTE_COUNT, +}; use ssz_rs::prelude::*; - +use std::ops::Deref; #[derive(Default, Debug, Clone, SimpleSerialize, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ExecutionPayloadHeader< @@ -39,31 +42,28 @@ impl >, ) -> Self { Self { - parent_hash: Node::from_bytes(header.parent_hash.as_ref().try_into().unwrap()), + parent_hash: header.parent_hash, fee_recipient: ByteVector( Vector::try_from(header.fee_recipient.0.as_ref().to_vec()).unwrap(), ), - state_root: Node::from_bytes(header.state_root.as_ref().try_into().unwrap()), - receipts_root: Node::from_bytes(header.receipts_root.as_ref().try_into().unwrap()), + state_root: header.state_root.deref().try_into().unwrap(), + receipts_root: header.receipts_root.deref().try_into().unwrap(), logs_bloom: ByteVector( Vector::try_from(header.logs_bloom.0.as_ref().to_vec()).unwrap(), ), - prev_randao: Node::from_bytes(header.prev_randao.as_ref().try_into().unwrap()), + prev_randao: header.prev_randao, block_number: header.block_number, gas_limit: header.gas_limit, gas_used: header.gas_used, timestamp: header.timestamp, extra_data: ByteList(List::try_from(header.extra_data.0.as_ref().to_vec()).unwrap()), - base_fee_per_gas: U256::from_bytes_le( - header.base_fee_per_gas.to_bytes_le().try_into().unwrap(), - ), - block_hash: Node::from_bytes(header.block_hash.as_ref().try_into().unwrap()), - transactions_root: Node::from_bytes( - header.transactions_root.as_ref().try_into().unwrap(), - ), - withdrawals_root: Node::from_bytes( - header.withdrawals_root.as_ref().try_into().unwrap(), + base_fee_per_gas: U256::from_le_bytes::( + header.base_fee_per_gas.as_le_slice().try_into().unwrap(), ), + block_hash: header.block_hash, + transactions_root: header.transactions_root, + + withdrawals_root: header.withdrawals_root, } } } diff --git a/test-utils/src/lib.rs b/test-utils/src/lib.rs index 339c3497..84337219 100644 --- a/test-utils/src/lib.rs +++ b/test-utils/src/lib.rs @@ -4,31 +4,34 @@ use eth_types::Minimal; use ethereum_consensus_types::presets::minimal::{LightClientBootstrap, LightClientUpdateCapella}; use ethereum_consensus_types::signing::{compute_domain, DomainType}; use ethereum_consensus_types::{ForkData, Root}; -use halo2_base::safe_types::ScalarField; -use halo2curves::bls12_381; -use halo2curves::bn256::{self, Fr}; -use halo2curves::group::UncompressedEncoding; + +use halo2curves::bn256::Fr; + use itertools::Itertools; use light_client_verifier::ZiplineUpdateWitnessCapella; use lightclient_circuits::gadget::crypto; -use lightclient_circuits::poseidon::fq_array_poseidon_native; +use lightclient_circuits::poseidon::{ + poseidon_committee_commitment_from_compressed, poseidon_committee_commitment_from_uncompressed, +}; use lightclient_circuits::witness::{CommitteeRotationArgs, SyncStepArgs}; use ssz_rs::prelude::*; use ssz_rs::Merkleized; -use std::path::PathBuf; -use sync_committee_primitives::consensus_types::BeaconBlockHeader; +use std::ops::Deref; +use std::path::Path; use zipline_test_utils::{load_snappy_ssz, load_yaml}; use crate::execution_payload_header::ExecutionPayloadHeader; use crate::test_types::{ByteVector, TestMeta, TestStep}; - +use ethereum_consensus_types::BeaconBlockHeader; +pub mod conversions; mod execution_payload_header; mod test_types; -pub mod conversions; + +pub(crate) const U256_BYTE_COUNT: usize = 32; // loads the boostrap on the path and return the initial sync committee poseidon and sync period pub fn get_initial_sync_committee_poseidon( - path: &PathBuf, + path: &Path, ) -> anyhow::Result<(usize, [u8; 32])> { let bootstrap: LightClientBootstrap = load_snappy_ssz(path.join("bootstrap.ssz_snappy").to_str().unwrap()).unwrap(); @@ -54,7 +57,7 @@ pub fn get_initial_sync_committee_poseidon Root { +pub fn validators_root_from_test_path(path: &Path) -> Root { let meta: TestMeta = load_yaml(path.join("meta.yaml").to_str().unwrap()); Root::try_from( hex::decode(meta.genesis_validators_root.trim_start_matches("0x")) @@ -67,15 +70,12 @@ pub fn validators_root_from_test_path(path: &PathBuf) -> Root { // Load the updates for a given test and only includes the first sequence of steps that Spectre can perform // e.g. the the steps are cut at the first `ForceUpdate` step pub fn valid_updates_from_test_path( - path: &PathBuf, + path: &Path, ) -> Vec> { let steps: Vec = load_yaml(path.join("steps.yaml").to_str().unwrap()); let updates = steps .iter() - .take_while(|step| match step { - TestStep::ProcessUpdate { .. } => true, - _ => false, - }) + .take_while(|step| matches!(step, TestStep::ProcessUpdate { .. })) .filter_map(|step| match step { TestStep::ProcessUpdate { update, .. } => { let update: LightClientUpdateCapella = load_snappy_ssz( @@ -93,7 +93,7 @@ pub fn valid_updates_from_test_path( } pub fn read_test_files_and_gen_witness( - path: &PathBuf, + path: &Path, ) -> (SyncStepArgs, CommitteeRotationArgs) { let bootstrap: LightClientBootstrap = load_snappy_ssz(path.join("bootstrap.ssz_snappy").to_str().unwrap()).unwrap(); @@ -111,7 +111,7 @@ pub fn read_test_files_and_gen_witness( .light_client_update .next_sync_committee_branch .iter() - .map(|n| n.as_ref().to_vec()) + .map(|n| n.deref().to_vec()) .collect_vec(); let agg_pubkeys_compressed = zipline_witness @@ -123,7 +123,7 @@ pub fn read_test_files_and_gen_witness( let mut agg_pk: ByteVector<48> = ByteVector(Vector::try_from(agg_pubkeys_compressed).unwrap()); - sync_committee_branch.insert(0, agg_pk.hash_tree_root().unwrap().as_ref().to_vec()); + sync_committee_branch.insert(0, agg_pk.hash_tree_root().unwrap().deref().to_vec()); let rotation_wit = CommitteeRotationArgs:: { pubkeys_compressed: zipline_witness @@ -142,35 +142,6 @@ pub fn read_test_files_and_gen_witness( (sync_wit, rotation_wit) } -pub fn poseidon_committee_commitment_from_uncompressed( - pubkeys_uncompressed: &Vec>, -) -> anyhow::Result<[u8; 32]> { - let pubkey_affines = pubkeys_uncompressed - .iter() - .cloned() - .map(|bytes| { - halo2curves::bls12_381::G1Affine::from_uncompressed_unchecked( - &bytes.as_slice().try_into().unwrap(), - ) - .unwrap() - }) - .collect_vec(); - let poseidon_commitment = - fq_array_poseidon_native::(pubkey_affines.iter().map(|p| p.x)).unwrap(); - Ok(poseidon_commitment.to_bytes_le().try_into().unwrap()) -} - -pub fn poseidon_committee_commitment_from_compressed( - pubkeys_compressed: &Vec>, -) -> anyhow::Result<[u8; 32]> { - let pubkeys_x = pubkeys_compressed.iter().cloned().map(|mut bytes| { - bytes[47] &= 0b00011111; - bls12_381::Fq::from_bytes_le(&bytes) - }); - let poseidon_commitment = fq_array_poseidon_native::(pubkeys_x).unwrap(); - Ok(poseidon_commitment.to_bytes_le().try_into().unwrap()) -} - fn to_sync_ciruit_witness< const SYNC_COMMITTEE_SIZE: usize, const NEXT_SYNC_COMMITTEE_GINDEX: usize, @@ -236,7 +207,7 @@ fn to_sync_ciruit_witness< .light_client_update .attested_header .beacon - .proposer_index as u64, + .proposer_index, parent_root: Node::try_from( zipline_witness .light_client_update @@ -275,7 +246,7 @@ fn to_sync_ciruit_witness< .light_client_update .finalized_header .beacon - .proposer_index as u64, + .proposer_index, parent_root: Node::try_from( zipline_witness .light_client_update @@ -331,14 +302,14 @@ fn to_sync_ciruit_witness< execution_payload_header .hash_tree_root() .unwrap() - .as_ref() + .deref() .to_vec() }; args.finality_branch = zipline_witness .light_client_update .finality_branch .iter() - .map(|b| b.as_ref().to_vec()) + .map(|b| b.deref().to_vec()) .collect(); args }