diff --git a/backend/.gitignore b/backend/.gitignore index b55c6369..5e48149c 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -2,3 +2,4 @@ # will have compiled files and executables /target .env +*_proof.json diff --git a/backend/README.md b/backend/README.md index 5168e2b4..3f04ef51 100644 --- a/backend/README.md +++ b/backend/README.md @@ -46,22 +46,22 @@ SIGNATURE_VERIFICATION_MESSAGE="Summa proof of solvency for CryptoExchange" carg ## Important Notices -### Generating Verifiers for Backend +### Generating and updating verifier contracts for Backend -The following steps are optional and are only required if you need to update the verifier contracts for the backend: +The verifier contracts in the backend were generated using a predefined set of parameters: `N_ASSETS = 2` and `N_BYTES=14`, as indicated [here](https://github.com/summa-dev/summa-solvency/blob/master/zk_prover/examples/gen_solvency_verifier.rs#L21-L22). +If you intend to work with different parameters, you'll need to adjust these hard-coded values and then generate new verifier contracts. + +The process described below assists in both generating the verifiers and updating the Summa contract, which integrates the new verifiers as constructors. -1. **Build the Verifier Contracts**: - - Move to the `zk_prover` directory. - - Run the [`gen_solvency_verifier`](https://github.com/summa-dev/summa-solvency/blob/master/zk_prover/examples/gen_solvency_verifier.rs) and [`gen_inclusion_verifier`](https://github.com/summa-dev/summa-solvency/blob/master/zk_prover/examples/gen_inclusion_verifier.rs) located within the `zk_prover/examples`. - - For detailed instructions [building a solvency verifier contract](https://github.com/summa-dev/summa-solvency/tree/master/zk_prover#build-a-solvency-verifier-contract) and [building an inclusion verifier contract.](https://github.com/summa-dev/summa-solvency/tree/master/zk_prover#build-an-inclusion-verifier-contract) -2. **Deploy Contracts to Local Environment**: - - Navigate to the `contracts` directory - - Deploy the contracts to a Hardhat environment. This step will refresh the ABI files(`src/contracts/abi/*.json`) in the backend. -3. **Generate Rust Interface Files**: - - Move to the `backend` directory. - - Execute the build script in the backend. This will produce the Rust interface files: `inclusion_verifier.rs`, `solvency_verifier.rs`, and `summa_contract.rs`. +#### Using the Bash Script -By completing these steps, the backend will be primed with the essential verifiers for its tasks. +We have provided a bash script to automate the process of updating the verifier contracts and the Summa contract. To use the script: + +Ensure you have the necessary permissions to execute the script. + +``` +backend $ chmod +x scripts/update_verifier_contracts.sh +``` ## Summa solvency flow example @@ -83,9 +83,6 @@ Key points: - The `dispatch_proof_of_address_ownership` function sends a transaction to the Summa contract to register CEX-owned addresses. -- After dispatching the transaction, the example computes the hashed addresses (address_hashes) to verify they've been correctly registered in the Summa contract - - Note: This demonstration takes place in a test environment. In real-world production, always ensure that the Summa contract is correctly deployed on the target chain. If executed successfully, you'll see: @@ -97,12 +94,16 @@ If executed successfully, you'll see: ### 2. Submit Proof of Solvency -This step is crucial for two primary reasons: first, to validate the root hash of the Merkle Sum Tree (`mst_root`); and second, to ensure that the assets held by the CEX exceed their liabilities, as confirmed through the proof verification on the Summa contract. -The CEX must submit this proof of solvency to the Summa contract. Currently, it's a mandatory requirement to provide this proof before generating the inclusion proof for each user in the current round. +This step is also crucial for two primary reasons: + + first, to validate the root hash of the Merkle Sum Tree (`mst_root`); and second, to ensure that the assets held by the CEX exceed their liabilities, as confirmed through the proof verification on the Summa contract. +The CEX must submit this proof of solvency to the Summa contract. + + Currently, it's a mandatory requirement to provide this proof before generating the inclusion proof for each user in the current round. Without this verification, It seems the user may not trust to the inclusion proof for the round. becuase the `mst_root` is not published on contract. More specifically, it means that the `mst_root` is not correctly verified on the Summa contract. -In this step, we'll guide you through the process of submitting a solvency proof using the Round to the Summa contract. +In here, we'll introduce you through the process of submitting a solvency proof using the `Round` to the Summa contract. The Round serves as the core of the backend in Summa, and we have briefly described it in the Components section. To initialize the `Round` instance, you'll need paths to specific CSV files (`assets.csv` and `entry_16.csv`) and the `ptau/hermez-raw-11` file. Here's what each file does: @@ -147,3 +148,17 @@ The result will display as: ``` 4. Verifying the proof on contract verifier for User #0: true ``` + +### 4. Verify Proof of Inclusion + +This is the final step in the Summa process and the only part that occurs on the user side. Users receive the proof for a specific round and use methods available on the deployed Summa contract. Importantly, the Summa contract verifier function is a view function, meaning it doesn't consume gas or change the blockchain's state. + +In this step, you'll see: +- Retrieve the `mst_root` from the Summa contract and match it with the `root_hash` in the proof. +- Ensure the `leaf_hash` aligns with the hash based on the `username` and `balances` provided by the CEX. +- Use the `verify_inclusion_proof` method on the Summa contract to validate the proof. +The result will display as: + +**Note:** In a production environment, users can independently verify their proof using public interfaces, such as Etherscan, as shown below: +![Summa contract interface on Etherscan](summa_verifier_interface.png) +This offers an added layer of transparency and trust. diff --git a/backend/examples/summa_solvency_flow.rs b/backend/examples/summa_solvency_flow.rs index 76a0861d..172ccb74 100644 --- a/backend/examples/summa_solvency_flow.rs +++ b/backend/examples/summa_solvency_flow.rs @@ -1,11 +1,7 @@ #![feature(generic_const_exprs)] use std::{error::Error, fs::File, io::BufReader, io::Write}; -use ethers::{ - abi::{encode, Token}, - types::{Bytes, U256}, - utils::keccak256, -}; +use ethers::types::U256; use serde_json::{from_reader, to_string_pretty}; use summa_backend::{ @@ -13,6 +9,7 @@ use summa_backend::{ address_ownership::AddressOwnership, round::{MstInclusionProof, Round}, }, + contracts::signer::AddressInput, tests::initialize_test_env, }; use summa_solvency::merkle_sum_tree::utils::generate_leaf_hash; @@ -29,52 +26,25 @@ async fn main() -> Result<(), Box> { // // Each CEX prepares its own `signature` CSV file. let signature_csv_path = "src/apis/csv/signatures.csv"; + + // Using AddressInput::Address to directly provide the summa_contract's address. + // For deployed contracts, if the address is stored in a config file, + // you can alternatively use AddressInput::Path to specify the file's path. let mut address_ownership_client = AddressOwnership::new( "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", anvil.chain_id(), anvil.endpoint().as_str(), - summa_contract.address(), + AddressInput::Address(summa_contract.address()), signature_csv_path, ) .unwrap(); - // Retrieve hashed addresses using the `keccak256` method. - let address_hashes = address_ownership_client - .get_ownership_proofs() - .iter() - .map(|x| keccak256(encode(&[Token::String(x.cex_address.clone())]))) - .collect::>(); - // Dispatch the proof of address ownership. // the `dispatch_proof_of_address_ownership` function sends a transaction to the Summa contract. address_ownership_client .dispatch_proof_of_address_ownership() - .await - .unwrap(); + .await?; - // If the `addressHash` isn't found in the `addressOwnershipProofs` mapping of the Summa contract, - // it will return 0; otherwise, it will return a non-zero value. - // - // You can find unregistered address with null bytes as follows: - // - // let unregistered = summa_contract - // .ownership_proof_by_address([0u8; 32]) - // .call() - // .await - // .unwrap(); - // - // assert_eq!(unregistered, 0); - - // Verify whether the addresses have been registered within the Summa contract. - for address_hash in address_hashes.iter() { - let registered = summa_contract - .ownership_proof_by_address(*address_hash) - .call() - .await - .unwrap(); - - assert_ne!(registered, U256::from(0)); - } println!("1. Ownership proofs are submitted successfully!"); // 2. Submit solvency proof @@ -89,7 +59,7 @@ async fn main() -> Result<(), Box> { "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", // anvil account [0] anvil.chain_id(), anvil.endpoint().as_str(), - summa_contract.address(), + AddressInput::Address(summa_contract.address()), entry_csv, asset_csv, params_path, @@ -98,21 +68,13 @@ async fn main() -> Result<(), Box> { .unwrap(); // Sends the solvency proof, which should ideally complete without errors. - assert_eq!(round.dispatch_solvency_proof().await.unwrap(), ()); - - // You can also use the `solvency_proof_submitted_filter` method to check if the solvency proof is submitted. - // println!("{:?}", summa_contract - // .solvency_proof_submitted_filter() - // .query() - // .await - // .unwrap();); + round.dispatch_solvency_proof().await?; println!("2. Solvency proof is submitted successfully!"); // 3. Generate Inclusion Proof // - // In a production setup, the CEX should first dispatch the solvency proof to update the Merkle sum tree's root before generating any inclusion proofs. - // Otherwise, users might distrust the provided `root_hash` in the inclusion proof, as it hasn't been published on-chain. + // Generate and export the inclusion proof for the specified user to a JSON file. let inclusion_proof = round.get_proof_of_inclusion(USER_INDEX).unwrap(); let filename = format!("user_{}_proof.json", USER_INDEX); @@ -142,49 +104,34 @@ async fn main() -> Result<(), Box> { // Verify the `leaf_hash` from the proof file. // It's assumed that both `user_name` and `balances` are provided by the CEX. + // The `balances` represent the user's balances on the CEX at `snapshot_time`. let user_name = "dxGaEAii".to_string(); let balances = vec![11888, 41163]; - let leaf_hash = public_inputs[0][0]; + let leaf_hash = public_inputs[0]; assert_eq!( leaf_hash, generate_leaf_hash::(user_name.clone(), balances.clone()) ); - // Before verifying `root_hath`, convert type of `proof` and `public_inputs` to the type of `Bytes` and `Vec`. - let proof: Bytes = Bytes::from(inclusion_proof.get_proof().clone()); - let public_inputs: Vec = inclusion_proof - .get_public_inputs() - .iter() - .flat_map(|input_set| { - input_set.iter().map(|input| { - let mut bytes = input.to_bytes(); - bytes.reverse(); - U256::from_big_endian(&bytes) - }) - }) - .collect(); - // Get `mst_root` from contract. the `mst_root` is disptached by CEX with specific time `snapshot_time`. - let mst_root = summa_contract - .mst_roots(snapshot_time) - .call() - .await - .unwrap(); + let mst_root = summa_contract.mst_roots(snapshot_time).call().await?; // Match the `mst_root` with the `root_hash` derived from the proof. assert_eq!(mst_root, public_inputs[1]); // Validate the inclusion proof using the contract verifier. + let proof = inclusion_proof.get_proof(); let verification_result = summa_contract - .verify_inclusion_proof(proof, public_inputs, snapshot_time) - .await - .unwrap(); + .verify_inclusion_proof(proof.clone(), public_inputs.clone(), snapshot_time) + .await?; println!( "4. Verifying the proof on contract veirifer for User #{}: {}", USER_INDEX, verification_result ); + // Wrapping up + drop(anvil); Ok(()) } diff --git a/backend/scripts/update_verifier_contracts.sh b/backend/scripts/update_verifier_contracts.sh new file mode 100644 index 00000000..4e5c8eec --- /dev/null +++ b/backend/scripts/update_verifier_contracts.sh @@ -0,0 +1,26 @@ +#!/bin/bash +set -e + +# Build the verifier contracts +echo "1. Building verifier contracts" +cd ../zk_prover +cargo run --release --example gen_inclusion_verifier +cargo run --release --example gen_solvency_verifier + +# Deploy contracts to local environment +echo "2. Deploying contracts to local environment" +cd ../contracts +npm install +npx hardhat node & +HARDHAT_PID=$! +sleep 5 +npx hardhat run scripts/deploy.ts --network localhost + +# Generate interface files for Backend +echo "3. Generating interface files for Backend" +cd ../backend +cargo build + +# Wrap up +echo "Terminate hardhat node" +kill $HARDHAT_PID diff --git a/backend/src/apis/address_ownership.rs b/backend/src/apis/address_ownership.rs index 7b56348b..54233e86 100644 --- a/backend/src/apis/address_ownership.rs +++ b/backend/src/apis/address_ownership.rs @@ -1,5 +1,7 @@ -use crate::contracts::{generated::summa_contract::AddressOwnershipProof, signer::SummaSigner}; -use ethers::types::Address; +use crate::contracts::{ + generated::summa_contract::AddressOwnershipProof, + signer::{AddressInput, SummaSigner}, +}; use std::{error::Error, result::Result}; use super::csv_parser::parse_signature_csv; @@ -14,14 +16,14 @@ impl AddressOwnership { signer_key: &str, chain_id: u64, rpc_url: &str, - summa_sc_address: Address, + summa_address_input: AddressInput, signature_csv_path: &str, ) -> Result> { let address_ownership_proofs = parse_signature_csv(signature_csv_path)?; Ok(AddressOwnership { address_ownership_proofs, - signer: SummaSigner::new(signer_key, chain_id, rpc_url, summa_sc_address), + signer: SummaSigner::new(signer_key, chain_id, rpc_url, summa_address_input), }) } diff --git a/backend/src/apis/round.rs b/backend/src/apis/round.rs index c1f47b45..eba011d3 100644 --- a/backend/src/apis/round.rs +++ b/backend/src/apis/round.rs @@ -1,18 +1,17 @@ -use ethers::{ - abi::Address, - types::{Bytes, U256}, -}; +use ethers::types::{Bytes, U256}; use halo2_proofs::{ halo2curves::bn256::{Bn256, Fr as Fp, G1Affine}, plonk::{ProvingKey, VerifyingKey}, poly::kzg::commitment::ParamsKZG, }; use serde::{Deserialize, Serialize}; -use snark_verifier_sdk::{evm::gen_evm_proof_shplonk, CircuitExt}; use std::error::Error; use super::csv_parser::parse_asset_csv; -use crate::contracts::{generated::summa_contract::summa::Asset, signer::SummaSigner}; +use crate::contracts::{ + generated::summa_contract::summa::Asset, + signer::{AddressInput, SummaSigner}, +}; use summa_solvency::{ circuits::{ merkle_sum_tree::MstInclusionCircuit, @@ -46,17 +45,17 @@ impl SolvencyProof { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct MstInclusionProof { - public_inputs: Vec>, - proof: Vec, + public_inputs: Vec, + proof_calldata: Bytes, } impl MstInclusionProof { - pub fn get_public_inputs(&self) -> &Vec> { + pub fn get_public_inputs(&self) -> &Vec { &self.public_inputs } - pub fn get_proof(&self) -> &Vec { - &self.proof + pub fn get_proof(&self) -> &Bytes { + &self.proof_calldata } } @@ -82,7 +81,7 @@ where signer_key: &str, chain_id: u64, rpc_url: &str, - summa_sc_address: Address, + summa_address_input: AddressInput, entry_csv_path: &str, asset_csv_path: &str, params_path: &str, @@ -96,7 +95,7 @@ where params_path, ) .unwrap(), - signer: SummaSigner::new(signer_key, chain_id, rpc_url, summa_sc_address), + signer: SummaSigner::new(signer_key, chain_id, rpc_url, summa_address_input), }) } @@ -203,16 +202,15 @@ where MstInclusionCircuit::::init(self.mst.clone(), user_index); // Currently, default manner of generating a inclusion proof for solidity-verifier. - let proof = gen_evm_proof_shplonk( + let calldata = gen_proof_solidity_calldata( &self.trusted_setup[0].0, &self.trusted_setup[0].1, circuit.clone(), - circuit.instances(), ); Ok(MstInclusionProof { - public_inputs: circuit.instances(), - proof, + proof_calldata: calldata.0, + public_inputs: calldata.1, }) } } diff --git a/backend/src/contracts/deployments.json b/backend/src/contracts/deployments.json index 0dadb21b..a945ae8f 100644 --- a/backend/src/contracts/deployments.json +++ b/backend/src/contracts/deployments.json @@ -1 +1 @@ -{"31337":{"address":"0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0"}} \ No newline at end of file +{"31337":{"address":"0xdc64a140aa3e981100a9beca4e685f962f0cf6c9"}} diff --git a/backend/src/contracts/mod.rs b/backend/src/contracts/mod.rs index a6499aca..55333bf2 100644 --- a/backend/src/contracts/mod.rs +++ b/backend/src/contracts/mod.rs @@ -1,3 +1,3 @@ pub mod generated; pub mod mock; -pub(crate) mod signer; +pub mod signer; diff --git a/backend/src/contracts/signer.rs b/backend/src/contracts/signer.rs index 5b4f3ccc..633b2ed6 100644 --- a/backend/src/contracts/signer.rs +++ b/backend/src/contracts/signer.rs @@ -1,4 +1,3 @@ -use crate::contracts::generated::summa_contract::Summa; use ethers::{ prelude::SignerMiddleware, providers::{Http, Provider}, @@ -11,6 +10,12 @@ use std::{ }; use super::generated::summa_contract::{AddressOwnershipProof, Asset}; +use crate::contracts::generated::summa_contract::Summa; + +pub enum AddressInput { + Address(Address), + Path(String), +} #[derive(Debug)] pub struct SummaSigner { @@ -23,8 +28,13 @@ impl SummaSigner { /// * `signer_key` - The private key of wallet that will interact with the chain on behalf of the exchange /// * `chain_id` - The chain id of the network /// * `rpc_url` - The RPC URL of the network - /// * `address` - The address of the Summa contract - pub fn new(signer_key: &str, chain_id: u64, rpc_url: &str, address: Address) -> Self { + /// * `address_input` - Either the contract's direct address or a path to its config file. + pub fn new( + signer_key: &str, + chain_id: u64, + rpc_url: &str, + address_input: AddressInput, + ) -> Self { let wallet: LocalWallet = LocalWallet::from_str(signer_key).unwrap(); let provider = Provider::::try_from(rpc_url) @@ -35,13 +45,25 @@ impl SummaSigner { wallet.with_chain_id(chain_id), )); + let address = match address_input { + AddressInput::Address(address) => address, + AddressInput::Path(path) => { + let address = Self::get_deployment_address(path, chain_id).unwrap(); + address + } + }; + let contract = Summa::new(address, client); Self { summa_contract: contract, } } - pub fn get_deployment_address>( + pub fn get_summa_address(&self) -> Address { + self.summa_contract.address() + } + + fn get_deployment_address>( path: P, chain_id: u64, ) -> Result> { @@ -72,9 +94,10 @@ impl SummaSigner { let submit_proof_of_address_ownership = &self .summa_contract .submit_proof_of_address_ownership(address_ownership_proofs); - let tx = submit_proof_of_address_ownership.send().await.unwrap(); + let tx = submit_proof_of_address_ownership.send().await?; - tx.await.unwrap(); + // Wait for the pending transaction to be mined + tx.await?; Ok(()) } @@ -89,9 +112,10 @@ impl SummaSigner { let submit_proof_of_solvency_call = &self .summa_contract .submit_proof_of_solvency(mst_root, assets, proof, timestamp); - let tx = submit_proof_of_solvency_call.send().await.unwrap(); + let tx = submit_proof_of_solvency_call.send().await?; - tx.await.unwrap(); + // Wait for the pending transaction to be mined + tx.await?; Ok(()) } diff --git a/backend/src/tests.rs b/backend/src/tests.rs index faf41cee..7098c8e6 100644 --- a/backend/src/tests.rs +++ b/backend/src/tests.rs @@ -102,64 +102,82 @@ pub async fn initialize_test_env() -> ( #[cfg(test)] mod test { - use ethers::{ - abi::AbiEncode, - types::{Bytes, U256}, - utils::to_checksum, - }; + use ethers::{abi::AbiEncode, types::U256, utils::to_checksum}; + use std::error::Error; use crate::apis::{address_ownership::AddressOwnership, round::Round}; - use crate::contracts::generated::summa_contract::{ - AddressOwnershipProof, AddressOwnershipProofSubmittedFilter, Asset, - SolvencyProofSubmittedFilter, + use crate::contracts::{ + generated::summa_contract::{ + AddressOwnershipProof, AddressOwnershipProofSubmittedFilter, Asset, + SolvencyProofSubmittedFilter, + }, + signer::{AddressInput, SummaSigner}, }; use crate::tests::initialize_test_env; #[tokio::test] - async fn test_round_features() { + async fn test_deployed_address() -> Result<(), Box> { + let (anvil, _, _, _, summa_contract) = initialize_test_env().await; + + // Hardhat development environment, usually updates the address of a deployed contract in the `artifacts` directory. + // However, in our custom deployment script, `contracts/scripts/deploy.ts`, + // the address gets updated in `backend/src/contracts/deployments.json`. + let contract_address = summa_contract.address(); + + let summa_signer = SummaSigner::new( + "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", + anvil.chain_id(), + anvil.endpoint().as_str(), + AddressInput::Path("./src/contracts/deployments.json".into()), // the file contains the address of the deployed contract + ); + + assert_eq!(contract_address, summa_signer.get_summa_address()); + + Ok(()) + } + + #[tokio::test] + async fn test_round_features() -> Result<(), Box> { let (anvil, cex_addr_1, cex_addr_2, _, summa_contract) = initialize_test_env().await; let mut address_ownership_client = AddressOwnership::new( "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", anvil.chain_id(), anvil.endpoint().as_str(), - summa_contract.address(), + AddressInput::Address(summa_contract.address()), "src/apis/csv/signatures.csv", ) .unwrap(); - let ownership_submitted_result = address_ownership_client + address_ownership_client .dispatch_proof_of_address_ownership() - .await; + .await?; - assert_eq!(ownership_submitted_result.is_ok(), true); - - let logs = summa_contract + let ownership_proof_logs = summa_contract .address_ownership_proof_submitted_filter() .query() - .await - .unwrap(); + .await?; - assert_eq!(logs.len(), 1); + assert_eq!(ownership_proof_logs.len(), 1); assert_eq!( - logs[0], - AddressOwnershipProofSubmittedFilter { - address_ownership_proofs: vec![AddressOwnershipProof { - chain: "ETH".to_string(), - cex_address: to_checksum(&cex_addr_1, None), - signature: - ("0x089b32327d332c295dc3b8873c205b72153211de6dc1c51235782b091cefb9d06d6df2661b86a7d441cd322f125b84901486b150e684221a7b7636eb8182af551b").parse().unwrap(), - message: "Summa proof of solvency for CryptoExchange".encode().into(), - },AddressOwnershipProof { - chain: "ETH".to_string(), - cex_address:to_checksum(&cex_addr_2, None), - signature: - ("0xb17a9e25265d3b88de7bfad81e7accad6e3d5612308ff83cc0fef76a34152b0444309e8fc3dea5139e49b6fc83a8553071a7af3d0cfd3fb8c1aea2a4c171729c1c").parse().unwrap(), - message: "Summa proof of solvency for CryptoExchange".encode().into(), - }, - ], - } - ); + ownership_proof_logs[0], + AddressOwnershipProofSubmittedFilter { + address_ownership_proofs: vec![AddressOwnershipProof { + chain: "ETH".to_string(), + cex_address: to_checksum(&cex_addr_1, None), + signature: + ("0x089b32327d332c295dc3b8873c205b72153211de6dc1c51235782b091cefb9d06d6df2661b86a7d441cd322f125b84901486b150e684221a7b7636eb8182af551b").parse().unwrap(), + message: "Summa proof of solvency for CryptoExchange".encode().into(), + },AddressOwnershipProof { + chain: "ETH".to_string(), + cex_address:to_checksum(&cex_addr_2, None), + signature: + ("0xb17a9e25265d3b88de7bfad81e7accad6e3d5612308ff83cc0fef76a34152b0444309e8fc3dea5139e49b6fc83a8553071a7af3d0cfd3fb8c1aea2a4c171729c1c").parse().unwrap(), + message: "Summa proof of solvency for CryptoExchange".encode().into(), + }, + ], + } + ); // Initialize round let asset_csv = "src/apis/csv/assets.csv"; @@ -170,7 +188,7 @@ mod test { "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", // anvil account [0] anvil.chain_id(), anvil.endpoint().as_str(), - summa_contract.address(), + AddressInput::Address(summa_contract.address()), entry_csv, asset_csv, params_path, @@ -179,12 +197,12 @@ mod test { .unwrap(); // Verify solvency proof - let mut logs = summa_contract + let mut solvency_proof_logs = summa_contract .solvency_proof_submitted_filter() .query() - .await - .unwrap(); - assert_eq!(logs.len(), 0); + .await?; + + assert_eq!(solvency_proof_logs.len(), 0); // Dispatch solvency proof let assets = [ @@ -200,20 +218,18 @@ mod test { }, ]; - assert_eq!(round.dispatch_solvency_proof().await.unwrap(), ()); + // Send sovlecy proof to contract + round.dispatch_solvency_proof().await?; // After sending transaction of proof of solvency, logs should be updated - logs = summa_contract + solvency_proof_logs = summa_contract .solvency_proof_submitted_filter() .query() - .await - .unwrap(); - - assert_eq!(logs.len(), 1); + .await?; - assert_eq!(logs.len(), 1); + assert_eq!(solvency_proof_logs.len(), 1); assert_eq!( - logs[0], + solvency_proof_logs[0], SolvencyProofSubmittedFilter { timestamp: U256::from(1), mst_root: "0x2E021D9BF99C5BD7267488B6A7A5CF5F7D00222A41B6A9B971899C44089E0C5" @@ -225,27 +241,19 @@ mod test { // Test inclusion proof let inclusion_proof = round.get_proof_of_inclusion(0).unwrap(); - let proof = Bytes::from(inclusion_proof.get_proof().clone()); - let public_inputs: Vec = inclusion_proof - .get_public_inputs() - .iter() - .flat_map(|input_set| { - input_set.iter().map(|input| { - let mut bytes = input.to_bytes(); - bytes.reverse(); - U256::from_big_endian(&bytes) - }) - }) - .collect(); // Verify inclusion proof with onchain function let verified = summa_contract - .verify_inclusion_proof(proof, public_inputs, U256::from(1)) - .await - .unwrap(); + .verify_inclusion_proof( + inclusion_proof.get_proof().clone(), + inclusion_proof.get_public_inputs().clone(), + U256::from(1), + ) + .await?; - assert_eq!(verified, true); + assert!(verified); drop(anvil); + Ok(()) } } diff --git a/backend/summa_verifier_interface.png b/backend/summa_verifier_interface.png new file mode 100644 index 00000000..3d544772 Binary files /dev/null and b/backend/summa_verifier_interface.png differ diff --git a/contracts/README.md b/contracts/README.md index ac63c987..f0a061b7 100644 --- a/contracts/README.md +++ b/contracts/README.md @@ -1,22 +1,43 @@ -# Summa Smart contract +# Summa Smart Contract -Install dependencies: +The [Summa smart contract](src/Summa.sol) acts as a registrar for Centralized Exchanges (CEXs) to commit to their liabilities by submitting a Merkle sum tree (MST) root of all the CEX user asset balances. Users can then verify their inclusion into the liabilities commitment, and the public can compare the committed total sums with the assets owned by the CEX onchain. + +## Features + +- **Address Ownership Proofs**: CEXs should submit the proof of address ownership for all addresses that hold the assets included into the commitment by using `submitProofOfAddressOwnership` function. The proofs are accepted optimistically and subject to off-chain verification. + +- **Liabilities Commitments**: CEXs can submit commitments to its liabilities in the form of MST roots and the corresponding total sums that represent the snapshots of the liabilities at a given timestamp by using `submitCommitment` function. + +- **Inclusion Verification**: Users are able to verify the zero-knowledge proof of inclusion of their balances into the MST using `verifyInclusionProof` function. The function is calling the underlying smart contract [Verifier](src/InclusionVerifier.sol). The verifier is generated from the [zk_prover](./../zk_prover/) module (see module's [readme](./../zk_prover/README.md)). + +## Installation + +Ensure you have Node.js installed on your machine before proceeding. The smart contract is written in Solidity and uses the Hardhat environment for testing and deployment. + +To set up the project environment, install the necessary dependencies: ```shell npm install ``` -Testing: +## Testing ```shell npx hardhat node REPORT_GAS=true npx hardhat test ``` -Deploying: +### Test Coverage + +```shell +npx hardhat coverage +``` + +## Deploying ```shell npx hardhat run scripts/deploy.ts --network localhost ``` -The deployment script will copy the contract ABIs from the ./artifacts/src/ to the [backend subproject](./../backend/src/contracts/abi/). The backend buildscript will then be able to generate the updated contract interfaces (see the [backend readme](./../backend/README.md)). +The deployment script writes the latest deployment address for the chain to the [deployments](./../backend/src/contracts/deployments.json) file in the backend project. This data can later be used by the backend module to connect to the deployed contract. +The deployment script will copy the contract ABIs from the ./artifacts/src/ to the [backend](./../backend/src/contracts/abi/) module. The backend buildscript will then be able to generate the updated contract interfaces (see the backend [readme](./../backend/README.md)). diff --git a/contracts/scripts/deploy.ts b/contracts/scripts/deploy.ts index 4425c4dc..5329c0d3 100644 --- a/contracts/scripts/deploy.ts +++ b/contracts/scripts/deploy.ts @@ -10,18 +10,12 @@ type Deployments = { }; async function main() { - const solvencyVerifier = await ethers.deployContract( - "src/SolvencyVerifier.sol:Verifier" - ); - await solvencyVerifier.deployed(); - const inclusionVerifier = await ethers.deployContract( "src/InclusionVerifier.sol:Verifier" ); await inclusionVerifier.deployed(); const summa = await ethers.deployContract("Summa", [ - solvencyVerifier.address, inclusionVerifier.address, ]); @@ -61,7 +55,6 @@ async function main() { //Copy the ABIs from `artifacts/src/*` to `backend/src/contracts/*` copyAbi(fs, "Summa", "Summa"); - copyAbi(fs, "SolvencyVerifier", "Verifier"); copyAbi(fs, "InclusionVerifier", "Verifier"); } diff --git a/contracts/src/SolvencyVerifier.sol b/contracts/src/SolvencyVerifier.sol deleted file mode 100644 index d1e940a9..00000000 --- a/contracts/src/SolvencyVerifier.sol +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-License-Identifier: MIT - pragma solidity ^0.8.17; - - contract Verifier { - function verify( - uint256[] memory pubInputs, - bytes memory proof - ) public view returns (bool) { - bool success = true; - bytes32[846] memory transcript; - 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(add(transcript, 0x20), mod(mload(add(pubInputs, 0x20)), f_q))mstore(add(transcript, 0x40), mod(mload(add(pubInputs, 0x40)), f_q))mstore(add(transcript, 0x60), mod(mload(add(pubInputs, 0x60)), f_q))mstore(add(transcript, 0x0), 308492134225063399814226381520525808113815168934209933683689315739401114858) { let x := mload(add(proof, 0x20)) mstore(add(transcript, 0x80), x) let y := mload(add(proof, 0x40)) mstore(add(transcript, 0xa0), y) success := and(validate_ec_point(x, y), success) } { let x := mload(add(proof, 0x60)) mstore(add(transcript, 0xc0), x) let y := mload(add(proof, 0x80)) mstore(add(transcript, 0xe0), y) success := and(validate_ec_point(x, y), success) } { let x := mload(add(proof, 0xa0)) mstore(add(transcript, 0x100), x) let y := mload(add(proof, 0xc0)) mstore(add(transcript, 0x120), y) success := and(validate_ec_point(x, y), success) }mstore(add(transcript, 0x140), keccak256(add(transcript, 0x0), 320)){ let hash := mload(add(transcript, 0x140)) mstore(add(transcript, 0x160), mod(hash, f_q)) mstore(add(transcript, 0x180), hash) } { let x := mload(add(proof, 0xe0)) mstore(add(transcript, 0x1a0), x) let y := mload(add(proof, 0x100)) mstore(add(transcript, 0x1c0), y) success := and(validate_ec_point(x, y), success) } { let x := mload(add(proof, 0x120)) mstore(add(transcript, 0x1e0), x) let y := mload(add(proof, 0x140)) mstore(add(transcript, 0x200), y) success := and(validate_ec_point(x, y), success) }mstore(add(transcript, 0x220), keccak256(add(transcript, 0x180), 160)){ let hash := mload(add(transcript, 0x220)) mstore(add(transcript, 0x240), mod(hash, f_q)) mstore(add(transcript, 0x260), hash) }mstore8(add(transcript, 0x280), 1)mstore(add(transcript, 0x280), keccak256(add(transcript, 0x260), 33)){ let hash := mload(add(transcript, 0x280)) mstore(add(transcript, 0x2a0), mod(hash, f_q)) mstore(add(transcript, 0x2c0), hash) } { let x := mload(add(proof, 0x160)) mstore(add(transcript, 0x2e0), x) let y := mload(add(proof, 0x180)) mstore(add(transcript, 0x300), y) success := and(validate_ec_point(x, y), success) } { let x := mload(add(proof, 0x1a0)) mstore(add(transcript, 0x320), x) let y := mload(add(proof, 0x1c0)) mstore(add(transcript, 0x340), y) success := and(validate_ec_point(x, y), success) } { let x := mload(add(proof, 0x1e0)) mstore(add(transcript, 0x360), x) let y := mload(add(proof, 0x200)) mstore(add(transcript, 0x380), y) success := and(validate_ec_point(x, y), success) } { let x := mload(add(proof, 0x220)) mstore(add(transcript, 0x3a0), x) let y := mload(add(proof, 0x240)) mstore(add(transcript, 0x3c0), y) success := and(validate_ec_point(x, y), success) }mstore(add(transcript, 0x3e0), keccak256(add(transcript, 0x2c0), 288)){ let hash := mload(add(transcript, 0x3e0)) mstore(add(transcript, 0x400), mod(hash, f_q)) mstore(add(transcript, 0x420), hash) } { let x := mload(add(proof, 0x260)) mstore(add(transcript, 0x440), x) let y := mload(add(proof, 0x280)) mstore(add(transcript, 0x460), y) success := and(validate_ec_point(x, y), success) } { let x := mload(add(proof, 0x2a0)) mstore(add(transcript, 0x480), x) let y := mload(add(proof, 0x2c0)) mstore(add(transcript, 0x4a0), y) success := and(validate_ec_point(x, y), success) } { let x := mload(add(proof, 0x2e0)) mstore(add(transcript, 0x4c0), x) let y := mload(add(proof, 0x300)) mstore(add(transcript, 0x4e0), y) success := and(validate_ec_point(x, y), success) } { let x := mload(add(proof, 0x320)) mstore(add(transcript, 0x500), x) let y := mload(add(proof, 0x340)) mstore(add(transcript, 0x520), y) success := and(validate_ec_point(x, y), success) } { let x := mload(add(proof, 0x360)) mstore(add(transcript, 0x540), x) let y := mload(add(proof, 0x380)) mstore(add(transcript, 0x560), y) success := and(validate_ec_point(x, y), success) }mstore(add(transcript, 0x580), keccak256(add(transcript, 0x420), 352)){ let hash := mload(add(transcript, 0x580)) mstore(add(transcript, 0x5a0), mod(hash, f_q)) mstore(add(transcript, 0x5c0), hash) }mstore(add(transcript, 0x5e0), mod(mload(add(proof, 0x3a0)), f_q))mstore(add(transcript, 0x600), mod(mload(add(proof, 0x3c0)), f_q))mstore(add(transcript, 0x620), mod(mload(add(proof, 0x3e0)), f_q))mstore(add(transcript, 0x640), mod(mload(add(proof, 0x400)), f_q))mstore(add(transcript, 0x660), mod(mload(add(proof, 0x420)), f_q))mstore(add(transcript, 0x680), mod(mload(add(proof, 0x440)), f_q))mstore(add(transcript, 0x6a0), mod(mload(add(proof, 0x460)), f_q))mstore(add(transcript, 0x6c0), mod(mload(add(proof, 0x480)), f_q))mstore(add(transcript, 0x6e0), mod(mload(add(proof, 0x4a0)), f_q))mstore(add(transcript, 0x700), mod(mload(add(proof, 0x4c0)), f_q))mstore(add(transcript, 0x720), mod(mload(add(proof, 0x4e0)), f_q))mstore(add(transcript, 0x740), mod(mload(add(proof, 0x500)), f_q))mstore(add(transcript, 0x760), mod(mload(add(proof, 0x520)), f_q))mstore(add(transcript, 0x780), mod(mload(add(proof, 0x540)), f_q))mstore(add(transcript, 0x7a0), mod(mload(add(proof, 0x560)), f_q))mstore(add(transcript, 0x7c0), mod(mload(add(proof, 0x580)), f_q))mstore(add(transcript, 0x7e0), mod(mload(add(proof, 0x5a0)), f_q))mstore(add(transcript, 0x800), mod(mload(add(proof, 0x5c0)), f_q))mstore(add(transcript, 0x820), mod(mload(add(proof, 0x5e0)), f_q))mstore(add(transcript, 0x840), mod(mload(add(proof, 0x600)), f_q))mstore(add(transcript, 0x860), mod(mload(add(proof, 0x620)), f_q))mstore(add(transcript, 0x880), mod(mload(add(proof, 0x640)), f_q))mstore(add(transcript, 0x8a0), mod(mload(add(proof, 0x660)), f_q))mstore(add(transcript, 0x8c0), mod(mload(add(proof, 0x680)), f_q))mstore(add(transcript, 0x8e0), mod(mload(add(proof, 0x6a0)), f_q))mstore(add(transcript, 0x900), mod(mload(add(proof, 0x6c0)), f_q))mstore(add(transcript, 0x920), mod(mload(add(proof, 0x6e0)), f_q))mstore(add(transcript, 0x940), mod(mload(add(proof, 0x700)), f_q))mstore(add(transcript, 0x960), mod(mload(add(proof, 0x720)), f_q))mstore(add(transcript, 0x980), mod(mload(add(proof, 0x740)), f_q))mstore(add(transcript, 0x9a0), mod(mload(add(proof, 0x760)), f_q))mstore(add(transcript, 0x9c0), mod(mload(add(proof, 0x780)), f_q))mstore(add(transcript, 0x9e0), mod(mload(add(proof, 0x7a0)), f_q))mstore(add(transcript, 0xa00), keccak256(add(transcript, 0x5c0), 1088)){ let hash := mload(add(transcript, 0xa00)) mstore(add(transcript, 0xa20), mod(hash, f_q)) mstore(add(transcript, 0xa40), hash) }mstore8(add(transcript, 0xa60), 1)mstore(add(transcript, 0xa60), keccak256(add(transcript, 0xa40), 33)){ let hash := mload(add(transcript, 0xa60)) mstore(add(transcript, 0xa80), mod(hash, f_q)) mstore(add(transcript, 0xaa0), hash) } { let x := mload(add(proof, 0x7c0)) mstore(add(transcript, 0xac0), x) let y := mload(add(proof, 0x7e0)) mstore(add(transcript, 0xae0), y) success := and(validate_ec_point(x, y), success) }mstore(add(transcript, 0xb00), keccak256(add(transcript, 0xaa0), 96)){ let hash := mload(add(transcript, 0xb00)) mstore(add(transcript, 0xb20), mod(hash, f_q)) mstore(add(transcript, 0xb40), hash) } { let x := mload(add(proof, 0x800)) mstore(add(transcript, 0xb60), x) let y := mload(add(proof, 0x820)) mstore(add(transcript, 0xb80), y) success := and(validate_ec_point(x, y), success) }mstore(add(transcript, 0xba0), mulmod(mload(add(transcript, 0x5a0)), mload(add(transcript, 0x5a0)), f_q))mstore(add(transcript, 0xbc0), mulmod(mload(add(transcript, 0xba0)), mload(add(transcript, 0xba0)), f_q))mstore(add(transcript, 0xbe0), mulmod(mload(add(transcript, 0xbc0)), mload(add(transcript, 0xbc0)), f_q))mstore(add(transcript, 0xc00), mulmod(mload(add(transcript, 0xbe0)), mload(add(transcript, 0xbe0)), f_q))mstore(add(transcript, 0xc20), mulmod(mload(add(transcript, 0xc00)), mload(add(transcript, 0xc00)), f_q))mstore(add(transcript, 0xc40), mulmod(mload(add(transcript, 0xc20)), mload(add(transcript, 0xc20)), f_q))mstore(add(transcript, 0xc60), mulmod(mload(add(transcript, 0xc40)), mload(add(transcript, 0xc40)), f_q))mstore(add(transcript, 0xc80), mulmod(mload(add(transcript, 0xc60)), mload(add(transcript, 0xc60)), f_q))mstore(add(transcript, 0xca0), mulmod(mload(add(transcript, 0xc80)), mload(add(transcript, 0xc80)), f_q))mstore(add(transcript, 0xcc0), mulmod(mload(add(transcript, 0xca0)), mload(add(transcript, 0xca0)), f_q))mstore(add(transcript, 0xce0), addmod(mload(add(transcript, 0xcc0)), 21888242871839275222246405745257275088548364400416034343698204186575808495616, f_q))mstore(add(transcript, 0xd00), mulmod(mload(add(transcript, 0xce0)), 21866867634659744680037180739646672280844703888306253060159436409049855557633, f_q))mstore(add(transcript, 0xd20), mulmod(mload(add(transcript, 0xd00)), 9936069627611189518829255670237324269287146421271524553312532036927871056678, f_q))mstore(add(transcript, 0xd40), addmod(mload(add(transcript, 0x5a0)), 11952173244228085703417150075019950819261217979144509790385672149647937438939, f_q))mstore(add(transcript, 0xd60), mulmod(mload(add(transcript, 0xd00)), 1680739780407307830605919050682431078078760076686599579086116998224280619988, f_q))mstore(add(transcript, 0xd80), addmod(mload(add(transcript, 0x5a0)), 20207503091431967391640486694574844010469604323729434764612087188351527875629, f_q))mstore(add(transcript, 0xda0), mulmod(mload(add(transcript, 0xd00)), 14158528901797138466244491986759313854666262535363044392173788062030301470987, f_q))mstore(add(transcript, 0xdc0), addmod(mload(add(transcript, 0x5a0)), 7729713970042136756001913758497961233882101865052989951524416124545507024630, f_q))mstore(add(transcript, 0xde0), mulmod(mload(add(transcript, 0xd00)), 15699029810934084314820646074566828280617789951162923449200398535581206172418, f_q))mstore(add(transcript, 0xe00), addmod(mload(add(transcript, 0x5a0)), 6189213060905190907425759670690446807930574449253110894497805650994602323199, f_q))mstore(add(transcript, 0xe20), mulmod(mload(add(transcript, 0xd00)), 4260969412351770314333984243767775737437927068151180798236715529158398853173, f_q))mstore(add(transcript, 0xe40), addmod(mload(add(transcript, 0x5a0)), 17627273459487504907912421501489499351110437332264853545461488657417409642444, f_q))mstore(add(transcript, 0xe60), mulmod(mload(add(transcript, 0xd00)), 4925592601992654644734291590386747644864797672605745962807370354577123815907, f_q))mstore(add(transcript, 0xe80), addmod(mload(add(transcript, 0x5a0)), 16962650269846620577512114154870527443683566727810288380890833831998684679710, f_q))mstore(add(transcript, 0xea0), mulmod(mload(add(transcript, 0xd00)), 1, f_q))mstore(add(transcript, 0xec0), addmod(mload(add(transcript, 0x5a0)), 21888242871839275222246405745257275088548364400416034343698204186575808495616, f_q))mstore(add(transcript, 0xee0), mulmod(mload(add(transcript, 0xd00)), 19380560087801265747114831706136320509424814679569278834391540198888293317501, f_q))mstore(add(transcript, 0xf00), addmod(mload(add(transcript, 0x5a0)), 2507682784038009475131574039120954579123549720846755509306663987687515178116, f_q))mstore(add(transcript, 0xf20), mulmod(mload(add(transcript, 0xd00)), 6252951856119339508807713076978770803512896272623217303779254502899773638908, f_q))mstore(add(transcript, 0xf40), addmod(mload(add(transcript, 0x5a0)), 15635291015719935713438692668278504285035468127792817039918949683676034856709, f_q)){ let prod := mload(add(transcript, 0xd40)) prod := mulmod(mload(add(transcript, 0xd80)), prod, f_q) mstore(add(transcript, 0xf60), prod) prod := mulmod(mload(add(transcript, 0xdc0)), prod, f_q) mstore(add(transcript, 0xf80), prod) prod := mulmod(mload(add(transcript, 0xe00)), prod, f_q) mstore(add(transcript, 0xfa0), prod) prod := mulmod(mload(add(transcript, 0xe40)), prod, f_q) mstore(add(transcript, 0xfc0), prod) prod := mulmod(mload(add(transcript, 0xe80)), prod, f_q) mstore(add(transcript, 0xfe0), prod) prod := mulmod(mload(add(transcript, 0xec0)), prod, f_q) mstore(add(transcript, 0x1000), prod) prod := mulmod(mload(add(transcript, 0xf00)), prod, f_q) mstore(add(transcript, 0x1020), prod) prod := mulmod(mload(add(transcript, 0xf40)), prod, f_q) mstore(add(transcript, 0x1040), prod) prod := mulmod(mload(add(transcript, 0xce0)), prod, f_q) mstore(add(transcript, 0x1060), prod) }mstore(add(transcript, 0x10a0), 32)mstore(add(transcript, 0x10c0), 32)mstore(add(transcript, 0x10e0), 32)mstore(add(transcript, 0x1100), mload(add(transcript, 0x1060)))mstore(add(transcript, 0x1120), 21888242871839275222246405745257275088548364400416034343698204186575808495615)mstore(add(transcript, 0x1140), 21888242871839275222246405745257275088548364400416034343698204186575808495617)success := and(eq(staticcall(gas(), 0x5, add(transcript, 0x10a0), 0xc0, add(transcript, 0x1080), 0x20), 1), success){ let inv := mload(add(transcript, 0x1080)) let v v := mload(add(transcript, 0xce0)) mstore(add(transcript, 0xce0), mulmod(mload(add(transcript, 0x1040)), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(add(transcript, 0xf40)) mstore(add(transcript, 0xf40), mulmod(mload(add(transcript, 0x1020)), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(add(transcript, 0xf00)) mstore(add(transcript, 0xf00), mulmod(mload(add(transcript, 0x1000)), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(add(transcript, 0xec0)) mstore(add(transcript, 0xec0), mulmod(mload(add(transcript, 0xfe0)), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(add(transcript, 0xe80)) mstore(add(transcript, 0xe80), mulmod(mload(add(transcript, 0xfc0)), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(add(transcript, 0xe40)) mstore(add(transcript, 0xe40), mulmod(mload(add(transcript, 0xfa0)), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(add(transcript, 0xe00)) mstore(add(transcript, 0xe00), mulmod(mload(add(transcript, 0xf80)), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(add(transcript, 0xdc0)) mstore(add(transcript, 0xdc0), mulmod(mload(add(transcript, 0xf60)), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(add(transcript, 0xd80)) mstore(add(transcript, 0xd80), mulmod(mload(add(transcript, 0xd40)), inv, f_q)) inv := mulmod(v, inv, f_q) mstore(add(transcript, 0xd40), inv) }mstore(add(transcript, 0x1160), mulmod(mload(add(transcript, 0xd20)), mload(add(transcript, 0xd40)), f_q))mstore(add(transcript, 0x1180), mulmod(mload(add(transcript, 0xd60)), mload(add(transcript, 0xd80)), f_q))mstore(add(transcript, 0x11a0), mulmod(mload(add(transcript, 0xda0)), mload(add(transcript, 0xdc0)), f_q))mstore(add(transcript, 0x11c0), mulmod(mload(add(transcript, 0xde0)), mload(add(transcript, 0xe00)), f_q))mstore(add(transcript, 0x11e0), mulmod(mload(add(transcript, 0xe20)), mload(add(transcript, 0xe40)), f_q))mstore(add(transcript, 0x1200), mulmod(mload(add(transcript, 0xe60)), mload(add(transcript, 0xe80)), f_q))mstore(add(transcript, 0x1220), mulmod(mload(add(transcript, 0xea0)), mload(add(transcript, 0xec0)), f_q))mstore(add(transcript, 0x1240), mulmod(mload(add(transcript, 0xee0)), mload(add(transcript, 0xf00)), f_q))mstore(add(transcript, 0x1260), mulmod(mload(add(transcript, 0xf20)), mload(add(transcript, 0xf40)), f_q)){ let result := mulmod(mload(add(transcript, 0x1220)), mload(add(transcript, 0x20)), f_q)result := addmod(mulmod(mload(add(transcript, 0x1240)), mload(add(transcript, 0x40)), f_q), result, f_q)result := addmod(mulmod(mload(add(transcript, 0x1260)), mload(add(transcript, 0x60)), f_q), result, f_q)mstore(add(transcript, 0x1280), result) }mstore(add(transcript, 0x12a0), addmod(mload(add(transcript, 0x5e0)), mload(add(transcript, 0x720)), f_q))mstore(add(transcript, 0x12c0), mulmod(mload(add(transcript, 0x12a0)), mload(add(transcript, 0x12a0)), f_q))mstore(add(transcript, 0x12e0), mulmod(mload(add(transcript, 0x12c0)), mload(add(transcript, 0x12c0)), f_q))mstore(add(transcript, 0x1300), mulmod(mload(add(transcript, 0x12a0)), mload(add(transcript, 0x12e0)), f_q))mstore(add(transcript, 0x1320), mulmod(mload(add(transcript, 0x1300)), 2910766817845651019878574839501801340070030115151021261302834310722729507541, f_q))mstore(add(transcript, 0x1340), addmod(mload(add(transcript, 0x600)), mload(add(transcript, 0x740)), f_q))mstore(add(transcript, 0x1360), mulmod(mload(add(transcript, 0x1340)), mload(add(transcript, 0x1340)), f_q))mstore(add(transcript, 0x1380), mulmod(mload(add(transcript, 0x1360)), mload(add(transcript, 0x1360)), f_q))mstore(add(transcript, 0x13a0), mulmod(mload(add(transcript, 0x1340)), mload(add(transcript, 0x1380)), f_q))mstore(add(transcript, 0x13c0), mulmod(mload(add(transcript, 0x13a0)), 19727366863391167538122140361473584127147630672623100827934084310230022599144, f_q))mstore(add(transcript, 0x13e0), addmod(mload(add(transcript, 0x1320)), mload(add(transcript, 0x13c0)), f_q))mstore(add(transcript, 0x1400), addmod(mload(add(transcript, 0x13e0)), sub(f_q, mload(add(transcript, 0x620))), f_q))mstore(add(transcript, 0x1420), mulmod(mload(add(transcript, 0x1400)), mload(add(transcript, 0x7a0)), f_q))mstore(add(transcript, 0x1440), mulmod(mload(add(transcript, 0x400)), mload(add(transcript, 0x1420)), f_q))mstore(add(transcript, 0x1460), mulmod(mload(add(transcript, 0x1300)), 5776684794125549462448597414050232243778680302179439492664047328281728356345, f_q))mstore(add(transcript, 0x1480), mulmod(mload(add(transcript, 0x13a0)), 8348174920934122550483593999453880006756108121341067172388445916328941978568, f_q))mstore(add(transcript, 0x14a0), addmod(mload(add(transcript, 0x1460)), mload(add(transcript, 0x1480)), f_q))mstore(add(transcript, 0x14c0), addmod(mload(add(transcript, 0x14a0)), sub(f_q, mload(add(transcript, 0x640))), f_q))mstore(add(transcript, 0x14e0), mulmod(mload(add(transcript, 0x14c0)), mload(add(transcript, 0x7a0)), f_q))mstore(add(transcript, 0x1500), addmod(mload(add(transcript, 0x1440)), mload(add(transcript, 0x14e0)), f_q))mstore(add(transcript, 0x1520), mulmod(mload(add(transcript, 0x400)), mload(add(transcript, 0x1500)), f_q))mstore(add(transcript, 0x1540), addmod(mload(add(transcript, 0x1300)), sub(f_q, mload(add(transcript, 0x660))), f_q))mstore(add(transcript, 0x1560), mulmod(mload(add(transcript, 0x1540)), mload(add(transcript, 0x7c0)), f_q))mstore(add(transcript, 0x1580), addmod(mload(add(transcript, 0x1520)), mload(add(transcript, 0x1560)), f_q))mstore(add(transcript, 0x15a0), mulmod(mload(add(transcript, 0x400)), mload(add(transcript, 0x1580)), f_q))mstore(add(transcript, 0x15c0), mulmod(mload(add(transcript, 0x660)), 2910766817845651019878574839501801340070030115151021261302834310722729507541, f_q))mstore(add(transcript, 0x15e0), mulmod(mload(add(transcript, 0x1340)), 19727366863391167538122140361473584127147630672623100827934084310230022599144, f_q))mstore(add(transcript, 0x1600), addmod(mload(add(transcript, 0x15c0)), mload(add(transcript, 0x15e0)), f_q))mstore(add(transcript, 0x1620), addmod(mload(add(transcript, 0x1600)), mload(add(transcript, 0x6e0)), f_q))mstore(add(transcript, 0x1640), mulmod(mload(add(transcript, 0x1620)), mload(add(transcript, 0x1620)), f_q))mstore(add(transcript, 0x1660), mulmod(mload(add(transcript, 0x1640)), mload(add(transcript, 0x1640)), f_q))mstore(add(transcript, 0x1680), mulmod(mload(add(transcript, 0x1620)), mload(add(transcript, 0x1660)), f_q))mstore(add(transcript, 0x16a0), mulmod(mload(add(transcript, 0x620)), 8897705321156975119607866206188469715432233408805434913352778521345836531302, f_q))mstore(add(transcript, 0x16c0), mulmod(mload(add(transcript, 0x640)), 13897810991298242824030978581179475767377101082166056046492926701399149797630, f_q))mstore(add(transcript, 0x16e0), addmod(mload(add(transcript, 0x16a0)), mload(add(transcript, 0x16c0)), f_q))mstore(add(transcript, 0x1700), addmod(mload(add(transcript, 0x1680)), sub(f_q, mload(add(transcript, 0x16e0))), f_q))mstore(add(transcript, 0x1720), mulmod(mload(add(transcript, 0x1700)), mload(add(transcript, 0x7c0)), f_q))mstore(add(transcript, 0x1740), addmod(mload(add(transcript, 0x15a0)), mload(add(transcript, 0x1720)), f_q))mstore(add(transcript, 0x1760), mulmod(mload(add(transcript, 0x400)), mload(add(transcript, 0x1740)), f_q))mstore(add(transcript, 0x1780), mulmod(mload(add(transcript, 0x660)), 5776684794125549462448597414050232243778680302179439492664047328281728356345, f_q))mstore(add(transcript, 0x17a0), mulmod(mload(add(transcript, 0x1340)), 8348174920934122550483593999453880006756108121341067172388445916328941978568, f_q))mstore(add(transcript, 0x17c0), addmod(mload(add(transcript, 0x1780)), mload(add(transcript, 0x17a0)), f_q))mstore(add(transcript, 0x17e0), addmod(mload(add(transcript, 0x17c0)), mload(add(transcript, 0x700)), f_q))mstore(add(transcript, 0x1800), mulmod(mload(add(transcript, 0x620)), 7127083008168878795310303301757642617203533252990949589494537404444738046722, f_q))mstore(add(transcript, 0x1820), mulmod(mload(add(transcript, 0x640)), 10251091711782631878897995303436082826711938358699127319815611151510940403902, f_q))mstore(add(transcript, 0x1840), addmod(mload(add(transcript, 0x1800)), mload(add(transcript, 0x1820)), f_q))mstore(add(transcript, 0x1860), addmod(mload(add(transcript, 0x17e0)), sub(f_q, mload(add(transcript, 0x1840))), f_q))mstore(add(transcript, 0x1880), mulmod(mload(add(transcript, 0x1860)), mload(add(transcript, 0x7c0)), f_q))mstore(add(transcript, 0x18a0), addmod(mload(add(transcript, 0x1760)), mload(add(transcript, 0x1880)), f_q))mstore(add(transcript, 0x18c0), mulmod(mload(add(transcript, 0x400)), mload(add(transcript, 0x18a0)), f_q))mstore(add(transcript, 0x18e0), addmod(1, sub(f_q, mload(add(transcript, 0x780))), f_q))mstore(add(transcript, 0x1900), mulmod(mload(add(transcript, 0x18e0)), mload(add(transcript, 0x780)), f_q))mstore(add(transcript, 0x1920), addmod(2, sub(f_q, mload(add(transcript, 0x780))), f_q))mstore(add(transcript, 0x1940), mulmod(mload(add(transcript, 0x1920)), mload(add(transcript, 0x1900)), f_q))mstore(add(transcript, 0x1960), addmod(3, sub(f_q, mload(add(transcript, 0x780))), f_q))mstore(add(transcript, 0x1980), mulmod(mload(add(transcript, 0x1960)), mload(add(transcript, 0x1940)), f_q))mstore(add(transcript, 0x19a0), addmod(mload(add(transcript, 0x6a0)), mload(add(transcript, 0x5e0)), f_q))mstore(add(transcript, 0x19c0), addmod(mload(add(transcript, 0x19a0)), sub(f_q, mload(add(transcript, 0x620))), f_q))mstore(add(transcript, 0x19e0), mulmod(mload(add(transcript, 0x19c0)), mload(add(transcript, 0x1980)), f_q))mstore(add(transcript, 0x1a00), addmod(mload(add(transcript, 0x18c0)), mload(add(transcript, 0x19e0)), f_q))mstore(add(transcript, 0x1a20), mulmod(mload(add(transcript, 0x400)), mload(add(transcript, 0x1a00)), f_q))mstore(add(transcript, 0x1a40), addmod(mload(add(transcript, 0x680)), sub(f_q, mload(add(transcript, 0x640))), f_q))mstore(add(transcript, 0x1a60), mulmod(mload(add(transcript, 0x1a40)), mload(add(transcript, 0x1980)), f_q))mstore(add(transcript, 0x1a80), addmod(mload(add(transcript, 0x1a20)), mload(add(transcript, 0x1a60)), f_q))mstore(add(transcript, 0x1aa0), mulmod(mload(add(transcript, 0x400)), mload(add(transcript, 0x1a80)), f_q))mstore(add(transcript, 0x1ac0), mulmod(mload(add(transcript, 0x1920)), mload(add(transcript, 0x780)), f_q))mstore(add(transcript, 0x1ae0), mulmod(mload(add(transcript, 0x1960)), mload(add(transcript, 0x1ac0)), f_q))mstore(add(transcript, 0x1b00), addmod(4, sub(f_q, mload(add(transcript, 0x780))), f_q))mstore(add(transcript, 0x1b20), mulmod(mload(add(transcript, 0x1b00)), mload(add(transcript, 0x1ae0)), f_q))mstore(add(transcript, 0x1b40), mulmod(mload(add(transcript, 0x660)), mload(add(transcript, 0x1b20)), f_q))mstore(add(transcript, 0x1b60), addmod(1, sub(f_q, mload(add(transcript, 0x660))), f_q))mstore(add(transcript, 0x1b80), mulmod(mload(add(transcript, 0x1b60)), mload(add(transcript, 0x1b40)), f_q))mstore(add(transcript, 0x1ba0), addmod(mload(add(transcript, 0x1aa0)), mload(add(transcript, 0x1b80)), f_q))mstore(add(transcript, 0x1bc0), mulmod(mload(add(transcript, 0x400)), mload(add(transcript, 0x1ba0)), f_q))mstore(add(transcript, 0x1be0), mulmod(2, mload(add(transcript, 0x660)), f_q))mstore(add(transcript, 0x1c00), addmod(mload(add(transcript, 0x600)), sub(f_q, mload(add(transcript, 0x5e0))), f_q))mstore(add(transcript, 0x1c20), mulmod(mload(add(transcript, 0x1c00)), mload(add(transcript, 0x1be0)), f_q))mstore(add(transcript, 0x1c40), addmod(mload(add(transcript, 0x620)), sub(f_q, mload(add(transcript, 0x5e0))), f_q))mstore(add(transcript, 0x1c60), addmod(mload(add(transcript, 0x1c20)), sub(f_q, mload(add(transcript, 0x1c40))), f_q))mstore(add(transcript, 0x1c80), addmod(mload(add(transcript, 0x600)), sub(f_q, mload(add(transcript, 0x640))), f_q))mstore(add(transcript, 0x1ca0), addmod(mload(add(transcript, 0x1c60)), sub(f_q, mload(add(transcript, 0x1c80))), f_q))mstore(add(transcript, 0x1cc0), mulmod(mload(add(transcript, 0x1ca0)), mload(add(transcript, 0x1b20)), f_q))mstore(add(transcript, 0x1ce0), addmod(mload(add(transcript, 0x1bc0)), mload(add(transcript, 0x1cc0)), f_q))mstore(add(transcript, 0x1d00), mulmod(mload(add(transcript, 0x400)), mload(add(transcript, 0x1ce0)), f_q))mstore(add(transcript, 0x1d20), mulmod(mload(add(transcript, 0x1960)), mload(add(transcript, 0x1900)), f_q))mstore(add(transcript, 0x1d40), mulmod(mload(add(transcript, 0x1b00)), mload(add(transcript, 0x1d20)), f_q))mstore(add(transcript, 0x1d60), addmod(mload(add(transcript, 0x5e0)), mload(add(transcript, 0x600)), f_q))mstore(add(transcript, 0x1d80), addmod(mload(add(transcript, 0x1d60)), sub(f_q, mload(add(transcript, 0x660))), f_q))mstore(add(transcript, 0x1da0), mulmod(mload(add(transcript, 0x1d80)), mload(add(transcript, 0x1d40)), f_q))mstore(add(transcript, 0x1dc0), addmod(mload(add(transcript, 0x1d00)), mload(add(transcript, 0x1da0)), f_q))mstore(add(transcript, 0x1de0), mulmod(mload(add(transcript, 0x400)), mload(add(transcript, 0x1dc0)), f_q))mstore(add(transcript, 0x1e00), addmod(mload(add(transcript, 0x1de0)), mload(add(transcript, 0x1da0)), f_q))mstore(add(transcript, 0x1e20), mulmod(mload(add(transcript, 0x400)), mload(add(transcript, 0x1e00)), f_q))mstore(add(transcript, 0x1e40), mulmod(mload(add(transcript, 0x1b00)), mload(add(transcript, 0x1940)), f_q))mstore(add(transcript, 0x1e60), addmod(mload(add(transcript, 0x5e0)), sub(f_q, mload(add(transcript, 0x600))), f_q))mstore(add(transcript, 0x1e80), addmod(mload(add(transcript, 0x1e60)), 5192296858534827628530496329220096, f_q))mstore(add(transcript, 0x1ea0), addmod(mload(add(transcript, 0x1e80)), sub(f_q, mload(add(transcript, 0x660))), f_q))mstore(add(transcript, 0x1ec0), mulmod(mload(add(transcript, 0x1ea0)), mload(add(transcript, 0x1e40)), f_q))mstore(add(transcript, 0x1ee0), addmod(mload(add(transcript, 0x1e20)), mload(add(transcript, 0x1ec0)), f_q))mstore(add(transcript, 0x1f00), mulmod(mload(add(transcript, 0x400)), mload(add(transcript, 0x1ee0)), f_q))mstore(add(transcript, 0x1f20), addmod(1, sub(f_q, mload(add(transcript, 0x8c0))), f_q))mstore(add(transcript, 0x1f40), mulmod(mload(add(transcript, 0x1f20)), mload(add(transcript, 0x1220)), f_q))mstore(add(transcript, 0x1f60), addmod(mload(add(transcript, 0x1f00)), mload(add(transcript, 0x1f40)), f_q))mstore(add(transcript, 0x1f80), mulmod(mload(add(transcript, 0x400)), mload(add(transcript, 0x1f60)), f_q))mstore(add(transcript, 0x1fa0), mulmod(mload(add(transcript, 0x920)), mload(add(transcript, 0x920)), f_q))mstore(add(transcript, 0x1fc0), addmod(mload(add(transcript, 0x1fa0)), sub(f_q, mload(add(transcript, 0x920))), f_q))mstore(add(transcript, 0x1fe0), mulmod(mload(add(transcript, 0x1fc0)), mload(add(transcript, 0x1160)), f_q))mstore(add(transcript, 0x2000), addmod(mload(add(transcript, 0x1f80)), mload(add(transcript, 0x1fe0)), f_q))mstore(add(transcript, 0x2020), mulmod(mload(add(transcript, 0x400)), mload(add(transcript, 0x2000)), f_q))mstore(add(transcript, 0x2040), addmod(mload(add(transcript, 0x920)), sub(f_q, mload(add(transcript, 0x900))), f_q))mstore(add(transcript, 0x2060), mulmod(mload(add(transcript, 0x2040)), mload(add(transcript, 0x1220)), f_q))mstore(add(transcript, 0x2080), addmod(mload(add(transcript, 0x2020)), mload(add(transcript, 0x2060)), f_q))mstore(add(transcript, 0x20a0), mulmod(mload(add(transcript, 0x400)), mload(add(transcript, 0x2080)), f_q))mstore(add(transcript, 0x20c0), addmod(1, sub(f_q, mload(add(transcript, 0x1160))), f_q))mstore(add(transcript, 0x20e0), addmod(mload(add(transcript, 0x1180)), mload(add(transcript, 0x11a0)), f_q))mstore(add(transcript, 0x2100), addmod(mload(add(transcript, 0x20e0)), mload(add(transcript, 0x11c0)), f_q))mstore(add(transcript, 0x2120), addmod(mload(add(transcript, 0x2100)), mload(add(transcript, 0x11e0)), f_q))mstore(add(transcript, 0x2140), addmod(mload(add(transcript, 0x2120)), mload(add(transcript, 0x1200)), f_q))mstore(add(transcript, 0x2160), addmod(mload(add(transcript, 0x20c0)), sub(f_q, mload(add(transcript, 0x2140))), f_q))mstore(add(transcript, 0x2180), mulmod(mload(add(transcript, 0x800)), mload(add(transcript, 0x240)), f_q))mstore(add(transcript, 0x21a0), addmod(mload(add(transcript, 0x6e0)), mload(add(transcript, 0x2180)), f_q))mstore(add(transcript, 0x21c0), addmod(mload(add(transcript, 0x21a0)), mload(add(transcript, 0x2a0)), f_q))mstore(add(transcript, 0x21e0), mulmod(mload(add(transcript, 0x820)), mload(add(transcript, 0x240)), f_q))mstore(add(transcript, 0x2200), addmod(mload(add(transcript, 0x5e0)), mload(add(transcript, 0x21e0)), f_q))mstore(add(transcript, 0x2220), addmod(mload(add(transcript, 0x2200)), mload(add(transcript, 0x2a0)), f_q))mstore(add(transcript, 0x2240), mulmod(mload(add(transcript, 0x2220)), mload(add(transcript, 0x21c0)), f_q))mstore(add(transcript, 0x2260), mulmod(mload(add(transcript, 0x840)), mload(add(transcript, 0x240)), f_q))mstore(add(transcript, 0x2280), addmod(mload(add(transcript, 0x600)), mload(add(transcript, 0x2260)), f_q))mstore(add(transcript, 0x22a0), addmod(mload(add(transcript, 0x2280)), mload(add(transcript, 0x2a0)), f_q))mstore(add(transcript, 0x22c0), mulmod(mload(add(transcript, 0x22a0)), mload(add(transcript, 0x2240)), f_q))mstore(add(transcript, 0x22e0), mulmod(mload(add(transcript, 0x860)), mload(add(transcript, 0x240)), f_q))mstore(add(transcript, 0x2300), addmod(mload(add(transcript, 0x700)), mload(add(transcript, 0x22e0)), f_q))mstore(add(transcript, 0x2320), addmod(mload(add(transcript, 0x2300)), mload(add(transcript, 0x2a0)), f_q))mstore(add(transcript, 0x2340), mulmod(mload(add(transcript, 0x2320)), mload(add(transcript, 0x22c0)), f_q))mstore(add(transcript, 0x2360), mulmod(mload(add(transcript, 0x2340)), mload(add(transcript, 0x8e0)), f_q))mstore(add(transcript, 0x2380), mulmod(1, mload(add(transcript, 0x240)), f_q))mstore(add(transcript, 0x23a0), mulmod(mload(add(transcript, 0x5a0)), mload(add(transcript, 0x2380)), f_q))mstore(add(transcript, 0x23c0), addmod(mload(add(transcript, 0x6e0)), mload(add(transcript, 0x23a0)), f_q))mstore(add(transcript, 0x23e0), addmod(mload(add(transcript, 0x23c0)), mload(add(transcript, 0x2a0)), f_q))mstore(add(transcript, 0x2400), mulmod(4131629893567559867359510883348571134090853742863529169391034518566172092834, mload(add(transcript, 0x240)), f_q))mstore(add(transcript, 0x2420), mulmod(mload(add(transcript, 0x5a0)), mload(add(transcript, 0x2400)), f_q))mstore(add(transcript, 0x2440), addmod(mload(add(transcript, 0x5e0)), mload(add(transcript, 0x2420)), f_q))mstore(add(transcript, 0x2460), addmod(mload(add(transcript, 0x2440)), mload(add(transcript, 0x2a0)), f_q))mstore(add(transcript, 0x2480), mulmod(mload(add(transcript, 0x2460)), mload(add(transcript, 0x23e0)), f_q))mstore(add(transcript, 0x24a0), mulmod(8910878055287538404433155982483128285667088683464058436815641868457422632747, mload(add(transcript, 0x240)), f_q))mstore(add(transcript, 0x24c0), mulmod(mload(add(transcript, 0x5a0)), mload(add(transcript, 0x24a0)), f_q))mstore(add(transcript, 0x24e0), addmod(mload(add(transcript, 0x600)), mload(add(transcript, 0x24c0)), f_q))mstore(add(transcript, 0x2500), addmod(mload(add(transcript, 0x24e0)), mload(add(transcript, 0x2a0)), f_q))mstore(add(transcript, 0x2520), mulmod(mload(add(transcript, 0x2500)), mload(add(transcript, 0x2480)), f_q))mstore(add(transcript, 0x2540), mulmod(11166246659983828508719468090013646171463329086121580628794302409516816350802, mload(add(transcript, 0x240)), f_q))mstore(add(transcript, 0x2560), mulmod(mload(add(transcript, 0x5a0)), mload(add(transcript, 0x2540)), f_q))mstore(add(transcript, 0x2580), addmod(mload(add(transcript, 0x700)), mload(add(transcript, 0x2560)), f_q))mstore(add(transcript, 0x25a0), addmod(mload(add(transcript, 0x2580)), mload(add(transcript, 0x2a0)), f_q))mstore(add(transcript, 0x25c0), mulmod(mload(add(transcript, 0x25a0)), mload(add(transcript, 0x2520)), f_q))mstore(add(transcript, 0x25e0), mulmod(mload(add(transcript, 0x25c0)), mload(add(transcript, 0x8c0)), f_q))mstore(add(transcript, 0x2600), addmod(mload(add(transcript, 0x2360)), sub(f_q, mload(add(transcript, 0x25e0))), f_q))mstore(add(transcript, 0x2620), mulmod(mload(add(transcript, 0x2600)), mload(add(transcript, 0x2160)), f_q))mstore(add(transcript, 0x2640), addmod(mload(add(transcript, 0x20a0)), mload(add(transcript, 0x2620)), f_q))mstore(add(transcript, 0x2660), mulmod(mload(add(transcript, 0x400)), mload(add(transcript, 0x2640)), f_q))mstore(add(transcript, 0x2680), mulmod(mload(add(transcript, 0x880)), mload(add(transcript, 0x240)), f_q))mstore(add(transcript, 0x26a0), addmod(mload(add(transcript, 0x660)), mload(add(transcript, 0x2680)), f_q))mstore(add(transcript, 0x26c0), addmod(mload(add(transcript, 0x26a0)), mload(add(transcript, 0x2a0)), f_q))mstore(add(transcript, 0x26e0), mulmod(mload(add(transcript, 0x8a0)), mload(add(transcript, 0x240)), f_q))mstore(add(transcript, 0x2700), addmod(mload(add(transcript, 0x1280)), mload(add(transcript, 0x26e0)), f_q))mstore(add(transcript, 0x2720), addmod(mload(add(transcript, 0x2700)), mload(add(transcript, 0x2a0)), f_q))mstore(add(transcript, 0x2740), mulmod(mload(add(transcript, 0x2720)), mload(add(transcript, 0x26c0)), f_q))mstore(add(transcript, 0x2760), mulmod(mload(add(transcript, 0x2740)), mload(add(transcript, 0x940)), f_q))mstore(add(transcript, 0x2780), mulmod(284840088355319032285349970403338060113257071685626700086398481893096618818, mload(add(transcript, 0x240)), f_q))mstore(add(transcript, 0x27a0), mulmod(mload(add(transcript, 0x5a0)), mload(add(transcript, 0x2780)), f_q))mstore(add(transcript, 0x27c0), addmod(mload(add(transcript, 0x660)), mload(add(transcript, 0x27a0)), f_q))mstore(add(transcript, 0x27e0), addmod(mload(add(transcript, 0x27c0)), mload(add(transcript, 0x2a0)), f_q))mstore(add(transcript, 0x2800), mulmod(21134065618345176623193549882539580312263652408302468683943992798037078993309, mload(add(transcript, 0x240)), f_q))mstore(add(transcript, 0x2820), mulmod(mload(add(transcript, 0x5a0)), mload(add(transcript, 0x2800)), f_q))mstore(add(transcript, 0x2840), addmod(mload(add(transcript, 0x1280)), mload(add(transcript, 0x2820)), f_q))mstore(add(transcript, 0x2860), addmod(mload(add(transcript, 0x2840)), mload(add(transcript, 0x2a0)), f_q))mstore(add(transcript, 0x2880), mulmod(mload(add(transcript, 0x2860)), mload(add(transcript, 0x27e0)), f_q))mstore(add(transcript, 0x28a0), mulmod(mload(add(transcript, 0x2880)), mload(add(transcript, 0x920)), f_q))mstore(add(transcript, 0x28c0), addmod(mload(add(transcript, 0x2760)), sub(f_q, mload(add(transcript, 0x28a0))), f_q))mstore(add(transcript, 0x28e0), mulmod(mload(add(transcript, 0x28c0)), mload(add(transcript, 0x2160)), f_q))mstore(add(transcript, 0x2900), addmod(mload(add(transcript, 0x2660)), mload(add(transcript, 0x28e0)), f_q))mstore(add(transcript, 0x2920), mulmod(mload(add(transcript, 0x400)), mload(add(transcript, 0x2900)), f_q))mstore(add(transcript, 0x2940), addmod(1, sub(f_q, mload(add(transcript, 0x960))), f_q))mstore(add(transcript, 0x2960), mulmod(mload(add(transcript, 0x2940)), mload(add(transcript, 0x1220)), f_q))mstore(add(transcript, 0x2980), addmod(mload(add(transcript, 0x2920)), mload(add(transcript, 0x2960)), f_q))mstore(add(transcript, 0x29a0), mulmod(mload(add(transcript, 0x400)), mload(add(transcript, 0x2980)), f_q))mstore(add(transcript, 0x29c0), mulmod(mload(add(transcript, 0x960)), mload(add(transcript, 0x960)), f_q))mstore(add(transcript, 0x29e0), addmod(mload(add(transcript, 0x29c0)), sub(f_q, mload(add(transcript, 0x960))), f_q))mstore(add(transcript, 0x2a00), mulmod(mload(add(transcript, 0x29e0)), mload(add(transcript, 0x1160)), f_q))mstore(add(transcript, 0x2a20), addmod(mload(add(transcript, 0x29a0)), mload(add(transcript, 0x2a00)), f_q))mstore(add(transcript, 0x2a40), mulmod(mload(add(transcript, 0x400)), mload(add(transcript, 0x2a20)), f_q))mstore(add(transcript, 0x2a60), addmod(mload(add(transcript, 0x9a0)), mload(add(transcript, 0x240)), f_q))mstore(add(transcript, 0x2a80), mulmod(mload(add(transcript, 0x2a60)), mload(add(transcript, 0x980)), f_q))mstore(add(transcript, 0x2aa0), addmod(mload(add(transcript, 0x9e0)), mload(add(transcript, 0x2a0)), f_q))mstore(add(transcript, 0x2ac0), mulmod(mload(add(transcript, 0x2aa0)), mload(add(transcript, 0x2a80)), f_q))mstore(add(transcript, 0x2ae0), mulmod(256, mload(add(transcript, 0x6c0)), f_q))mstore(add(transcript, 0x2b00), addmod(mload(add(transcript, 0x660)), sub(f_q, mload(add(transcript, 0x2ae0))), f_q))mstore(add(transcript, 0x2b20), mulmod(mload(add(transcript, 0x2b00)), mload(add(transcript, 0x760)), f_q))mstore(add(transcript, 0x2b40), addmod(mload(add(transcript, 0x2b20)), mload(add(transcript, 0x240)), f_q))mstore(add(transcript, 0x2b60), mulmod(mload(add(transcript, 0x2b40)), mload(add(transcript, 0x960)), f_q))mstore(add(transcript, 0x2b80), addmod(mload(add(transcript, 0x720)), mload(add(transcript, 0x2a0)), f_q))mstore(add(transcript, 0x2ba0), mulmod(mload(add(transcript, 0x2b80)), mload(add(transcript, 0x2b60)), f_q))mstore(add(transcript, 0x2bc0), addmod(mload(add(transcript, 0x2ac0)), sub(f_q, mload(add(transcript, 0x2ba0))), f_q))mstore(add(transcript, 0x2be0), mulmod(mload(add(transcript, 0x2bc0)), mload(add(transcript, 0x2160)), f_q))mstore(add(transcript, 0x2c00), addmod(mload(add(transcript, 0x2a40)), mload(add(transcript, 0x2be0)), f_q))mstore(add(transcript, 0x2c20), mulmod(mload(add(transcript, 0x400)), mload(add(transcript, 0x2c00)), f_q))mstore(add(transcript, 0x2c40), addmod(mload(add(transcript, 0x9a0)), sub(f_q, mload(add(transcript, 0x9e0))), f_q))mstore(add(transcript, 0x2c60), mulmod(mload(add(transcript, 0x2c40)), mload(add(transcript, 0x1220)), f_q))mstore(add(transcript, 0x2c80), addmod(mload(add(transcript, 0x2c20)), mload(add(transcript, 0x2c60)), f_q))mstore(add(transcript, 0x2ca0), mulmod(mload(add(transcript, 0x400)), mload(add(transcript, 0x2c80)), f_q))mstore(add(transcript, 0x2cc0), mulmod(mload(add(transcript, 0x2c40)), mload(add(transcript, 0x2160)), f_q))mstore(add(transcript, 0x2ce0), addmod(mload(add(transcript, 0x9a0)), sub(f_q, mload(add(transcript, 0x9c0))), f_q))mstore(add(transcript, 0x2d00), mulmod(mload(add(transcript, 0x2ce0)), mload(add(transcript, 0x2cc0)), f_q))mstore(add(transcript, 0x2d20), addmod(mload(add(transcript, 0x2ca0)), mload(add(transcript, 0x2d00)), f_q))mstore(add(transcript, 0x2d40), mulmod(mload(add(transcript, 0xcc0)), mload(add(transcript, 0xcc0)), f_q))mstore(add(transcript, 0x2d60), mulmod(mload(add(transcript, 0x2d40)), mload(add(transcript, 0xcc0)), f_q))mstore(add(transcript, 0x2d80), mulmod(mload(add(transcript, 0x2d60)), mload(add(transcript, 0xcc0)), f_q))mstore(add(transcript, 0x2da0), mulmod(mload(add(transcript, 0x2d80)), mload(add(transcript, 0xcc0)), f_q))mstore(add(transcript, 0x2dc0), mulmod(1, mload(add(transcript, 0xcc0)), f_q))mstore(add(transcript, 0x2de0), mulmod(1, mload(add(transcript, 0x2d40)), f_q))mstore(add(transcript, 0x2e00), mulmod(1, mload(add(transcript, 0x2d60)), f_q))mstore(add(transcript, 0x2e20), mulmod(1, mload(add(transcript, 0x2d80)), f_q))mstore(add(transcript, 0x2e40), mulmod(mload(add(transcript, 0x2d20)), mload(add(transcript, 0xce0)), f_q))mstore(add(transcript, 0x2e60), mulmod(mload(add(transcript, 0xba0)), mload(add(transcript, 0x5a0)), f_q))mstore(add(transcript, 0x2e80), mulmod(mload(add(transcript, 0x5a0)), 1, f_q))mstore(add(transcript, 0x2ea0), addmod(mload(add(transcript, 0xb20)), sub(f_q, mload(add(transcript, 0x2e80))), f_q))mstore(add(transcript, 0x2ec0), mulmod(mload(add(transcript, 0x5a0)), 4925592601992654644734291590386747644864797672605745962807370354577123815907, f_q))mstore(add(transcript, 0x2ee0), addmod(mload(add(transcript, 0xb20)), sub(f_q, mload(add(transcript, 0x2ec0))), f_q))mstore(add(transcript, 0x2f00), mulmod(mload(add(transcript, 0x5a0)), 9936069627611189518829255670237324269287146421271524553312532036927871056678, f_q))mstore(add(transcript, 0x2f20), addmod(mload(add(transcript, 0xb20)), sub(f_q, mload(add(transcript, 0x2f00))), f_q))mstore(add(transcript, 0x2f40), mulmod(mload(add(transcript, 0x5a0)), 19380560087801265747114831706136320509424814679569278834391540198888293317501, f_q))mstore(add(transcript, 0x2f60), addmod(mload(add(transcript, 0xb20)), sub(f_q, mload(add(transcript, 0x2f40))), f_q)){ let result := mulmod(mload(add(transcript, 0xb20)), mulmod(mload(add(transcript, 0xba0)), 19470333053884630052643688193991482022807116448657043890197497819686199857828, f_q), f_q)result := addmod(mulmod(mload(add(transcript, 0x5a0)), mulmod(mload(add(transcript, 0xba0)), 2417909817954645169602717551265793065741247951758990453500706366889608637789, f_q), f_q), result, f_q)mstore(add(transcript, 0x2f80), result) }{ let result := mulmod(mload(add(transcript, 0xb20)), mulmod(mload(add(transcript, 0xba0)), 13686227242150003628673578706486473027501243666075718775893288845164412632930, f_q), f_q)result := addmod(mulmod(mload(add(transcript, 0x5a0)), mulmod(mload(add(transcript, 0xba0)), 10079503758141076467065204586507257535549232608832327988764044976944819876346, f_q), f_q), result, f_q)mstore(add(transcript, 0x2fa0), result) }{ let result := mulmod(mload(add(transcript, 0xb20)), mulmod(mload(add(transcript, 0xba0)), 18715936898160381416714524359517348601997944075114713669820885373469568354766, f_q), f_q)result := addmod(mulmod(mload(add(transcript, 0x5a0)), mulmod(mload(add(transcript, 0xba0)), 15375775075249615866494035504844970190233299190010037655541891534730124992278, f_q), f_q), result, f_q)mstore(add(transcript, 0x2fc0), result) }mstore(add(transcript, 0x2fe0), mulmod(1, mload(add(transcript, 0x2ea0)), f_q))mstore(add(transcript, 0x3000), mulmod(mload(add(transcript, 0x2fe0)), mload(add(transcript, 0x2f60)), f_q))mstore(add(transcript, 0x3020), mulmod(mload(add(transcript, 0x3000)), mload(add(transcript, 0x2ee0)), f_q)){ let result := mulmod(mload(add(transcript, 0xb20)), mulmod(mload(add(transcript, 0x5a0)), 2507682784038009475131574039120954579123549720846755509306663987687515178117, f_q), f_q)result := addmod(mulmod(mload(add(transcript, 0x5a0)), mulmod(mload(add(transcript, 0x5a0)), 19380560087801265747114831706136320509424814679569278834391540198888293317500, f_q), f_q), result, f_q)mstore(add(transcript, 0x3040), result) }{ let result := mulmod(mload(add(transcript, 0xb20)), mulmod(mload(add(transcript, 0x5a0)), 19380560087801265747114831706136320509424814679569278834391540198888293317500, f_q), f_q)result := addmod(mulmod(mload(add(transcript, 0x5a0)), mulmod(mload(add(transcript, 0x5a0)), 13127608231681926238307118629157549705911918406946061530612285695988519678593, f_q), f_q), result, f_q)mstore(add(transcript, 0x3060), result) }{ let result := mulmod(mload(add(transcript, 0xb20)), mulmod(mload(add(transcript, 0xba0)), 16140595808673403009154643164823336476463527776677864878778453135559733237044, f_q), f_q)result := addmod(mulmod(mload(add(transcript, 0x5a0)), mulmod(mload(add(transcript, 0xba0)), 5747647063165872213091762580433938612084836623738169464919751051016075258573, f_q), f_q), result, f_q)mstore(add(transcript, 0x3080), result) }{ let result := mulmod(mload(add(transcript, 0xb20)), mulmod(mload(add(transcript, 0xba0)), 17015964487361230672162623735654618573844832338054897787312333529290879253714, f_q), f_q)result := addmod(mulmod(mload(add(transcript, 0x5a0)), mulmod(mload(add(transcript, 0xba0)), 3176732791729641355588945816447819802711920387939493967460175841862547409845, f_q), f_q), result, f_q)mstore(add(transcript, 0x30a0), result) }{ let result := mulmod(mload(add(transcript, 0xb20)), mulmod(mload(add(transcript, 0xba0)), 19187508498431587163140984396833674282302409422288044257471288693049179355069, f_q), f_q)result := addmod(mulmod(mload(add(transcript, 0x5a0)), mulmod(mload(add(transcript, 0xba0)), 17472297497993506786357772047295541913219081564856233764575529621311665103799, f_q), f_q), result, f_q)mstore(add(transcript, 0x30c0), result) }mstore(add(transcript, 0x30e0), mulmod(mload(add(transcript, 0x3000)), mload(add(transcript, 0x2f20)), f_q)){ let result := mulmod(mload(add(transcript, 0xb20)), mulmod(mload(add(transcript, 0x5a0)), 16962650269846620577512114154870527443683566727810288380890833831998684679711, f_q), f_q)result := addmod(mulmod(mload(add(transcript, 0x5a0)), mulmod(mload(add(transcript, 0x5a0)), 4925592601992654644734291590386747644864797672605745962807370354577123815906, f_q), f_q), result, f_q)mstore(add(transcript, 0x3100), result) }{ let result := mulmod(mload(add(transcript, 0xb20)), mulmod(mload(add(transcript, 0x5a0)), 4925592601992654644734291590386747644864797672605745962807370354577123815906, f_q), f_q)result := addmod(mulmod(mload(add(transcript, 0x5a0)), mulmod(mload(add(transcript, 0x5a0)), 664623189640884330400307346618971907426870604454565164570654825418724962734, f_q), f_q), result, f_q)mstore(add(transcript, 0x3120), result) }mstore(add(transcript, 0x3140), mulmod(mload(add(transcript, 0x2fe0)), mload(add(transcript, 0x2ee0)), f_q)){ let result := mulmod(mload(add(transcript, 0xb20)), 1, f_q)result := addmod(mulmod(mload(add(transcript, 0x5a0)), 21888242871839275222246405745257275088548364400416034343698204186575808495616, f_q), result, f_q)mstore(add(transcript, 0x3160), result) }{ let prod := mload(add(transcript, 0x2f80)) prod := mulmod(mload(add(transcript, 0x2fa0)), prod, f_q) mstore(add(transcript, 0x3180), prod) prod := mulmod(mload(add(transcript, 0x2fc0)), prod, f_q) mstore(add(transcript, 0x31a0), prod) prod := mulmod(mload(add(transcript, 0x3040)), prod, f_q) mstore(add(transcript, 0x31c0), prod) prod := mulmod(mload(add(transcript, 0x3060)), prod, f_q) mstore(add(transcript, 0x31e0), prod) prod := mulmod(mload(add(transcript, 0x3000)), prod, f_q) mstore(add(transcript, 0x3200), prod) prod := mulmod(mload(add(transcript, 0x3080)), prod, f_q) mstore(add(transcript, 0x3220), prod) prod := mulmod(mload(add(transcript, 0x30a0)), prod, f_q) mstore(add(transcript, 0x3240), prod) prod := mulmod(mload(add(transcript, 0x30c0)), prod, f_q) mstore(add(transcript, 0x3260), prod) prod := mulmod(mload(add(transcript, 0x30e0)), prod, f_q) mstore(add(transcript, 0x3280), prod) prod := mulmod(mload(add(transcript, 0x3100)), prod, f_q) mstore(add(transcript, 0x32a0), prod) prod := mulmod(mload(add(transcript, 0x3120)), prod, f_q) mstore(add(transcript, 0x32c0), prod) prod := mulmod(mload(add(transcript, 0x3140)), prod, f_q) mstore(add(transcript, 0x32e0), prod) prod := mulmod(mload(add(transcript, 0x3160)), prod, f_q) mstore(add(transcript, 0x3300), prod) prod := mulmod(mload(add(transcript, 0x2fe0)), prod, f_q) mstore(add(transcript, 0x3320), prod) }mstore(add(transcript, 0x3360), 32)mstore(add(transcript, 0x3380), 32)mstore(add(transcript, 0x33a0), 32)mstore(add(transcript, 0x33c0), mload(add(transcript, 0x3320)))mstore(add(transcript, 0x33e0), 21888242871839275222246405745257275088548364400416034343698204186575808495615)mstore(add(transcript, 0x3400), 21888242871839275222246405745257275088548364400416034343698204186575808495617)success := and(eq(staticcall(gas(), 0x5, add(transcript, 0x3360), 0xc0, add(transcript, 0x3340), 0x20), 1), success){ let inv := mload(add(transcript, 0x3340)) let v v := mload(add(transcript, 0x2fe0)) mstore(add(transcript, 0x2fe0), mulmod(mload(add(transcript, 0x3300)), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(add(transcript, 0x3160)) mstore(add(transcript, 0x3160), mulmod(mload(add(transcript, 0x32e0)), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(add(transcript, 0x3140)) mstore(add(transcript, 0x3140), mulmod(mload(add(transcript, 0x32c0)), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(add(transcript, 0x3120)) mstore(add(transcript, 0x3120), mulmod(mload(add(transcript, 0x32a0)), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(add(transcript, 0x3100)) mstore(add(transcript, 0x3100), mulmod(mload(add(transcript, 0x3280)), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(add(transcript, 0x30e0)) mstore(add(transcript, 0x30e0), mulmod(mload(add(transcript, 0x3260)), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(add(transcript, 0x30c0)) mstore(add(transcript, 0x30c0), mulmod(mload(add(transcript, 0x3240)), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(add(transcript, 0x30a0)) mstore(add(transcript, 0x30a0), mulmod(mload(add(transcript, 0x3220)), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(add(transcript, 0x3080)) mstore(add(transcript, 0x3080), mulmod(mload(add(transcript, 0x3200)), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(add(transcript, 0x3000)) mstore(add(transcript, 0x3000), mulmod(mload(add(transcript, 0x31e0)), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(add(transcript, 0x3060)) mstore(add(transcript, 0x3060), mulmod(mload(add(transcript, 0x31c0)), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(add(transcript, 0x3040)) mstore(add(transcript, 0x3040), mulmod(mload(add(transcript, 0x31a0)), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(add(transcript, 0x2fc0)) mstore(add(transcript, 0x2fc0), mulmod(mload(add(transcript, 0x3180)), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(add(transcript, 0x2fa0)) mstore(add(transcript, 0x2fa0), mulmod(mload(add(transcript, 0x2f80)), inv, f_q)) inv := mulmod(v, inv, f_q) mstore(add(transcript, 0x2f80), inv) }{ let result := mload(add(transcript, 0x2f80))result := addmod(mload(add(transcript, 0x2fa0)), result, f_q)result := addmod(mload(add(transcript, 0x2fc0)), result, f_q)mstore(add(transcript, 0x3420), result) }mstore(add(transcript, 0x3440), mulmod(mload(add(transcript, 0x3020)), mload(add(transcript, 0x3000)), f_q)){ let result := mload(add(transcript, 0x3040))result := addmod(mload(add(transcript, 0x3060)), result, f_q)mstore(add(transcript, 0x3460), result) }mstore(add(transcript, 0x3480), mulmod(mload(add(transcript, 0x3020)), mload(add(transcript, 0x30e0)), f_q)){ let result := mload(add(transcript, 0x3080))result := addmod(mload(add(transcript, 0x30a0)), result, f_q)result := addmod(mload(add(transcript, 0x30c0)), result, f_q)mstore(add(transcript, 0x34a0), result) }mstore(add(transcript, 0x34c0), mulmod(mload(add(transcript, 0x3020)), mload(add(transcript, 0x3140)), f_q)){ let result := mload(add(transcript, 0x3100))result := addmod(mload(add(transcript, 0x3120)), result, f_q)mstore(add(transcript, 0x34e0), result) }mstore(add(transcript, 0x3500), mulmod(mload(add(transcript, 0x3020)), mload(add(transcript, 0x2fe0)), f_q)){ let result := mload(add(transcript, 0x3160))mstore(add(transcript, 0x3520), result) }{ let prod := mload(add(transcript, 0x3420)) prod := mulmod(mload(add(transcript, 0x3460)), prod, f_q) mstore(add(transcript, 0x3540), prod) prod := mulmod(mload(add(transcript, 0x34a0)), prod, f_q) mstore(add(transcript, 0x3560), prod) prod := mulmod(mload(add(transcript, 0x34e0)), prod, f_q) mstore(add(transcript, 0x3580), prod) prod := mulmod(mload(add(transcript, 0x3520)), prod, f_q) mstore(add(transcript, 0x35a0), prod) }mstore(add(transcript, 0x35e0), 32)mstore(add(transcript, 0x3600), 32)mstore(add(transcript, 0x3620), 32)mstore(add(transcript, 0x3640), mload(add(transcript, 0x35a0)))mstore(add(transcript, 0x3660), 21888242871839275222246405745257275088548364400416034343698204186575808495615)mstore(add(transcript, 0x3680), 21888242871839275222246405745257275088548364400416034343698204186575808495617)success := and(eq(staticcall(gas(), 0x5, add(transcript, 0x35e0), 0xc0, add(transcript, 0x35c0), 0x20), 1), success){ let inv := mload(add(transcript, 0x35c0)) let v v := mload(add(transcript, 0x3520)) mstore(add(transcript, 0x3520), mulmod(mload(add(transcript, 0x3580)), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(add(transcript, 0x34e0)) mstore(add(transcript, 0x34e0), mulmod(mload(add(transcript, 0x3560)), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(add(transcript, 0x34a0)) mstore(add(transcript, 0x34a0), mulmod(mload(add(transcript, 0x3540)), inv, f_q)) inv := mulmod(v, inv, f_q) v := mload(add(transcript, 0x3460)) mstore(add(transcript, 0x3460), mulmod(mload(add(transcript, 0x3420)), inv, f_q)) inv := mulmod(v, inv, f_q) mstore(add(transcript, 0x3420), inv) }mstore(add(transcript, 0x36a0), mulmod(mload(add(transcript, 0x3440)), mload(add(transcript, 0x3460)), f_q))mstore(add(transcript, 0x36c0), mulmod(mload(add(transcript, 0x3480)), mload(add(transcript, 0x34a0)), f_q))mstore(add(transcript, 0x36e0), mulmod(mload(add(transcript, 0x34c0)), mload(add(transcript, 0x34e0)), f_q))mstore(add(transcript, 0x3700), mulmod(mload(add(transcript, 0x3500)), mload(add(transcript, 0x3520)), f_q))mstore(add(transcript, 0x3720), mulmod(mload(add(transcript, 0xa20)), mload(add(transcript, 0xa20)), f_q))mstore(add(transcript, 0x3740), mulmod(mload(add(transcript, 0x3720)), mload(add(transcript, 0xa20)), f_q))mstore(add(transcript, 0x3760), mulmod(mload(add(transcript, 0x3740)), mload(add(transcript, 0xa20)), f_q))mstore(add(transcript, 0x3780), mulmod(mload(add(transcript, 0x3760)), mload(add(transcript, 0xa20)), f_q))mstore(add(transcript, 0x37a0), mulmod(mload(add(transcript, 0x3780)), mload(add(transcript, 0xa20)), f_q))mstore(add(transcript, 0x37c0), mulmod(mload(add(transcript, 0x37a0)), mload(add(transcript, 0xa20)), f_q))mstore(add(transcript, 0x37e0), mulmod(mload(add(transcript, 0x37c0)), mload(add(transcript, 0xa20)), f_q))mstore(add(transcript, 0x3800), mulmod(mload(add(transcript, 0x37e0)), mload(add(transcript, 0xa20)), f_q))mstore(add(transcript, 0x3820), mulmod(mload(add(transcript, 0x3800)), mload(add(transcript, 0xa20)), f_q))mstore(add(transcript, 0x3840), mulmod(mload(add(transcript, 0x3820)), mload(add(transcript, 0xa20)), f_q))mstore(add(transcript, 0x3860), mulmod(mload(add(transcript, 0x3840)), mload(add(transcript, 0xa20)), f_q))mstore(add(transcript, 0x3880), mulmod(mload(add(transcript, 0x3860)), mload(add(transcript, 0xa20)), f_q))mstore(add(transcript, 0x38a0), mulmod(mload(add(transcript, 0x3880)), mload(add(transcript, 0xa20)), f_q))mstore(add(transcript, 0x38c0), mulmod(mload(add(transcript, 0x38a0)), mload(add(transcript, 0xa20)), f_q))mstore(add(transcript, 0x38e0), mulmod(mload(add(transcript, 0x38c0)), mload(add(transcript, 0xa20)), f_q))mstore(add(transcript, 0x3900), mulmod(mload(add(transcript, 0x38e0)), mload(add(transcript, 0xa20)), f_q))mstore(add(transcript, 0x3920), mulmod(mload(add(transcript, 0xa80)), mload(add(transcript, 0xa80)), f_q))mstore(add(transcript, 0x3940), mulmod(mload(add(transcript, 0x3920)), mload(add(transcript, 0xa80)), f_q))mstore(add(transcript, 0x3960), mulmod(mload(add(transcript, 0x3940)), mload(add(transcript, 0xa80)), f_q))mstore(add(transcript, 0x3980), mulmod(mload(add(transcript, 0x3960)), mload(add(transcript, 0xa80)), f_q)){ let result := mulmod(mload(add(transcript, 0x5e0)), mload(add(transcript, 0x2f80)), f_q)result := addmod(mulmod(mload(add(transcript, 0x620)), mload(add(transcript, 0x2fa0)), f_q), result, f_q)result := addmod(mulmod(mload(add(transcript, 0x6a0)), mload(add(transcript, 0x2fc0)), f_q), result, f_q)mstore(add(transcript, 0x39a0), result) }mstore(add(transcript, 0x39c0), mulmod(mload(add(transcript, 0x39a0)), mload(add(transcript, 0x3420)), f_q))mstore(add(transcript, 0x39e0), mulmod(sub(f_q, mload(add(transcript, 0x39c0))), 1, f_q)){ let result := mulmod(mload(add(transcript, 0x600)), mload(add(transcript, 0x2f80)), f_q)result := addmod(mulmod(mload(add(transcript, 0x640)), mload(add(transcript, 0x2fa0)), f_q), result, f_q)result := addmod(mulmod(mload(add(transcript, 0x680)), mload(add(transcript, 0x2fc0)), f_q), result, f_q)mstore(add(transcript, 0x3a00), result) }mstore(add(transcript, 0x3a20), mulmod(mload(add(transcript, 0x3a00)), mload(add(transcript, 0x3420)), f_q))mstore(add(transcript, 0x3a40), mulmod(sub(f_q, mload(add(transcript, 0x3a20))), mload(add(transcript, 0xa20)), f_q))mstore(add(transcript, 0x3a60), mulmod(1, mload(add(transcript, 0xa20)), f_q))mstore(add(transcript, 0x3a80), addmod(mload(add(transcript, 0x39e0)), mload(add(transcript, 0x3a40)), f_q))mstore(add(transcript, 0x3aa0), mulmod(mload(add(transcript, 0x3a80)), 1, f_q))mstore(add(transcript, 0x3ac0), mulmod(mload(add(transcript, 0x3a60)), 1, f_q))mstore(add(transcript, 0x3ae0), mulmod(1, mload(add(transcript, 0x3440)), f_q)){ let result := mulmod(mload(add(transcript, 0x660)), mload(add(transcript, 0x3040)), f_q)result := addmod(mulmod(mload(add(transcript, 0x6c0)), mload(add(transcript, 0x3060)), f_q), result, f_q)mstore(add(transcript, 0x3b00), result) }mstore(add(transcript, 0x3b20), mulmod(mload(add(transcript, 0x3b00)), mload(add(transcript, 0x36a0)), f_q))mstore(add(transcript, 0x3b40), mulmod(sub(f_q, mload(add(transcript, 0x3b20))), 1, f_q))mstore(add(transcript, 0x3b60), mulmod(mload(add(transcript, 0x3ae0)), 1, f_q)){ let result := mulmod(mload(add(transcript, 0x920)), mload(add(transcript, 0x3040)), f_q)result := addmod(mulmod(mload(add(transcript, 0x940)), mload(add(transcript, 0x3060)), f_q), result, f_q)mstore(add(transcript, 0x3b80), result) }mstore(add(transcript, 0x3ba0), mulmod(mload(add(transcript, 0x3b80)), mload(add(transcript, 0x36a0)), f_q))mstore(add(transcript, 0x3bc0), mulmod(sub(f_q, mload(add(transcript, 0x3ba0))), mload(add(transcript, 0xa20)), f_q))mstore(add(transcript, 0x3be0), mulmod(mload(add(transcript, 0x3ae0)), mload(add(transcript, 0xa20)), f_q))mstore(add(transcript, 0x3c00), addmod(mload(add(transcript, 0x3b40)), mload(add(transcript, 0x3bc0)), f_q)){ let result := mulmod(mload(add(transcript, 0x960)), mload(add(transcript, 0x3040)), f_q)result := addmod(mulmod(mload(add(transcript, 0x980)), mload(add(transcript, 0x3060)), f_q), result, f_q)mstore(add(transcript, 0x3c20), result) }mstore(add(transcript, 0x3c40), mulmod(mload(add(transcript, 0x3c20)), mload(add(transcript, 0x36a0)), f_q))mstore(add(transcript, 0x3c60), mulmod(sub(f_q, mload(add(transcript, 0x3c40))), mload(add(transcript, 0x3720)), f_q))mstore(add(transcript, 0x3c80), mulmod(mload(add(transcript, 0x3ae0)), mload(add(transcript, 0x3720)), f_q))mstore(add(transcript, 0x3ca0), addmod(mload(add(transcript, 0x3c00)), mload(add(transcript, 0x3c60)), f_q))mstore(add(transcript, 0x3cc0), mulmod(mload(add(transcript, 0x3ca0)), mload(add(transcript, 0xa80)), f_q))mstore(add(transcript, 0x3ce0), mulmod(mload(add(transcript, 0x3b60)), mload(add(transcript, 0xa80)), f_q))mstore(add(transcript, 0x3d00), mulmod(mload(add(transcript, 0x3be0)), mload(add(transcript, 0xa80)), f_q))mstore(add(transcript, 0x3d20), mulmod(mload(add(transcript, 0x3c80)), mload(add(transcript, 0xa80)), f_q))mstore(add(transcript, 0x3d40), addmod(mload(add(transcript, 0x3aa0)), mload(add(transcript, 0x3cc0)), f_q))mstore(add(transcript, 0x3d60), mulmod(1, mload(add(transcript, 0x3480)), f_q)){ let result := mulmod(mload(add(transcript, 0x8c0)), mload(add(transcript, 0x3080)), f_q)result := addmod(mulmod(mload(add(transcript, 0x8e0)), mload(add(transcript, 0x30a0)), f_q), result, f_q)result := addmod(mulmod(mload(add(transcript, 0x900)), mload(add(transcript, 0x30c0)), f_q), result, f_q)mstore(add(transcript, 0x3d80), result) }mstore(add(transcript, 0x3da0), mulmod(mload(add(transcript, 0x3d80)), mload(add(transcript, 0x36c0)), f_q))mstore(add(transcript, 0x3dc0), mulmod(sub(f_q, mload(add(transcript, 0x3da0))), 1, f_q))mstore(add(transcript, 0x3de0), mulmod(mload(add(transcript, 0x3d60)), 1, f_q))mstore(add(transcript, 0x3e00), mulmod(mload(add(transcript, 0x3dc0)), mload(add(transcript, 0x3920)), f_q))mstore(add(transcript, 0x3e20), mulmod(mload(add(transcript, 0x3de0)), mload(add(transcript, 0x3920)), f_q))mstore(add(transcript, 0x3e40), addmod(mload(add(transcript, 0x3d40)), mload(add(transcript, 0x3e00)), f_q))mstore(add(transcript, 0x3e60), mulmod(1, mload(add(transcript, 0x34c0)), f_q)){ let result := mulmod(mload(add(transcript, 0x9a0)), mload(add(transcript, 0x3100)), f_q)result := addmod(mulmod(mload(add(transcript, 0x9c0)), mload(add(transcript, 0x3120)), f_q), result, f_q)mstore(add(transcript, 0x3e80), result) }mstore(add(transcript, 0x3ea0), mulmod(mload(add(transcript, 0x3e80)), mload(add(transcript, 0x36e0)), f_q))mstore(add(transcript, 0x3ec0), mulmod(sub(f_q, mload(add(transcript, 0x3ea0))), 1, f_q))mstore(add(transcript, 0x3ee0), mulmod(mload(add(transcript, 0x3e60)), 1, f_q))mstore(add(transcript, 0x3f00), mulmod(mload(add(transcript, 0x3ec0)), mload(add(transcript, 0x3940)), f_q))mstore(add(transcript, 0x3f20), mulmod(mload(add(transcript, 0x3ee0)), mload(add(transcript, 0x3940)), f_q))mstore(add(transcript, 0x3f40), addmod(mload(add(transcript, 0x3e40)), mload(add(transcript, 0x3f00)), f_q))mstore(add(transcript, 0x3f60), mulmod(1, mload(add(transcript, 0x3500)), f_q)){ let result := mulmod(mload(add(transcript, 0x9e0)), mload(add(transcript, 0x3160)), f_q)mstore(add(transcript, 0x3f80), result) }mstore(add(transcript, 0x3fa0), mulmod(mload(add(transcript, 0x3f80)), mload(add(transcript, 0x3700)), f_q))mstore(add(transcript, 0x3fc0), mulmod(sub(f_q, mload(add(transcript, 0x3fa0))), 1, f_q))mstore(add(transcript, 0x3fe0), mulmod(mload(add(transcript, 0x3f60)), 1, f_q)){ let result := mulmod(mload(add(transcript, 0x6e0)), mload(add(transcript, 0x3160)), f_q)mstore(add(transcript, 0x4000), result) }mstore(add(transcript, 0x4020), mulmod(mload(add(transcript, 0x4000)), mload(add(transcript, 0x3700)), f_q))mstore(add(transcript, 0x4040), mulmod(sub(f_q, mload(add(transcript, 0x4020))), mload(add(transcript, 0xa20)), f_q))mstore(add(transcript, 0x4060), mulmod(mload(add(transcript, 0x3f60)), mload(add(transcript, 0xa20)), f_q))mstore(add(transcript, 0x4080), addmod(mload(add(transcript, 0x3fc0)), mload(add(transcript, 0x4040)), f_q)){ let result := mulmod(mload(add(transcript, 0x700)), mload(add(transcript, 0x3160)), f_q)mstore(add(transcript, 0x40a0), result) }mstore(add(transcript, 0x40c0), mulmod(mload(add(transcript, 0x40a0)), mload(add(transcript, 0x3700)), f_q))mstore(add(transcript, 0x40e0), mulmod(sub(f_q, mload(add(transcript, 0x40c0))), mload(add(transcript, 0x3720)), f_q))mstore(add(transcript, 0x4100), mulmod(mload(add(transcript, 0x3f60)), mload(add(transcript, 0x3720)), f_q))mstore(add(transcript, 0x4120), addmod(mload(add(transcript, 0x4080)), mload(add(transcript, 0x40e0)), f_q)){ let result := mulmod(mload(add(transcript, 0x720)), mload(add(transcript, 0x3160)), f_q)mstore(add(transcript, 0x4140), result) }mstore(add(transcript, 0x4160), mulmod(mload(add(transcript, 0x4140)), mload(add(transcript, 0x3700)), f_q))mstore(add(transcript, 0x4180), mulmod(sub(f_q, mload(add(transcript, 0x4160))), mload(add(transcript, 0x3740)), f_q))mstore(add(transcript, 0x41a0), mulmod(mload(add(transcript, 0x3f60)), mload(add(transcript, 0x3740)), f_q))mstore(add(transcript, 0x41c0), addmod(mload(add(transcript, 0x4120)), mload(add(transcript, 0x4180)), f_q)){ let result := mulmod(mload(add(transcript, 0x740)), mload(add(transcript, 0x3160)), f_q)mstore(add(transcript, 0x41e0), result) }mstore(add(transcript, 0x4200), mulmod(mload(add(transcript, 0x41e0)), mload(add(transcript, 0x3700)), f_q))mstore(add(transcript, 0x4220), mulmod(sub(f_q, mload(add(transcript, 0x4200))), mload(add(transcript, 0x3760)), f_q))mstore(add(transcript, 0x4240), mulmod(mload(add(transcript, 0x3f60)), mload(add(transcript, 0x3760)), f_q))mstore(add(transcript, 0x4260), addmod(mload(add(transcript, 0x41c0)), mload(add(transcript, 0x4220)), f_q)){ let result := mulmod(mload(add(transcript, 0x760)), mload(add(transcript, 0x3160)), f_q)mstore(add(transcript, 0x4280), result) }mstore(add(transcript, 0x42a0), mulmod(mload(add(transcript, 0x4280)), mload(add(transcript, 0x3700)), f_q))mstore(add(transcript, 0x42c0), mulmod(sub(f_q, mload(add(transcript, 0x42a0))), mload(add(transcript, 0x3780)), f_q))mstore(add(transcript, 0x42e0), mulmod(mload(add(transcript, 0x3f60)), mload(add(transcript, 0x3780)), f_q))mstore(add(transcript, 0x4300), addmod(mload(add(transcript, 0x4260)), mload(add(transcript, 0x42c0)), f_q)){ let result := mulmod(mload(add(transcript, 0x780)), mload(add(transcript, 0x3160)), f_q)mstore(add(transcript, 0x4320), result) }mstore(add(transcript, 0x4340), mulmod(mload(add(transcript, 0x4320)), mload(add(transcript, 0x3700)), f_q))mstore(add(transcript, 0x4360), mulmod(sub(f_q, mload(add(transcript, 0x4340))), mload(add(transcript, 0x37a0)), f_q))mstore(add(transcript, 0x4380), mulmod(mload(add(transcript, 0x3f60)), mload(add(transcript, 0x37a0)), f_q))mstore(add(transcript, 0x43a0), addmod(mload(add(transcript, 0x4300)), mload(add(transcript, 0x4360)), f_q)){ let result := mulmod(mload(add(transcript, 0x7a0)), mload(add(transcript, 0x3160)), f_q)mstore(add(transcript, 0x43c0), result) }mstore(add(transcript, 0x43e0), mulmod(mload(add(transcript, 0x43c0)), mload(add(transcript, 0x3700)), f_q))mstore(add(transcript, 0x4400), mulmod(sub(f_q, mload(add(transcript, 0x43e0))), mload(add(transcript, 0x37c0)), f_q))mstore(add(transcript, 0x4420), mulmod(mload(add(transcript, 0x3f60)), mload(add(transcript, 0x37c0)), f_q))mstore(add(transcript, 0x4440), addmod(mload(add(transcript, 0x43a0)), mload(add(transcript, 0x4400)), f_q)){ let result := mulmod(mload(add(transcript, 0x7c0)), mload(add(transcript, 0x3160)), f_q)mstore(add(transcript, 0x4460), result) }mstore(add(transcript, 0x4480), mulmod(mload(add(transcript, 0x4460)), mload(add(transcript, 0x3700)), f_q))mstore(add(transcript, 0x44a0), mulmod(sub(f_q, mload(add(transcript, 0x4480))), mload(add(transcript, 0x37e0)), f_q))mstore(add(transcript, 0x44c0), mulmod(mload(add(transcript, 0x3f60)), mload(add(transcript, 0x37e0)), f_q))mstore(add(transcript, 0x44e0), addmod(mload(add(transcript, 0x4440)), mload(add(transcript, 0x44a0)), f_q)){ let result := mulmod(mload(add(transcript, 0x800)), mload(add(transcript, 0x3160)), f_q)mstore(add(transcript, 0x4500), result) }mstore(add(transcript, 0x4520), mulmod(mload(add(transcript, 0x4500)), mload(add(transcript, 0x3700)), f_q))mstore(add(transcript, 0x4540), mulmod(sub(f_q, mload(add(transcript, 0x4520))), mload(add(transcript, 0x3800)), f_q))mstore(add(transcript, 0x4560), mulmod(mload(add(transcript, 0x3f60)), mload(add(transcript, 0x3800)), f_q))mstore(add(transcript, 0x4580), addmod(mload(add(transcript, 0x44e0)), mload(add(transcript, 0x4540)), f_q)){ let result := mulmod(mload(add(transcript, 0x820)), mload(add(transcript, 0x3160)), f_q)mstore(add(transcript, 0x45a0), result) }mstore(add(transcript, 0x45c0), mulmod(mload(add(transcript, 0x45a0)), mload(add(transcript, 0x3700)), f_q))mstore(add(transcript, 0x45e0), mulmod(sub(f_q, mload(add(transcript, 0x45c0))), mload(add(transcript, 0x3820)), f_q))mstore(add(transcript, 0x4600), mulmod(mload(add(transcript, 0x3f60)), mload(add(transcript, 0x3820)), f_q))mstore(add(transcript, 0x4620), addmod(mload(add(transcript, 0x4580)), mload(add(transcript, 0x45e0)), f_q)){ let result := mulmod(mload(add(transcript, 0x840)), mload(add(transcript, 0x3160)), f_q)mstore(add(transcript, 0x4640), result) }mstore(add(transcript, 0x4660), mulmod(mload(add(transcript, 0x4640)), mload(add(transcript, 0x3700)), f_q))mstore(add(transcript, 0x4680), mulmod(sub(f_q, mload(add(transcript, 0x4660))), mload(add(transcript, 0x3840)), f_q))mstore(add(transcript, 0x46a0), mulmod(mload(add(transcript, 0x3f60)), mload(add(transcript, 0x3840)), f_q))mstore(add(transcript, 0x46c0), addmod(mload(add(transcript, 0x4620)), mload(add(transcript, 0x4680)), f_q)){ let result := mulmod(mload(add(transcript, 0x860)), mload(add(transcript, 0x3160)), f_q)mstore(add(transcript, 0x46e0), result) }mstore(add(transcript, 0x4700), mulmod(mload(add(transcript, 0x46e0)), mload(add(transcript, 0x3700)), f_q))mstore(add(transcript, 0x4720), mulmod(sub(f_q, mload(add(transcript, 0x4700))), mload(add(transcript, 0x3860)), f_q))mstore(add(transcript, 0x4740), mulmod(mload(add(transcript, 0x3f60)), mload(add(transcript, 0x3860)), f_q))mstore(add(transcript, 0x4760), addmod(mload(add(transcript, 0x46c0)), mload(add(transcript, 0x4720)), f_q)){ let result := mulmod(mload(add(transcript, 0x880)), mload(add(transcript, 0x3160)), f_q)mstore(add(transcript, 0x4780), result) }mstore(add(transcript, 0x47a0), mulmod(mload(add(transcript, 0x4780)), mload(add(transcript, 0x3700)), f_q))mstore(add(transcript, 0x47c0), mulmod(sub(f_q, mload(add(transcript, 0x47a0))), mload(add(transcript, 0x3880)), f_q))mstore(add(transcript, 0x47e0), mulmod(mload(add(transcript, 0x3f60)), mload(add(transcript, 0x3880)), f_q))mstore(add(transcript, 0x4800), addmod(mload(add(transcript, 0x4760)), mload(add(transcript, 0x47c0)), f_q)){ let result := mulmod(mload(add(transcript, 0x8a0)), mload(add(transcript, 0x3160)), f_q)mstore(add(transcript, 0x4820), result) }mstore(add(transcript, 0x4840), mulmod(mload(add(transcript, 0x4820)), mload(add(transcript, 0x3700)), f_q))mstore(add(transcript, 0x4860), mulmod(sub(f_q, mload(add(transcript, 0x4840))), mload(add(transcript, 0x38a0)), f_q))mstore(add(transcript, 0x4880), mulmod(mload(add(transcript, 0x3f60)), mload(add(transcript, 0x38a0)), f_q))mstore(add(transcript, 0x48a0), addmod(mload(add(transcript, 0x4800)), mload(add(transcript, 0x4860)), f_q))mstore(add(transcript, 0x48c0), mulmod(mload(add(transcript, 0x2dc0)), mload(add(transcript, 0x3500)), f_q))mstore(add(transcript, 0x48e0), mulmod(mload(add(transcript, 0x2de0)), mload(add(transcript, 0x3500)), f_q))mstore(add(transcript, 0x4900), mulmod(mload(add(transcript, 0x2e00)), mload(add(transcript, 0x3500)), f_q))mstore(add(transcript, 0x4920), mulmod(mload(add(transcript, 0x2e20)), mload(add(transcript, 0x3500)), f_q)){ let result := mulmod(mload(add(transcript, 0x2e40)), mload(add(transcript, 0x3160)), f_q)mstore(add(transcript, 0x4940), result) }mstore(add(transcript, 0x4960), mulmod(mload(add(transcript, 0x4940)), mload(add(transcript, 0x3700)), f_q))mstore(add(transcript, 0x4980), mulmod(sub(f_q, mload(add(transcript, 0x4960))), mload(add(transcript, 0x38c0)), f_q))mstore(add(transcript, 0x49a0), mulmod(mload(add(transcript, 0x3f60)), mload(add(transcript, 0x38c0)), f_q))mstore(add(transcript, 0x49c0), mulmod(mload(add(transcript, 0x48c0)), mload(add(transcript, 0x38c0)), f_q))mstore(add(transcript, 0x49e0), mulmod(mload(add(transcript, 0x48e0)), mload(add(transcript, 0x38c0)), f_q))mstore(add(transcript, 0x4a00), mulmod(mload(add(transcript, 0x4900)), mload(add(transcript, 0x38c0)), f_q))mstore(add(transcript, 0x4a20), mulmod(mload(add(transcript, 0x4920)), mload(add(transcript, 0x38c0)), f_q))mstore(add(transcript, 0x4a40), addmod(mload(add(transcript, 0x48a0)), mload(add(transcript, 0x4980)), f_q)){ let result := mulmod(mload(add(transcript, 0x7e0)), mload(add(transcript, 0x3160)), f_q)mstore(add(transcript, 0x4a60), result) }mstore(add(transcript, 0x4a80), mulmod(mload(add(transcript, 0x4a60)), mload(add(transcript, 0x3700)), f_q))mstore(add(transcript, 0x4aa0), mulmod(sub(f_q, mload(add(transcript, 0x4a80))), mload(add(transcript, 0x38e0)), f_q))mstore(add(transcript, 0x4ac0), mulmod(mload(add(transcript, 0x3f60)), mload(add(transcript, 0x38e0)), f_q))mstore(add(transcript, 0x4ae0), addmod(mload(add(transcript, 0x4a40)), mload(add(transcript, 0x4aa0)), f_q))mstore(add(transcript, 0x4b00), mulmod(mload(add(transcript, 0x4ae0)), mload(add(transcript, 0x3960)), f_q))mstore(add(transcript, 0x4b20), mulmod(mload(add(transcript, 0x3fe0)), mload(add(transcript, 0x3960)), f_q))mstore(add(transcript, 0x4b40), mulmod(mload(add(transcript, 0x4060)), mload(add(transcript, 0x3960)), f_q))mstore(add(transcript, 0x4b60), mulmod(mload(add(transcript, 0x4100)), mload(add(transcript, 0x3960)), f_q))mstore(add(transcript, 0x4b80), mulmod(mload(add(transcript, 0x41a0)), mload(add(transcript, 0x3960)), f_q))mstore(add(transcript, 0x4ba0), mulmod(mload(add(transcript, 0x4240)), mload(add(transcript, 0x3960)), f_q))mstore(add(transcript, 0x4bc0), mulmod(mload(add(transcript, 0x42e0)), mload(add(transcript, 0x3960)), f_q))mstore(add(transcript, 0x4be0), mulmod(mload(add(transcript, 0x4380)), mload(add(transcript, 0x3960)), f_q))mstore(add(transcript, 0x4c00), mulmod(mload(add(transcript, 0x4420)), mload(add(transcript, 0x3960)), f_q))mstore(add(transcript, 0x4c20), mulmod(mload(add(transcript, 0x44c0)), mload(add(transcript, 0x3960)), f_q))mstore(add(transcript, 0x4c40), mulmod(mload(add(transcript, 0x4560)), mload(add(transcript, 0x3960)), f_q))mstore(add(transcript, 0x4c60), mulmod(mload(add(transcript, 0x4600)), mload(add(transcript, 0x3960)), f_q))mstore(add(transcript, 0x4c80), mulmod(mload(add(transcript, 0x46a0)), mload(add(transcript, 0x3960)), f_q))mstore(add(transcript, 0x4ca0), mulmod(mload(add(transcript, 0x4740)), mload(add(transcript, 0x3960)), f_q))mstore(add(transcript, 0x4cc0), mulmod(mload(add(transcript, 0x47e0)), mload(add(transcript, 0x3960)), f_q))mstore(add(transcript, 0x4ce0), mulmod(mload(add(transcript, 0x4880)), mload(add(transcript, 0x3960)), f_q))mstore(add(transcript, 0x4d00), mulmod(mload(add(transcript, 0x49a0)), mload(add(transcript, 0x3960)), f_q))mstore(add(transcript, 0x4d20), mulmod(mload(add(transcript, 0x49c0)), mload(add(transcript, 0x3960)), f_q))mstore(add(transcript, 0x4d40), mulmod(mload(add(transcript, 0x49e0)), mload(add(transcript, 0x3960)), f_q))mstore(add(transcript, 0x4d60), mulmod(mload(add(transcript, 0x4a00)), mload(add(transcript, 0x3960)), f_q))mstore(add(transcript, 0x4d80), mulmod(mload(add(transcript, 0x4a20)), mload(add(transcript, 0x3960)), f_q))mstore(add(transcript, 0x4da0), mulmod(mload(add(transcript, 0x4ac0)), mload(add(transcript, 0x3960)), f_q))mstore(add(transcript, 0x4dc0), addmod(mload(add(transcript, 0x3f40)), mload(add(transcript, 0x4b00)), f_q))mstore(add(transcript, 0x4de0), mulmod(1, mload(add(transcript, 0x3020)), f_q))mstore(add(transcript, 0x4e00), mulmod(1, mload(add(transcript, 0xb20)), f_q))mstore(add(transcript, 0x4e20), 0x0000000000000000000000000000000000000000000000000000000000000001) mstore(add(transcript, 0x4e40), 0x0000000000000000000000000000000000000000000000000000000000000002)mstore(add(transcript, 0x4e60), mload(add(transcript, 0x4dc0)))success := and(eq(staticcall(gas(), 0x7, add(transcript, 0x4e20), 0x60, add(transcript, 0x4e20), 0x40), 1), success)mstore(add(transcript, 0x4e80), mload(add(transcript, 0x4e20))) mstore(add(transcript, 0x4ea0), mload(add(transcript, 0x4e40)))mstore(add(transcript, 0x4ec0), mload(add(transcript, 0x80))) mstore(add(transcript, 0x4ee0), mload(add(transcript, 0xa0)))success := and(eq(staticcall(gas(), 0x6, add(transcript, 0x4e80), 0x80, add(transcript, 0x4e80), 0x40), 1), success)mstore(add(transcript, 0x4f00), mload(add(transcript, 0xc0))) mstore(add(transcript, 0x4f20), mload(add(transcript, 0xe0)))mstore(add(transcript, 0x4f40), mload(add(transcript, 0x3ac0)))success := and(eq(staticcall(gas(), 0x7, add(transcript, 0x4f00), 0x60, add(transcript, 0x4f00), 0x40), 1), success)mstore(add(transcript, 0x4f60), mload(add(transcript, 0x4e80))) mstore(add(transcript, 0x4f80), mload(add(transcript, 0x4ea0)))mstore(add(transcript, 0x4fa0), mload(add(transcript, 0x4f00))) mstore(add(transcript, 0x4fc0), mload(add(transcript, 0x4f20)))success := and(eq(staticcall(gas(), 0x6, add(transcript, 0x4f60), 0x80, add(transcript, 0x4f60), 0x40), 1), success)mstore(add(transcript, 0x4fe0), mload(add(transcript, 0x100))) mstore(add(transcript, 0x5000), mload(add(transcript, 0x120)))mstore(add(transcript, 0x5020), mload(add(transcript, 0x3ce0)))success := and(eq(staticcall(gas(), 0x7, add(transcript, 0x4fe0), 0x60, add(transcript, 0x4fe0), 0x40), 1), success)mstore(add(transcript, 0x5040), mload(add(transcript, 0x4f60))) mstore(add(transcript, 0x5060), mload(add(transcript, 0x4f80)))mstore(add(transcript, 0x5080), mload(add(transcript, 0x4fe0))) mstore(add(transcript, 0x50a0), mload(add(transcript, 0x5000)))success := and(eq(staticcall(gas(), 0x6, add(transcript, 0x5040), 0x80, add(transcript, 0x5040), 0x40), 1), success)mstore(add(transcript, 0x50c0), mload(add(transcript, 0x320))) mstore(add(transcript, 0x50e0), mload(add(transcript, 0x340)))mstore(add(transcript, 0x5100), mload(add(transcript, 0x3d00)))success := and(eq(staticcall(gas(), 0x7, add(transcript, 0x50c0), 0x60, add(transcript, 0x50c0), 0x40), 1), success)mstore(add(transcript, 0x5120), mload(add(transcript, 0x5040))) mstore(add(transcript, 0x5140), mload(add(transcript, 0x5060)))mstore(add(transcript, 0x5160), mload(add(transcript, 0x50c0))) mstore(add(transcript, 0x5180), mload(add(transcript, 0x50e0)))success := and(eq(staticcall(gas(), 0x6, add(transcript, 0x5120), 0x80, add(transcript, 0x5120), 0x40), 1), success)mstore(add(transcript, 0x51a0), mload(add(transcript, 0x360))) mstore(add(transcript, 0x51c0), mload(add(transcript, 0x380)))mstore(add(transcript, 0x51e0), mload(add(transcript, 0x3d20)))success := and(eq(staticcall(gas(), 0x7, add(transcript, 0x51a0), 0x60, add(transcript, 0x51a0), 0x40), 1), success)mstore(add(transcript, 0x5200), mload(add(transcript, 0x5120))) mstore(add(transcript, 0x5220), mload(add(transcript, 0x5140)))mstore(add(transcript, 0x5240), mload(add(transcript, 0x51a0))) mstore(add(transcript, 0x5260), mload(add(transcript, 0x51c0)))success := and(eq(staticcall(gas(), 0x6, add(transcript, 0x5200), 0x80, add(transcript, 0x5200), 0x40), 1), success)mstore(add(transcript, 0x5280), mload(add(transcript, 0x2e0))) mstore(add(transcript, 0x52a0), mload(add(transcript, 0x300)))mstore(add(transcript, 0x52c0), mload(add(transcript, 0x3e20)))success := and(eq(staticcall(gas(), 0x7, add(transcript, 0x5280), 0x60, add(transcript, 0x5280), 0x40), 1), success)mstore(add(transcript, 0x52e0), mload(add(transcript, 0x5200))) mstore(add(transcript, 0x5300), mload(add(transcript, 0x5220)))mstore(add(transcript, 0x5320), mload(add(transcript, 0x5280))) mstore(add(transcript, 0x5340), mload(add(transcript, 0x52a0)))success := and(eq(staticcall(gas(), 0x6, add(transcript, 0x52e0), 0x80, add(transcript, 0x52e0), 0x40), 1), success)mstore(add(transcript, 0x5360), mload(add(transcript, 0x1a0))) mstore(add(transcript, 0x5380), mload(add(transcript, 0x1c0)))mstore(add(transcript, 0x53a0), mload(add(transcript, 0x3f20)))success := and(eq(staticcall(gas(), 0x7, add(transcript, 0x5360), 0x60, add(transcript, 0x5360), 0x40), 1), success)mstore(add(transcript, 0x53c0), mload(add(transcript, 0x52e0))) mstore(add(transcript, 0x53e0), mload(add(transcript, 0x5300)))mstore(add(transcript, 0x5400), mload(add(transcript, 0x5360))) mstore(add(transcript, 0x5420), mload(add(transcript, 0x5380)))success := and(eq(staticcall(gas(), 0x6, add(transcript, 0x53c0), 0x80, add(transcript, 0x53c0), 0x40), 1), success)mstore(add(transcript, 0x5440), mload(add(transcript, 0x1e0))) mstore(add(transcript, 0x5460), mload(add(transcript, 0x200)))mstore(add(transcript, 0x5480), mload(add(transcript, 0x4b20)))success := and(eq(staticcall(gas(), 0x7, add(transcript, 0x5440), 0x60, add(transcript, 0x5440), 0x40), 1), success)mstore(add(transcript, 0x54a0), mload(add(transcript, 0x53c0))) mstore(add(transcript, 0x54c0), mload(add(transcript, 0x53e0)))mstore(add(transcript, 0x54e0), mload(add(transcript, 0x5440))) mstore(add(transcript, 0x5500), mload(add(transcript, 0x5460)))success := and(eq(staticcall(gas(), 0x6, add(transcript, 0x54a0), 0x80, add(transcript, 0x54a0), 0x40), 1), success)mstore(add(transcript, 0x5520), 0x0655d62979dc062dc1e8c15334bf17cf5c7e6352219c0bd0b0cc039598d9a669) mstore(add(transcript, 0x5540), 0x019589d3002f6a0ded46a69053f0c05e7a7ca45339b3d2bae85d00f26ac0f33e)mstore(add(transcript, 0x5560), mload(add(transcript, 0x4b40)))success := and(eq(staticcall(gas(), 0x7, add(transcript, 0x5520), 0x60, add(transcript, 0x5520), 0x40), 1), success)mstore(add(transcript, 0x5580), mload(add(transcript, 0x54a0))) mstore(add(transcript, 0x55a0), mload(add(transcript, 0x54c0)))mstore(add(transcript, 0x55c0), mload(add(transcript, 0x5520))) mstore(add(transcript, 0x55e0), mload(add(transcript, 0x5540)))success := and(eq(staticcall(gas(), 0x6, add(transcript, 0x5580), 0x80, add(transcript, 0x5580), 0x40), 1), success)mstore(add(transcript, 0x5600), 0x1eab999592385208b42a12dcc047763ee5cf12052eb4b0d9a7a286e043f3df9f) mstore(add(transcript, 0x5620), 0x18887eb3c646ffd4961226c91bac9661004798b1fc6a39c43f9a6d1d03767194)mstore(add(transcript, 0x5640), mload(add(transcript, 0x4b60)))success := and(eq(staticcall(gas(), 0x7, add(transcript, 0x5600), 0x60, add(transcript, 0x5600), 0x40), 1), success)mstore(add(transcript, 0x5660), mload(add(transcript, 0x5580))) mstore(add(transcript, 0x5680), mload(add(transcript, 0x55a0)))mstore(add(transcript, 0x56a0), mload(add(transcript, 0x5600))) mstore(add(transcript, 0x56c0), mload(add(transcript, 0x5620)))success := and(eq(staticcall(gas(), 0x6, add(transcript, 0x5660), 0x80, add(transcript, 0x5660), 0x40), 1), success)mstore(add(transcript, 0x56e0), 0x076806058b02e1b5e99b2f2e8eee613194f8df73481659f5938baacd64e4d7b8) mstore(add(transcript, 0x5700), 0x05ae81644b244dceea5350a84a79cbf0a199bbd6c1fad9887423f8c8048e6039)mstore(add(transcript, 0x5720), mload(add(transcript, 0x4b80)))success := and(eq(staticcall(gas(), 0x7, add(transcript, 0x56e0), 0x60, add(transcript, 0x56e0), 0x40), 1), success)mstore(add(transcript, 0x5740), mload(add(transcript, 0x5660))) mstore(add(transcript, 0x5760), mload(add(transcript, 0x5680)))mstore(add(transcript, 0x5780), mload(add(transcript, 0x56e0))) mstore(add(transcript, 0x57a0), mload(add(transcript, 0x5700)))success := and(eq(staticcall(gas(), 0x6, add(transcript, 0x5740), 0x80, add(transcript, 0x5740), 0x40), 1), success)mstore(add(transcript, 0x57c0), 0x0438873fc00231ae527efcf74d9092f5059dfa05275cbe7381e948fe3b7b61d8) mstore(add(transcript, 0x57e0), 0x03c0f314c93403792ba86ec3841b3080f2f7cd092c6bcb632cdbc2122a13cdef)mstore(add(transcript, 0x5800), mload(add(transcript, 0x4ba0)))success := and(eq(staticcall(gas(), 0x7, add(transcript, 0x57c0), 0x60, add(transcript, 0x57c0), 0x40), 1), success)mstore(add(transcript, 0x5820), mload(add(transcript, 0x5740))) mstore(add(transcript, 0x5840), mload(add(transcript, 0x5760)))mstore(add(transcript, 0x5860), mload(add(transcript, 0x57c0))) mstore(add(transcript, 0x5880), mload(add(transcript, 0x57e0)))success := and(eq(staticcall(gas(), 0x6, add(transcript, 0x5820), 0x80, add(transcript, 0x5820), 0x40), 1), success)mstore(add(transcript, 0x58a0), 0x2b12b132a6261ad60249147599cae0d5b3547e510deabba65526d130f04f2402) mstore(add(transcript, 0x58c0), 0x13e3c02ce00f6b7e00d5b099eda5f1155956ae83899a2d59cefcb00dad66a7f6)mstore(add(transcript, 0x58e0), mload(add(transcript, 0x4bc0)))success := and(eq(staticcall(gas(), 0x7, add(transcript, 0x58a0), 0x60, add(transcript, 0x58a0), 0x40), 1), success)mstore(add(transcript, 0x5900), mload(add(transcript, 0x5820))) mstore(add(transcript, 0x5920), mload(add(transcript, 0x5840)))mstore(add(transcript, 0x5940), mload(add(transcript, 0x58a0))) mstore(add(transcript, 0x5960), mload(add(transcript, 0x58c0)))success := and(eq(staticcall(gas(), 0x6, add(transcript, 0x5900), 0x80, add(transcript, 0x5900), 0x40), 1), success)mstore(add(transcript, 0x5980), 0x0ed83370277e0fd35e8258dd5630ce0ce3491fdf7e166d293745635b0cc4eba5) mstore(add(transcript, 0x59a0), 0x03994e7e77ad58072b78757700ab0b79247f2e462b0cf6a5a18b607414c095db)mstore(add(transcript, 0x59c0), mload(add(transcript, 0x4be0)))success := and(eq(staticcall(gas(), 0x7, add(transcript, 0x5980), 0x60, add(transcript, 0x5980), 0x40), 1), success)mstore(add(transcript, 0x59e0), mload(add(transcript, 0x5900))) mstore(add(transcript, 0x5a00), mload(add(transcript, 0x5920)))mstore(add(transcript, 0x5a20), mload(add(transcript, 0x5980))) mstore(add(transcript, 0x5a40), mload(add(transcript, 0x59a0)))success := and(eq(staticcall(gas(), 0x6, add(transcript, 0x59e0), 0x80, add(transcript, 0x59e0), 0x40), 1), success)mstore(add(transcript, 0x5a60), 0x23ba4bc49baab563e9d5bf07da231a4496d35b6bc782dbbd76135fe44c31cbcf) mstore(add(transcript, 0x5a80), 0x28b88097e7edf1b2904f6c25c1e30ca0e38c74a52125b2995c4cf0a750683854)mstore(add(transcript, 0x5aa0), mload(add(transcript, 0x4c00)))success := and(eq(staticcall(gas(), 0x7, add(transcript, 0x5a60), 0x60, add(transcript, 0x5a60), 0x40), 1), success)mstore(add(transcript, 0x5ac0), mload(add(transcript, 0x59e0))) mstore(add(transcript, 0x5ae0), mload(add(transcript, 0x5a00)))mstore(add(transcript, 0x5b00), mload(add(transcript, 0x5a60))) mstore(add(transcript, 0x5b20), mload(add(transcript, 0x5a80)))success := and(eq(staticcall(gas(), 0x6, add(transcript, 0x5ac0), 0x80, add(transcript, 0x5ac0), 0x40), 1), success)mstore(add(transcript, 0x5b40), 0x17dcbba0bbe2d24566de620998f62ff54ea5302ada6aeb18028b61b4dc19bbd3) mstore(add(transcript, 0x5b60), 0x0e1f908007a5c2cc934426c26de7206c53fa10c79d63ef52ee91bfb568c72fbd)mstore(add(transcript, 0x5b80), mload(add(transcript, 0x4c20)))success := and(eq(staticcall(gas(), 0x7, add(transcript, 0x5b40), 0x60, add(transcript, 0x5b40), 0x40), 1), success)mstore(add(transcript, 0x5ba0), mload(add(transcript, 0x5ac0))) mstore(add(transcript, 0x5bc0), mload(add(transcript, 0x5ae0)))mstore(add(transcript, 0x5be0), mload(add(transcript, 0x5b40))) mstore(add(transcript, 0x5c00), mload(add(transcript, 0x5b60)))success := and(eq(staticcall(gas(), 0x6, add(transcript, 0x5ba0), 0x80, add(transcript, 0x5ba0), 0x40), 1), success)mstore(add(transcript, 0x5c20), 0x2c7823f99cb2adb716becb960914ffa1d6e2f257875fdd03c2bee52ef571d41e) mstore(add(transcript, 0x5c40), 0x09ab83ebc6803e65b2e6831c8706d9a134f6b1cf6f6ec5c2697afee095320ee1)mstore(add(transcript, 0x5c60), mload(add(transcript, 0x4c40)))success := and(eq(staticcall(gas(), 0x7, add(transcript, 0x5c20), 0x60, add(transcript, 0x5c20), 0x40), 1), success)mstore(add(transcript, 0x5c80), mload(add(transcript, 0x5ba0))) mstore(add(transcript, 0x5ca0), mload(add(transcript, 0x5bc0)))mstore(add(transcript, 0x5cc0), mload(add(transcript, 0x5c20))) mstore(add(transcript, 0x5ce0), mload(add(transcript, 0x5c40)))success := and(eq(staticcall(gas(), 0x6, add(transcript, 0x5c80), 0x80, add(transcript, 0x5c80), 0x40), 1), success)mstore(add(transcript, 0x5d00), 0x2dc12c205152bfacc354c838225571172c215e01a64de6a2ca6665c70937ac58) mstore(add(transcript, 0x5d20), 0x0449eb529f0038a2bd2f6c0b6213a70488b69b479d90a953579e4c9d45621b85)mstore(add(transcript, 0x5d40), mload(add(transcript, 0x4c60)))success := and(eq(staticcall(gas(), 0x7, add(transcript, 0x5d00), 0x60, add(transcript, 0x5d00), 0x40), 1), success)mstore(add(transcript, 0x5d60), mload(add(transcript, 0x5c80))) mstore(add(transcript, 0x5d80), mload(add(transcript, 0x5ca0)))mstore(add(transcript, 0x5da0), mload(add(transcript, 0x5d00))) mstore(add(transcript, 0x5dc0), mload(add(transcript, 0x5d20)))success := and(eq(staticcall(gas(), 0x6, add(transcript, 0x5d60), 0x80, add(transcript, 0x5d60), 0x40), 1), success)mstore(add(transcript, 0x5de0), 0x09a2ab3913a4770fd50f6ba9ec4ce0b3178b54d7bfca5b42fe7e8e1ce5cf60a9) mstore(add(transcript, 0x5e00), 0x13ec31f94026569c4bb76e2c4ceaa3e6525438ffb4a7a69cde61f6d77b93bad7)mstore(add(transcript, 0x5e20), mload(add(transcript, 0x4c80)))success := and(eq(staticcall(gas(), 0x7, add(transcript, 0x5de0), 0x60, add(transcript, 0x5de0), 0x40), 1), success)mstore(add(transcript, 0x5e40), mload(add(transcript, 0x5d60))) mstore(add(transcript, 0x5e60), mload(add(transcript, 0x5d80)))mstore(add(transcript, 0x5e80), mload(add(transcript, 0x5de0))) mstore(add(transcript, 0x5ea0), mload(add(transcript, 0x5e00)))success := and(eq(staticcall(gas(), 0x6, add(transcript, 0x5e40), 0x80, add(transcript, 0x5e40), 0x40), 1), success)mstore(add(transcript, 0x5ec0), 0x0743ea40f14084db2673217283aa053f986896ee7c181f52118442e99c452974) mstore(add(transcript, 0x5ee0), 0x0203e3493a2594ece57d22cc75dd081ac68271ec7c758153cfd2152bfb5c19e3)mstore(add(transcript, 0x5f00), mload(add(transcript, 0x4ca0)))success := and(eq(staticcall(gas(), 0x7, add(transcript, 0x5ec0), 0x60, add(transcript, 0x5ec0), 0x40), 1), success)mstore(add(transcript, 0x5f20), mload(add(transcript, 0x5e40))) mstore(add(transcript, 0x5f40), mload(add(transcript, 0x5e60)))mstore(add(transcript, 0x5f60), mload(add(transcript, 0x5ec0))) mstore(add(transcript, 0x5f80), mload(add(transcript, 0x5ee0)))success := and(eq(staticcall(gas(), 0x6, add(transcript, 0x5f20), 0x80, add(transcript, 0x5f20), 0x40), 1), success)mstore(add(transcript, 0x5fa0), 0x02a9a77d8e6f6fd89f33ba56fe45f6f5c907e50ba28cdd4198acfb562eb21103) mstore(add(transcript, 0x5fc0), 0x23abee68d83c2d6632576e5e95ca01a61e5e3e6d9438c2ac6428c8b6edfe2aba)mstore(add(transcript, 0x5fe0), mload(add(transcript, 0x4cc0)))success := and(eq(staticcall(gas(), 0x7, add(transcript, 0x5fa0), 0x60, add(transcript, 0x5fa0), 0x40), 1), success)mstore(add(transcript, 0x6000), mload(add(transcript, 0x5f20))) mstore(add(transcript, 0x6020), mload(add(transcript, 0x5f40)))mstore(add(transcript, 0x6040), mload(add(transcript, 0x5fa0))) mstore(add(transcript, 0x6060), mload(add(transcript, 0x5fc0)))success := and(eq(staticcall(gas(), 0x6, add(transcript, 0x6000), 0x80, add(transcript, 0x6000), 0x40), 1), success)mstore(add(transcript, 0x6080), 0x2cf36d36bfacbe1a8d8c626aefa75ac0feb46b725989a8e6fabed074bf9f6af7) mstore(add(transcript, 0x60a0), 0x060fded3ed5ec56d9e0064d73c472245a4c4b6ec371e9808692bf7f259d3b730)mstore(add(transcript, 0x60c0), mload(add(transcript, 0x4ce0)))success := and(eq(staticcall(gas(), 0x7, add(transcript, 0x6080), 0x60, add(transcript, 0x6080), 0x40), 1), success)mstore(add(transcript, 0x60e0), mload(add(transcript, 0x6000))) mstore(add(transcript, 0x6100), mload(add(transcript, 0x6020)))mstore(add(transcript, 0x6120), mload(add(transcript, 0x6080))) mstore(add(transcript, 0x6140), mload(add(transcript, 0x60a0)))success := and(eq(staticcall(gas(), 0x6, add(transcript, 0x60e0), 0x80, add(transcript, 0x60e0), 0x40), 1), success)mstore(add(transcript, 0x6160), mload(add(transcript, 0x440))) mstore(add(transcript, 0x6180), mload(add(transcript, 0x460)))mstore(add(transcript, 0x61a0), mload(add(transcript, 0x4d00)))success := and(eq(staticcall(gas(), 0x7, add(transcript, 0x6160), 0x60, add(transcript, 0x6160), 0x40), 1), success)mstore(add(transcript, 0x61c0), mload(add(transcript, 0x60e0))) mstore(add(transcript, 0x61e0), mload(add(transcript, 0x6100)))mstore(add(transcript, 0x6200), mload(add(transcript, 0x6160))) mstore(add(transcript, 0x6220), mload(add(transcript, 0x6180)))success := and(eq(staticcall(gas(), 0x6, add(transcript, 0x61c0), 0x80, add(transcript, 0x61c0), 0x40), 1), success)mstore(add(transcript, 0x6240), mload(add(transcript, 0x480))) mstore(add(transcript, 0x6260), mload(add(transcript, 0x4a0)))mstore(add(transcript, 0x6280), mload(add(transcript, 0x4d20)))success := and(eq(staticcall(gas(), 0x7, add(transcript, 0x6240), 0x60, add(transcript, 0x6240), 0x40), 1), success)mstore(add(transcript, 0x62a0), mload(add(transcript, 0x61c0))) mstore(add(transcript, 0x62c0), mload(add(transcript, 0x61e0)))mstore(add(transcript, 0x62e0), mload(add(transcript, 0x6240))) mstore(add(transcript, 0x6300), mload(add(transcript, 0x6260)))success := and(eq(staticcall(gas(), 0x6, add(transcript, 0x62a0), 0x80, add(transcript, 0x62a0), 0x40), 1), success)mstore(add(transcript, 0x6320), mload(add(transcript, 0x4c0))) mstore(add(transcript, 0x6340), mload(add(transcript, 0x4e0)))mstore(add(transcript, 0x6360), mload(add(transcript, 0x4d40)))success := and(eq(staticcall(gas(), 0x7, add(transcript, 0x6320), 0x60, add(transcript, 0x6320), 0x40), 1), success)mstore(add(transcript, 0x6380), mload(add(transcript, 0x62a0))) mstore(add(transcript, 0x63a0), mload(add(transcript, 0x62c0)))mstore(add(transcript, 0x63c0), mload(add(transcript, 0x6320))) mstore(add(transcript, 0x63e0), mload(add(transcript, 0x6340)))success := and(eq(staticcall(gas(), 0x6, add(transcript, 0x6380), 0x80, add(transcript, 0x6380), 0x40), 1), success)mstore(add(transcript, 0x6400), mload(add(transcript, 0x500))) mstore(add(transcript, 0x6420), mload(add(transcript, 0x520)))mstore(add(transcript, 0x6440), mload(add(transcript, 0x4d60)))success := and(eq(staticcall(gas(), 0x7, add(transcript, 0x6400), 0x60, add(transcript, 0x6400), 0x40), 1), success)mstore(add(transcript, 0x6460), mload(add(transcript, 0x6380))) mstore(add(transcript, 0x6480), mload(add(transcript, 0x63a0)))mstore(add(transcript, 0x64a0), mload(add(transcript, 0x6400))) mstore(add(transcript, 0x64c0), mload(add(transcript, 0x6420)))success := and(eq(staticcall(gas(), 0x6, add(transcript, 0x6460), 0x80, add(transcript, 0x6460), 0x40), 1), success)mstore(add(transcript, 0x64e0), mload(add(transcript, 0x540))) mstore(add(transcript, 0x6500), mload(add(transcript, 0x560)))mstore(add(transcript, 0x6520), mload(add(transcript, 0x4d80)))success := and(eq(staticcall(gas(), 0x7, add(transcript, 0x64e0), 0x60, add(transcript, 0x64e0), 0x40), 1), success)mstore(add(transcript, 0x6540), mload(add(transcript, 0x6460))) mstore(add(transcript, 0x6560), mload(add(transcript, 0x6480)))mstore(add(transcript, 0x6580), mload(add(transcript, 0x64e0))) mstore(add(transcript, 0x65a0), mload(add(transcript, 0x6500)))success := and(eq(staticcall(gas(), 0x6, add(transcript, 0x6540), 0x80, add(transcript, 0x6540), 0x40), 1), success)mstore(add(transcript, 0x65c0), mload(add(transcript, 0x3a0))) mstore(add(transcript, 0x65e0), mload(add(transcript, 0x3c0)))mstore(add(transcript, 0x6600), mload(add(transcript, 0x4da0)))success := and(eq(staticcall(gas(), 0x7, add(transcript, 0x65c0), 0x60, add(transcript, 0x65c0), 0x40), 1), success)mstore(add(transcript, 0x6620), mload(add(transcript, 0x6540))) mstore(add(transcript, 0x6640), mload(add(transcript, 0x6560)))mstore(add(transcript, 0x6660), mload(add(transcript, 0x65c0))) mstore(add(transcript, 0x6680), mload(add(transcript, 0x65e0)))success := and(eq(staticcall(gas(), 0x6, add(transcript, 0x6620), 0x80, add(transcript, 0x6620), 0x40), 1), success)mstore(add(transcript, 0x66a0), mload(add(transcript, 0xac0))) mstore(add(transcript, 0x66c0), mload(add(transcript, 0xae0)))mstore(add(transcript, 0x66e0), sub(f_q, mload(add(transcript, 0x4de0))))success := and(eq(staticcall(gas(), 0x7, add(transcript, 0x66a0), 0x60, add(transcript, 0x66a0), 0x40), 1), success)mstore(add(transcript, 0x6700), mload(add(transcript, 0x6620))) mstore(add(transcript, 0x6720), mload(add(transcript, 0x6640)))mstore(add(transcript, 0x6740), mload(add(transcript, 0x66a0))) mstore(add(transcript, 0x6760), mload(add(transcript, 0x66c0)))success := and(eq(staticcall(gas(), 0x6, add(transcript, 0x6700), 0x80, add(transcript, 0x6700), 0x40), 1), success)mstore(add(transcript, 0x6780), mload(add(transcript, 0xb60))) mstore(add(transcript, 0x67a0), mload(add(transcript, 0xb80)))mstore(add(transcript, 0x67c0), mload(add(transcript, 0x4e00)))success := and(eq(staticcall(gas(), 0x7, add(transcript, 0x6780), 0x60, add(transcript, 0x6780), 0x40), 1), success)mstore(add(transcript, 0x67e0), mload(add(transcript, 0x6700))) mstore(add(transcript, 0x6800), mload(add(transcript, 0x6720)))mstore(add(transcript, 0x6820), mload(add(transcript, 0x6780))) mstore(add(transcript, 0x6840), mload(add(transcript, 0x67a0)))success := and(eq(staticcall(gas(), 0x6, add(transcript, 0x67e0), 0x80, add(transcript, 0x67e0), 0x40), 1), success)mstore(add(transcript, 0x6860), mload(add(transcript, 0x67e0))) mstore(add(transcript, 0x6880), mload(add(transcript, 0x6800)))mstore(add(transcript, 0x68a0), 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2) mstore(add(transcript, 0x68c0), 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed) mstore(add(transcript, 0x68e0), 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b) mstore(add(transcript, 0x6900), 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa)mstore(add(transcript, 0x6920), mload(add(transcript, 0xb60))) mstore(add(transcript, 0x6940), mload(add(transcript, 0xb80)))mstore(add(transcript, 0x6960), 0x26186a2d65ee4d2f9c9a5b91f86597d35f192cd120caf7e935d8443d1938e23d) mstore(add(transcript, 0x6980), 0x30441fd1b5d3370482c42152a8899027716989a6996c2535bc9f7fee8aaef79e) mstore(add(transcript, 0x69a0), 0x16f363f103c80d7bbc8ad3c6867e0822bbc6000be91a4689755c7df40221c145) mstore(add(transcript, 0x69c0), 0x2b1cbb3e521edf5a622d82762a44a5e63f1e50b332d71154a4a7958d6011deff)success := and(eq(staticcall(gas(), 0x8, add(transcript, 0x6860), 0x180, add(transcript, 0x6860), 0x20), 1), success)success := and(eq(mload(add(transcript, 0x6860)), 1), success)} return success; } } diff --git a/contracts/src/SolvencyVerifier.yul b/contracts/src/SolvencyVerifier.yul deleted file mode 100644 index d72d7d0e..00000000 --- a/contracts/src/SolvencyVerifier.yul +++ /dev/null @@ -1,1418 +0,0 @@ - - object "plonk_verifier" { - code { - function allocate(size) -> ptr { - ptr := mload(0x40) - if eq(ptr, 0) { ptr := 0x60 } - mstore(0x40, add(ptr, size)) - } - let size := datasize("Runtime") - let offset := allocate(size) - datacopy(offset, dataoffset("Runtime"), size) - return(offset, size) - } - object "Runtime" { - code { - let success:bool := true - let f_p := 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47 - let f_q := 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001 - function validate_ec_point(x, y) -> valid:bool { - { - let x_lt_p:bool := lt(x, 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47) - let y_lt_p:bool := 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:bool := eq(x_cube_plus_3, y_square) - valid := and(valid, is_affine) - } - } - mstore(0x20, mod(calldataload(0x0), f_q)) -mstore(0x40, mod(calldataload(0x20), f_q)) -mstore(0x60, mod(calldataload(0x40), f_q)) -mstore(0x0, 308492134225063399814226381520525808113815168934209933683689315739401114858) - - { - let x := calldataload(0x60) - mstore(0x80, x) - let y := calldataload(0x80) - mstore(0xa0, y) - success := and(validate_ec_point(x, y), success) - } - - { - let x := calldataload(0xa0) - mstore(0xc0, x) - let y := calldataload(0xc0) - mstore(0xe0, y) - success := and(validate_ec_point(x, y), success) - } - - { - let x := calldataload(0xe0) - mstore(0x100, x) - let y := calldataload(0x100) - mstore(0x120, y) - success := and(validate_ec_point(x, y), success) - } -mstore(0x140, keccak256(0x0, 320)) -{ - let hash := mload(0x140) - mstore(0x160, mod(hash, f_q)) - mstore(0x180, hash) - } - - { - let x := calldataload(0x120) - mstore(0x1a0, x) - let y := calldataload(0x140) - mstore(0x1c0, y) - success := and(validate_ec_point(x, y), success) - } - - { - let x := calldataload(0x160) - mstore(0x1e0, x) - let y := calldataload(0x180) - mstore(0x200, y) - success := and(validate_ec_point(x, y), success) - } -mstore(0x220, keccak256(0x180, 160)) -{ - let hash := mload(0x220) - mstore(0x240, mod(hash, f_q)) - mstore(0x260, hash) - } -mstore8(640, 1) -mstore(0x280, keccak256(0x260, 33)) -{ - let hash := mload(0x280) - mstore(0x2a0, mod(hash, f_q)) - mstore(0x2c0, hash) - } - - { - let x := calldataload(0x1a0) - mstore(0x2e0, x) - let y := calldataload(0x1c0) - mstore(0x300, y) - success := and(validate_ec_point(x, y), success) - } - - { - let x := calldataload(0x1e0) - mstore(0x320, x) - let y := calldataload(0x200) - mstore(0x340, y) - success := and(validate_ec_point(x, y), success) - } - - { - let x := calldataload(0x220) - mstore(0x360, x) - let y := calldataload(0x240) - mstore(0x380, y) - success := and(validate_ec_point(x, y), success) - } - - { - let x := calldataload(0x260) - mstore(0x3a0, x) - let y := calldataload(0x280) - mstore(0x3c0, y) - success := and(validate_ec_point(x, y), success) - } -mstore(0x3e0, keccak256(0x2c0, 288)) -{ - let hash := mload(0x3e0) - mstore(0x400, mod(hash, f_q)) - mstore(0x420, hash) - } - - { - let x := calldataload(0x2a0) - mstore(0x440, x) - let y := calldataload(0x2c0) - mstore(0x460, y) - success := and(validate_ec_point(x, y), success) - } - - { - let x := calldataload(0x2e0) - mstore(0x480, x) - let y := calldataload(0x300) - mstore(0x4a0, y) - success := and(validate_ec_point(x, y), success) - } - - { - let x := calldataload(0x320) - mstore(0x4c0, x) - let y := calldataload(0x340) - mstore(0x4e0, y) - success := and(validate_ec_point(x, y), success) - } - - { - let x := calldataload(0x360) - mstore(0x500, x) - let y := calldataload(0x380) - mstore(0x520, y) - success := and(validate_ec_point(x, y), success) - } - - { - let x := calldataload(0x3a0) - mstore(0x540, x) - let y := calldataload(0x3c0) - mstore(0x560, y) - success := and(validate_ec_point(x, y), success) - } -mstore(0x580, keccak256(0x420, 352)) -{ - let hash := mload(0x580) - mstore(0x5a0, mod(hash, f_q)) - mstore(0x5c0, hash) - } -mstore(0x5e0, mod(calldataload(0x3e0), f_q)) -mstore(0x600, mod(calldataload(0x400), f_q)) -mstore(0x620, mod(calldataload(0x420), f_q)) -mstore(0x640, mod(calldataload(0x440), f_q)) -mstore(0x660, mod(calldataload(0x460), f_q)) -mstore(0x680, mod(calldataload(0x480), f_q)) -mstore(0x6a0, mod(calldataload(0x4a0), f_q)) -mstore(0x6c0, mod(calldataload(0x4c0), f_q)) -mstore(0x6e0, mod(calldataload(0x4e0), f_q)) -mstore(0x700, mod(calldataload(0x500), f_q)) -mstore(0x720, mod(calldataload(0x520), f_q)) -mstore(0x740, mod(calldataload(0x540), f_q)) -mstore(0x760, mod(calldataload(0x560), f_q)) -mstore(0x780, mod(calldataload(0x580), f_q)) -mstore(0x7a0, mod(calldataload(0x5a0), f_q)) -mstore(0x7c0, mod(calldataload(0x5c0), f_q)) -mstore(0x7e0, mod(calldataload(0x5e0), f_q)) -mstore(0x800, mod(calldataload(0x600), f_q)) -mstore(0x820, mod(calldataload(0x620), f_q)) -mstore(0x840, mod(calldataload(0x640), f_q)) -mstore(0x860, mod(calldataload(0x660), f_q)) -mstore(0x880, mod(calldataload(0x680), f_q)) -mstore(0x8a0, mod(calldataload(0x6a0), f_q)) -mstore(0x8c0, mod(calldataload(0x6c0), f_q)) -mstore(0x8e0, mod(calldataload(0x6e0), f_q)) -mstore(0x900, mod(calldataload(0x700), f_q)) -mstore(0x920, mod(calldataload(0x720), f_q)) -mstore(0x940, mod(calldataload(0x740), f_q)) -mstore(0x960, mod(calldataload(0x760), f_q)) -mstore(0x980, mod(calldataload(0x780), f_q)) -mstore(0x9a0, mod(calldataload(0x7a0), f_q)) -mstore(0x9c0, mod(calldataload(0x7c0), f_q)) -mstore(0x9e0, mod(calldataload(0x7e0), f_q)) -mstore(0xa00, keccak256(0x5c0, 1088)) -{ - let hash := mload(0xa00) - mstore(0xa20, mod(hash, f_q)) - mstore(0xa40, hash) - } -mstore8(2656, 1) -mstore(0xa60, keccak256(0xa40, 33)) -{ - let hash := mload(0xa60) - mstore(0xa80, mod(hash, f_q)) - mstore(0xaa0, hash) - } - - { - let x := calldataload(0x800) - mstore(0xac0, x) - let y := calldataload(0x820) - mstore(0xae0, y) - success := and(validate_ec_point(x, y), success) - } -mstore(0xb00, keccak256(0xaa0, 96)) -{ - let hash := mload(0xb00) - mstore(0xb20, mod(hash, f_q)) - mstore(0xb40, hash) - } - - { - let x := calldataload(0x840) - mstore(0xb60, x) - let y := calldataload(0x860) - mstore(0xb80, y) - success := and(validate_ec_point(x, y), success) - } -mstore(0xba0, mulmod(mload(0x5a0), mload(0x5a0), f_q)) -mstore(0xbc0, mulmod(mload(0xba0), mload(0xba0), f_q)) -mstore(0xbe0, mulmod(mload(0xbc0), mload(0xbc0), f_q)) -mstore(0xc00, mulmod(mload(0xbe0), mload(0xbe0), f_q)) -mstore(0xc20, mulmod(mload(0xc00), mload(0xc00), f_q)) -mstore(0xc40, mulmod(mload(0xc20), mload(0xc20), f_q)) -mstore(0xc60, mulmod(mload(0xc40), mload(0xc40), f_q)) -mstore(0xc80, mulmod(mload(0xc60), mload(0xc60), f_q)) -mstore(0xca0, mulmod(mload(0xc80), mload(0xc80), f_q)) -mstore(0xcc0, mulmod(mload(0xca0), mload(0xca0), f_q)) -mstore(0xce0, addmod(mload(0xcc0), 21888242871839275222246405745257275088548364400416034343698204186575808495616, f_q)) -mstore(0xd00, mulmod(mload(0xce0), 21866867634659744680037180739646672280844703888306253060159436409049855557633, f_q)) -mstore(0xd20, mulmod(mload(0xd00), 9936069627611189518829255670237324269287146421271524553312532036927871056678, f_q)) -mstore(0xd40, addmod(mload(0x5a0), 11952173244228085703417150075019950819261217979144509790385672149647937438939, f_q)) -mstore(0xd60, mulmod(mload(0xd00), 1680739780407307830605919050682431078078760076686599579086116998224280619988, f_q)) -mstore(0xd80, addmod(mload(0x5a0), 20207503091431967391640486694574844010469604323729434764612087188351527875629, f_q)) -mstore(0xda0, mulmod(mload(0xd00), 14158528901797138466244491986759313854666262535363044392173788062030301470987, f_q)) -mstore(0xdc0, addmod(mload(0x5a0), 7729713970042136756001913758497961233882101865052989951524416124545507024630, f_q)) -mstore(0xde0, mulmod(mload(0xd00), 15699029810934084314820646074566828280617789951162923449200398535581206172418, f_q)) -mstore(0xe00, addmod(mload(0x5a0), 6189213060905190907425759670690446807930574449253110894497805650994602323199, f_q)) -mstore(0xe20, mulmod(mload(0xd00), 4260969412351770314333984243767775737437927068151180798236715529158398853173, f_q)) -mstore(0xe40, addmod(mload(0x5a0), 17627273459487504907912421501489499351110437332264853545461488657417409642444, f_q)) -mstore(0xe60, mulmod(mload(0xd00), 4925592601992654644734291590386747644864797672605745962807370354577123815907, f_q)) -mstore(0xe80, addmod(mload(0x5a0), 16962650269846620577512114154870527443683566727810288380890833831998684679710, f_q)) -mstore(0xea0, mulmod(mload(0xd00), 1, f_q)) -mstore(0xec0, addmod(mload(0x5a0), 21888242871839275222246405745257275088548364400416034343698204186575808495616, f_q)) -mstore(0xee0, mulmod(mload(0xd00), 19380560087801265747114831706136320509424814679569278834391540198888293317501, f_q)) -mstore(0xf00, addmod(mload(0x5a0), 2507682784038009475131574039120954579123549720846755509306663987687515178116, f_q)) -mstore(0xf20, mulmod(mload(0xd00), 6252951856119339508807713076978770803512896272623217303779254502899773638908, f_q)) -mstore(0xf40, addmod(mload(0x5a0), 15635291015719935713438692668278504285035468127792817039918949683676034856709, f_q)) -{ - let prod := mload(0xd40) - - prod := mulmod(mload(0xd80), prod, f_q) - mstore(0xf60, prod) - - prod := mulmod(mload(0xdc0), prod, f_q) - mstore(0xf80, prod) - - prod := mulmod(mload(0xe00), prod, f_q) - mstore(0xfa0, prod) - - prod := mulmod(mload(0xe40), prod, f_q) - mstore(0xfc0, prod) - - prod := mulmod(mload(0xe80), prod, f_q) - mstore(0xfe0, prod) - - prod := mulmod(mload(0xec0), prod, f_q) - mstore(0x1000, prod) - - prod := mulmod(mload(0xf00), prod, f_q) - mstore(0x1020, prod) - - prod := mulmod(mload(0xf40), prod, f_q) - mstore(0x1040, prod) - - prod := mulmod(mload(0xce0), prod, f_q) - mstore(0x1060, prod) - - } -mstore(0x10a0, 32) -mstore(0x10c0, 32) -mstore(0x10e0, 32) -mstore(0x1100, mload(0x1060)) -mstore(0x1120, 21888242871839275222246405745257275088548364400416034343698204186575808495615) -mstore(0x1140, 21888242871839275222246405745257275088548364400416034343698204186575808495617) -success := and(eq(staticcall(gas(), 0x5, 0x10a0, 0xc0, 0x1080, 0x20), 1), success) -{ - - let inv := mload(0x1080) - let v - - v := mload(0xce0) - mstore(3296, mulmod(mload(0x1040), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0xf40) - mstore(3904, mulmod(mload(0x1020), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0xf00) - mstore(3840, mulmod(mload(0x1000), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0xec0) - mstore(3776, mulmod(mload(0xfe0), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0xe80) - mstore(3712, mulmod(mload(0xfc0), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0xe40) - mstore(3648, mulmod(mload(0xfa0), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0xe00) - mstore(3584, mulmod(mload(0xf80), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0xdc0) - mstore(3520, mulmod(mload(0xf60), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0xd80) - mstore(3456, mulmod(mload(0xd40), inv, f_q)) - inv := mulmod(v, inv, f_q) - mstore(0xd40, inv) - - } -mstore(0x1160, mulmod(mload(0xd20), mload(0xd40), f_q)) -mstore(0x1180, mulmod(mload(0xd60), mload(0xd80), f_q)) -mstore(0x11a0, mulmod(mload(0xda0), mload(0xdc0), f_q)) -mstore(0x11c0, mulmod(mload(0xde0), mload(0xe00), f_q)) -mstore(0x11e0, mulmod(mload(0xe20), mload(0xe40), f_q)) -mstore(0x1200, mulmod(mload(0xe60), mload(0xe80), f_q)) -mstore(0x1220, mulmod(mload(0xea0), mload(0xec0), f_q)) -mstore(0x1240, mulmod(mload(0xee0), mload(0xf00), f_q)) -mstore(0x1260, mulmod(mload(0xf20), mload(0xf40), f_q)) -{ - let result := mulmod(mload(0x1220), mload(0x20), f_q) -result := addmod(mulmod(mload(0x1240), mload(0x40), f_q), result, f_q) -result := addmod(mulmod(mload(0x1260), mload(0x60), f_q), result, f_q) -mstore(4736, result) - } -mstore(0x12a0, addmod(mload(0x5e0), mload(0x720), f_q)) -mstore(0x12c0, mulmod(mload(0x12a0), mload(0x12a0), f_q)) -mstore(0x12e0, mulmod(mload(0x12c0), mload(0x12c0), f_q)) -mstore(0x1300, mulmod(mload(0x12a0), mload(0x12e0), f_q)) -mstore(0x1320, mulmod(mload(0x1300), 2910766817845651019878574839501801340070030115151021261302834310722729507541, f_q)) -mstore(0x1340, addmod(mload(0x600), mload(0x740), f_q)) -mstore(0x1360, mulmod(mload(0x1340), mload(0x1340), f_q)) -mstore(0x1380, mulmod(mload(0x1360), mload(0x1360), f_q)) -mstore(0x13a0, mulmod(mload(0x1340), mload(0x1380), f_q)) -mstore(0x13c0, mulmod(mload(0x13a0), 19727366863391167538122140361473584127147630672623100827934084310230022599144, f_q)) -mstore(0x13e0, addmod(mload(0x1320), mload(0x13c0), f_q)) -mstore(0x1400, addmod(mload(0x13e0), sub(f_q, mload(0x620)), f_q)) -mstore(0x1420, mulmod(mload(0x1400), mload(0x7a0), f_q)) -mstore(0x1440, mulmod(mload(0x400), mload(0x1420), f_q)) -mstore(0x1460, mulmod(mload(0x1300), 5776684794125549462448597414050232243778680302179439492664047328281728356345, f_q)) -mstore(0x1480, mulmod(mload(0x13a0), 8348174920934122550483593999453880006756108121341067172388445916328941978568, f_q)) -mstore(0x14a0, addmod(mload(0x1460), mload(0x1480), f_q)) -mstore(0x14c0, addmod(mload(0x14a0), sub(f_q, mload(0x640)), f_q)) -mstore(0x14e0, mulmod(mload(0x14c0), mload(0x7a0), f_q)) -mstore(0x1500, addmod(mload(0x1440), mload(0x14e0), f_q)) -mstore(0x1520, mulmod(mload(0x400), mload(0x1500), f_q)) -mstore(0x1540, addmod(mload(0x1300), sub(f_q, mload(0x660)), f_q)) -mstore(0x1560, mulmod(mload(0x1540), mload(0x7c0), f_q)) -mstore(0x1580, addmod(mload(0x1520), mload(0x1560), f_q)) -mstore(0x15a0, mulmod(mload(0x400), mload(0x1580), f_q)) -mstore(0x15c0, mulmod(mload(0x660), 2910766817845651019878574839501801340070030115151021261302834310722729507541, f_q)) -mstore(0x15e0, mulmod(mload(0x1340), 19727366863391167538122140361473584127147630672623100827934084310230022599144, f_q)) -mstore(0x1600, addmod(mload(0x15c0), mload(0x15e0), f_q)) -mstore(0x1620, addmod(mload(0x1600), mload(0x6e0), f_q)) -mstore(0x1640, mulmod(mload(0x1620), mload(0x1620), f_q)) -mstore(0x1660, mulmod(mload(0x1640), mload(0x1640), f_q)) -mstore(0x1680, mulmod(mload(0x1620), mload(0x1660), f_q)) -mstore(0x16a0, mulmod(mload(0x620), 8897705321156975119607866206188469715432233408805434913352778521345836531302, f_q)) -mstore(0x16c0, mulmod(mload(0x640), 13897810991298242824030978581179475767377101082166056046492926701399149797630, f_q)) -mstore(0x16e0, addmod(mload(0x16a0), mload(0x16c0), f_q)) -mstore(0x1700, addmod(mload(0x1680), sub(f_q, mload(0x16e0)), f_q)) -mstore(0x1720, mulmod(mload(0x1700), mload(0x7c0), f_q)) -mstore(0x1740, addmod(mload(0x15a0), mload(0x1720), f_q)) -mstore(0x1760, mulmod(mload(0x400), mload(0x1740), f_q)) -mstore(0x1780, mulmod(mload(0x660), 5776684794125549462448597414050232243778680302179439492664047328281728356345, f_q)) -mstore(0x17a0, mulmod(mload(0x1340), 8348174920934122550483593999453880006756108121341067172388445916328941978568, f_q)) -mstore(0x17c0, addmod(mload(0x1780), mload(0x17a0), f_q)) -mstore(0x17e0, addmod(mload(0x17c0), mload(0x700), f_q)) -mstore(0x1800, mulmod(mload(0x620), 7127083008168878795310303301757642617203533252990949589494537404444738046722, f_q)) -mstore(0x1820, mulmod(mload(0x640), 10251091711782631878897995303436082826711938358699127319815611151510940403902, f_q)) -mstore(0x1840, addmod(mload(0x1800), mload(0x1820), f_q)) -mstore(0x1860, addmod(mload(0x17e0), sub(f_q, mload(0x1840)), f_q)) -mstore(0x1880, mulmod(mload(0x1860), mload(0x7c0), f_q)) -mstore(0x18a0, addmod(mload(0x1760), mload(0x1880), f_q)) -mstore(0x18c0, mulmod(mload(0x400), mload(0x18a0), f_q)) -mstore(0x18e0, addmod(1, sub(f_q, mload(0x780)), f_q)) -mstore(0x1900, mulmod(mload(0x18e0), mload(0x780), f_q)) -mstore(0x1920, addmod(2, sub(f_q, mload(0x780)), f_q)) -mstore(0x1940, mulmod(mload(0x1920), mload(0x1900), f_q)) -mstore(0x1960, addmod(3, sub(f_q, mload(0x780)), f_q)) -mstore(0x1980, mulmod(mload(0x1960), mload(0x1940), f_q)) -mstore(0x19a0, addmod(mload(0x6a0), mload(0x5e0), f_q)) -mstore(0x19c0, addmod(mload(0x19a0), sub(f_q, mload(0x620)), f_q)) -mstore(0x19e0, mulmod(mload(0x19c0), mload(0x1980), f_q)) -mstore(0x1a00, addmod(mload(0x18c0), mload(0x19e0), f_q)) -mstore(0x1a20, mulmod(mload(0x400), mload(0x1a00), f_q)) -mstore(0x1a40, addmod(mload(0x680), sub(f_q, mload(0x640)), f_q)) -mstore(0x1a60, mulmod(mload(0x1a40), mload(0x1980), f_q)) -mstore(0x1a80, addmod(mload(0x1a20), mload(0x1a60), f_q)) -mstore(0x1aa0, mulmod(mload(0x400), mload(0x1a80), f_q)) -mstore(0x1ac0, mulmod(mload(0x1920), mload(0x780), f_q)) -mstore(0x1ae0, mulmod(mload(0x1960), mload(0x1ac0), f_q)) -mstore(0x1b00, addmod(4, sub(f_q, mload(0x780)), f_q)) -mstore(0x1b20, mulmod(mload(0x1b00), mload(0x1ae0), f_q)) -mstore(0x1b40, mulmod(mload(0x660), mload(0x1b20), f_q)) -mstore(0x1b60, addmod(1, sub(f_q, mload(0x660)), f_q)) -mstore(0x1b80, mulmod(mload(0x1b60), mload(0x1b40), f_q)) -mstore(0x1ba0, addmod(mload(0x1aa0), mload(0x1b80), f_q)) -mstore(0x1bc0, mulmod(mload(0x400), mload(0x1ba0), f_q)) -mstore(0x1be0, mulmod(2, mload(0x660), f_q)) -mstore(0x1c00, addmod(mload(0x600), sub(f_q, mload(0x5e0)), f_q)) -mstore(0x1c20, mulmod(mload(0x1c00), mload(0x1be0), f_q)) -mstore(0x1c40, addmod(mload(0x620), sub(f_q, mload(0x5e0)), f_q)) -mstore(0x1c60, addmod(mload(0x1c20), sub(f_q, mload(0x1c40)), f_q)) -mstore(0x1c80, addmod(mload(0x600), sub(f_q, mload(0x640)), f_q)) -mstore(0x1ca0, addmod(mload(0x1c60), sub(f_q, mload(0x1c80)), f_q)) -mstore(0x1cc0, mulmod(mload(0x1ca0), mload(0x1b20), f_q)) -mstore(0x1ce0, addmod(mload(0x1bc0), mload(0x1cc0), f_q)) -mstore(0x1d00, mulmod(mload(0x400), mload(0x1ce0), f_q)) -mstore(0x1d20, mulmod(mload(0x1960), mload(0x1900), f_q)) -mstore(0x1d40, mulmod(mload(0x1b00), mload(0x1d20), f_q)) -mstore(0x1d60, addmod(mload(0x5e0), mload(0x600), f_q)) -mstore(0x1d80, addmod(mload(0x1d60), sub(f_q, mload(0x660)), f_q)) -mstore(0x1da0, mulmod(mload(0x1d80), mload(0x1d40), f_q)) -mstore(0x1dc0, addmod(mload(0x1d00), mload(0x1da0), f_q)) -mstore(0x1de0, mulmod(mload(0x400), mload(0x1dc0), f_q)) -mstore(0x1e00, addmod(mload(0x1de0), mload(0x1da0), f_q)) -mstore(0x1e20, mulmod(mload(0x400), mload(0x1e00), f_q)) -mstore(0x1e40, mulmod(mload(0x1b00), mload(0x1940), f_q)) -mstore(0x1e60, addmod(mload(0x5e0), sub(f_q, mload(0x600)), f_q)) -mstore(0x1e80, addmod(mload(0x1e60), 5192296858534827628530496329220096, f_q)) -mstore(0x1ea0, addmod(mload(0x1e80), sub(f_q, mload(0x660)), f_q)) -mstore(0x1ec0, mulmod(mload(0x1ea0), mload(0x1e40), f_q)) -mstore(0x1ee0, addmod(mload(0x1e20), mload(0x1ec0), f_q)) -mstore(0x1f00, mulmod(mload(0x400), mload(0x1ee0), f_q)) -mstore(0x1f20, addmod(1, sub(f_q, mload(0x8c0)), f_q)) -mstore(0x1f40, mulmod(mload(0x1f20), mload(0x1220), f_q)) -mstore(0x1f60, addmod(mload(0x1f00), mload(0x1f40), f_q)) -mstore(0x1f80, mulmod(mload(0x400), mload(0x1f60), f_q)) -mstore(0x1fa0, mulmod(mload(0x920), mload(0x920), f_q)) -mstore(0x1fc0, addmod(mload(0x1fa0), sub(f_q, mload(0x920)), f_q)) -mstore(0x1fe0, mulmod(mload(0x1fc0), mload(0x1160), f_q)) -mstore(0x2000, addmod(mload(0x1f80), mload(0x1fe0), f_q)) -mstore(0x2020, mulmod(mload(0x400), mload(0x2000), f_q)) -mstore(0x2040, addmod(mload(0x920), sub(f_q, mload(0x900)), f_q)) -mstore(0x2060, mulmod(mload(0x2040), mload(0x1220), f_q)) -mstore(0x2080, addmod(mload(0x2020), mload(0x2060), f_q)) -mstore(0x20a0, mulmod(mload(0x400), mload(0x2080), f_q)) -mstore(0x20c0, addmod(1, sub(f_q, mload(0x1160)), f_q)) -mstore(0x20e0, addmod(mload(0x1180), mload(0x11a0), f_q)) -mstore(0x2100, addmod(mload(0x20e0), mload(0x11c0), f_q)) -mstore(0x2120, addmod(mload(0x2100), mload(0x11e0), f_q)) -mstore(0x2140, addmod(mload(0x2120), mload(0x1200), f_q)) -mstore(0x2160, addmod(mload(0x20c0), sub(f_q, mload(0x2140)), f_q)) -mstore(0x2180, mulmod(mload(0x800), mload(0x240), f_q)) -mstore(0x21a0, addmod(mload(0x6e0), mload(0x2180), f_q)) -mstore(0x21c0, addmod(mload(0x21a0), mload(0x2a0), f_q)) -mstore(0x21e0, mulmod(mload(0x820), mload(0x240), f_q)) -mstore(0x2200, addmod(mload(0x5e0), mload(0x21e0), f_q)) -mstore(0x2220, addmod(mload(0x2200), mload(0x2a0), f_q)) -mstore(0x2240, mulmod(mload(0x2220), mload(0x21c0), f_q)) -mstore(0x2260, mulmod(mload(0x840), mload(0x240), f_q)) -mstore(0x2280, addmod(mload(0x600), mload(0x2260), f_q)) -mstore(0x22a0, addmod(mload(0x2280), mload(0x2a0), f_q)) -mstore(0x22c0, mulmod(mload(0x22a0), mload(0x2240), f_q)) -mstore(0x22e0, mulmod(mload(0x860), mload(0x240), f_q)) -mstore(0x2300, addmod(mload(0x700), mload(0x22e0), f_q)) -mstore(0x2320, addmod(mload(0x2300), mload(0x2a0), f_q)) -mstore(0x2340, mulmod(mload(0x2320), mload(0x22c0), f_q)) -mstore(0x2360, mulmod(mload(0x2340), mload(0x8e0), f_q)) -mstore(0x2380, mulmod(1, mload(0x240), f_q)) -mstore(0x23a0, mulmod(mload(0x5a0), mload(0x2380), f_q)) -mstore(0x23c0, addmod(mload(0x6e0), mload(0x23a0), f_q)) -mstore(0x23e0, addmod(mload(0x23c0), mload(0x2a0), f_q)) -mstore(0x2400, mulmod(4131629893567559867359510883348571134090853742863529169391034518566172092834, mload(0x240), f_q)) -mstore(0x2420, mulmod(mload(0x5a0), mload(0x2400), f_q)) -mstore(0x2440, addmod(mload(0x5e0), mload(0x2420), f_q)) -mstore(0x2460, addmod(mload(0x2440), mload(0x2a0), f_q)) -mstore(0x2480, mulmod(mload(0x2460), mload(0x23e0), f_q)) -mstore(0x24a0, mulmod(8910878055287538404433155982483128285667088683464058436815641868457422632747, mload(0x240), f_q)) -mstore(0x24c0, mulmod(mload(0x5a0), mload(0x24a0), f_q)) -mstore(0x24e0, addmod(mload(0x600), mload(0x24c0), f_q)) -mstore(0x2500, addmod(mload(0x24e0), mload(0x2a0), f_q)) -mstore(0x2520, mulmod(mload(0x2500), mload(0x2480), f_q)) -mstore(0x2540, mulmod(11166246659983828508719468090013646171463329086121580628794302409516816350802, mload(0x240), f_q)) -mstore(0x2560, mulmod(mload(0x5a0), mload(0x2540), f_q)) -mstore(0x2580, addmod(mload(0x700), mload(0x2560), f_q)) -mstore(0x25a0, addmod(mload(0x2580), mload(0x2a0), f_q)) -mstore(0x25c0, mulmod(mload(0x25a0), mload(0x2520), f_q)) -mstore(0x25e0, mulmod(mload(0x25c0), mload(0x8c0), f_q)) -mstore(0x2600, addmod(mload(0x2360), sub(f_q, mload(0x25e0)), f_q)) -mstore(0x2620, mulmod(mload(0x2600), mload(0x2160), f_q)) -mstore(0x2640, addmod(mload(0x20a0), mload(0x2620), f_q)) -mstore(0x2660, mulmod(mload(0x400), mload(0x2640), f_q)) -mstore(0x2680, mulmod(mload(0x880), mload(0x240), f_q)) -mstore(0x26a0, addmod(mload(0x660), mload(0x2680), f_q)) -mstore(0x26c0, addmod(mload(0x26a0), mload(0x2a0), f_q)) -mstore(0x26e0, mulmod(mload(0x8a0), mload(0x240), f_q)) -mstore(0x2700, addmod(mload(0x1280), mload(0x26e0), f_q)) -mstore(0x2720, addmod(mload(0x2700), mload(0x2a0), f_q)) -mstore(0x2740, mulmod(mload(0x2720), mload(0x26c0), f_q)) -mstore(0x2760, mulmod(mload(0x2740), mload(0x940), f_q)) -mstore(0x2780, mulmod(284840088355319032285349970403338060113257071685626700086398481893096618818, mload(0x240), f_q)) -mstore(0x27a0, mulmod(mload(0x5a0), mload(0x2780), f_q)) -mstore(0x27c0, addmod(mload(0x660), mload(0x27a0), f_q)) -mstore(0x27e0, addmod(mload(0x27c0), mload(0x2a0), f_q)) -mstore(0x2800, mulmod(21134065618345176623193549882539580312263652408302468683943992798037078993309, mload(0x240), f_q)) -mstore(0x2820, mulmod(mload(0x5a0), mload(0x2800), f_q)) -mstore(0x2840, addmod(mload(0x1280), mload(0x2820), f_q)) -mstore(0x2860, addmod(mload(0x2840), mload(0x2a0), f_q)) -mstore(0x2880, mulmod(mload(0x2860), mload(0x27e0), f_q)) -mstore(0x28a0, mulmod(mload(0x2880), mload(0x920), f_q)) -mstore(0x28c0, addmod(mload(0x2760), sub(f_q, mload(0x28a0)), f_q)) -mstore(0x28e0, mulmod(mload(0x28c0), mload(0x2160), f_q)) -mstore(0x2900, addmod(mload(0x2660), mload(0x28e0), f_q)) -mstore(0x2920, mulmod(mload(0x400), mload(0x2900), f_q)) -mstore(0x2940, addmod(1, sub(f_q, mload(0x960)), f_q)) -mstore(0x2960, mulmod(mload(0x2940), mload(0x1220), f_q)) -mstore(0x2980, addmod(mload(0x2920), mload(0x2960), f_q)) -mstore(0x29a0, mulmod(mload(0x400), mload(0x2980), f_q)) -mstore(0x29c0, mulmod(mload(0x960), mload(0x960), f_q)) -mstore(0x29e0, addmod(mload(0x29c0), sub(f_q, mload(0x960)), f_q)) -mstore(0x2a00, mulmod(mload(0x29e0), mload(0x1160), f_q)) -mstore(0x2a20, addmod(mload(0x29a0), mload(0x2a00), f_q)) -mstore(0x2a40, mulmod(mload(0x400), mload(0x2a20), f_q)) -mstore(0x2a60, addmod(mload(0x9a0), mload(0x240), f_q)) -mstore(0x2a80, mulmod(mload(0x2a60), mload(0x980), f_q)) -mstore(0x2aa0, addmod(mload(0x9e0), mload(0x2a0), f_q)) -mstore(0x2ac0, mulmod(mload(0x2aa0), mload(0x2a80), f_q)) -mstore(0x2ae0, mulmod(256, mload(0x6c0), f_q)) -mstore(0x2b00, addmod(mload(0x660), sub(f_q, mload(0x2ae0)), f_q)) -mstore(0x2b20, mulmod(mload(0x2b00), mload(0x760), f_q)) -mstore(0x2b40, addmod(mload(0x2b20), mload(0x240), f_q)) -mstore(0x2b60, mulmod(mload(0x2b40), mload(0x960), f_q)) -mstore(0x2b80, addmod(mload(0x720), mload(0x2a0), f_q)) -mstore(0x2ba0, mulmod(mload(0x2b80), mload(0x2b60), f_q)) -mstore(0x2bc0, addmod(mload(0x2ac0), sub(f_q, mload(0x2ba0)), f_q)) -mstore(0x2be0, mulmod(mload(0x2bc0), mload(0x2160), f_q)) -mstore(0x2c00, addmod(mload(0x2a40), mload(0x2be0), f_q)) -mstore(0x2c20, mulmod(mload(0x400), mload(0x2c00), f_q)) -mstore(0x2c40, addmod(mload(0x9a0), sub(f_q, mload(0x9e0)), f_q)) -mstore(0x2c60, mulmod(mload(0x2c40), mload(0x1220), f_q)) -mstore(0x2c80, addmod(mload(0x2c20), mload(0x2c60), f_q)) -mstore(0x2ca0, mulmod(mload(0x400), mload(0x2c80), f_q)) -mstore(0x2cc0, mulmod(mload(0x2c40), mload(0x2160), f_q)) -mstore(0x2ce0, addmod(mload(0x9a0), sub(f_q, mload(0x9c0)), f_q)) -mstore(0x2d00, mulmod(mload(0x2ce0), mload(0x2cc0), f_q)) -mstore(0x2d20, addmod(mload(0x2ca0), mload(0x2d00), f_q)) -mstore(0x2d40, mulmod(mload(0xcc0), mload(0xcc0), f_q)) -mstore(0x2d60, mulmod(mload(0x2d40), mload(0xcc0), f_q)) -mstore(0x2d80, mulmod(mload(0x2d60), mload(0xcc0), f_q)) -mstore(0x2da0, mulmod(mload(0x2d80), mload(0xcc0), f_q)) -mstore(0x2dc0, mulmod(1, mload(0xcc0), f_q)) -mstore(0x2de0, mulmod(1, mload(0x2d40), f_q)) -mstore(0x2e00, mulmod(1, mload(0x2d60), f_q)) -mstore(0x2e20, mulmod(1, mload(0x2d80), f_q)) -mstore(0x2e40, mulmod(mload(0x2d20), mload(0xce0), f_q)) -mstore(0x2e60, mulmod(mload(0xba0), mload(0x5a0), f_q)) -mstore(0x2e80, mulmod(mload(0x5a0), 1, f_q)) -mstore(0x2ea0, addmod(mload(0xb20), sub(f_q, mload(0x2e80)), f_q)) -mstore(0x2ec0, mulmod(mload(0x5a0), 4925592601992654644734291590386747644864797672605745962807370354577123815907, f_q)) -mstore(0x2ee0, addmod(mload(0xb20), sub(f_q, mload(0x2ec0)), f_q)) -mstore(0x2f00, mulmod(mload(0x5a0), 9936069627611189518829255670237324269287146421271524553312532036927871056678, f_q)) -mstore(0x2f20, addmod(mload(0xb20), sub(f_q, mload(0x2f00)), f_q)) -mstore(0x2f40, mulmod(mload(0x5a0), 19380560087801265747114831706136320509424814679569278834391540198888293317501, f_q)) -mstore(0x2f60, addmod(mload(0xb20), sub(f_q, mload(0x2f40)), f_q)) -{ - let result := mulmod(mload(0xb20), mulmod(mload(0xba0), 19470333053884630052643688193991482022807116448657043890197497819686199857828, f_q), f_q) -result := addmod(mulmod(mload(0x5a0), mulmod(mload(0xba0), 2417909817954645169602717551265793065741247951758990453500706366889608637789, f_q), f_q), result, f_q) -mstore(12160, result) - } -{ - let result := mulmod(mload(0xb20), mulmod(mload(0xba0), 13686227242150003628673578706486473027501243666075718775893288845164412632930, f_q), f_q) -result := addmod(mulmod(mload(0x5a0), mulmod(mload(0xba0), 10079503758141076467065204586507257535549232608832327988764044976944819876346, f_q), f_q), result, f_q) -mstore(12192, result) - } -{ - let result := mulmod(mload(0xb20), mulmod(mload(0xba0), 18715936898160381416714524359517348601997944075114713669820885373469568354766, f_q), f_q) -result := addmod(mulmod(mload(0x5a0), mulmod(mload(0xba0), 15375775075249615866494035504844970190233299190010037655541891534730124992278, f_q), f_q), result, f_q) -mstore(12224, result) - } -mstore(0x2fe0, mulmod(1, mload(0x2ea0), f_q)) -mstore(0x3000, mulmod(mload(0x2fe0), mload(0x2f60), f_q)) -mstore(0x3020, mulmod(mload(0x3000), mload(0x2ee0), f_q)) -{ - let result := mulmod(mload(0xb20), mulmod(mload(0x5a0), 2507682784038009475131574039120954579123549720846755509306663987687515178117, f_q), f_q) -result := addmod(mulmod(mload(0x5a0), mulmod(mload(0x5a0), 19380560087801265747114831706136320509424814679569278834391540198888293317500, f_q), f_q), result, f_q) -mstore(12352, result) - } -{ - let result := mulmod(mload(0xb20), mulmod(mload(0x5a0), 19380560087801265747114831706136320509424814679569278834391540198888293317500, f_q), f_q) -result := addmod(mulmod(mload(0x5a0), mulmod(mload(0x5a0), 13127608231681926238307118629157549705911918406946061530612285695988519678593, f_q), f_q), result, f_q) -mstore(12384, result) - } -{ - let result := mulmod(mload(0xb20), mulmod(mload(0xba0), 16140595808673403009154643164823336476463527776677864878778453135559733237044, f_q), f_q) -result := addmod(mulmod(mload(0x5a0), mulmod(mload(0xba0), 5747647063165872213091762580433938612084836623738169464919751051016075258573, f_q), f_q), result, f_q) -mstore(12416, result) - } -{ - let result := mulmod(mload(0xb20), mulmod(mload(0xba0), 17015964487361230672162623735654618573844832338054897787312333529290879253714, f_q), f_q) -result := addmod(mulmod(mload(0x5a0), mulmod(mload(0xba0), 3176732791729641355588945816447819802711920387939493967460175841862547409845, f_q), f_q), result, f_q) -mstore(12448, result) - } -{ - let result := mulmod(mload(0xb20), mulmod(mload(0xba0), 19187508498431587163140984396833674282302409422288044257471288693049179355069, f_q), f_q) -result := addmod(mulmod(mload(0x5a0), mulmod(mload(0xba0), 17472297497993506786357772047295541913219081564856233764575529621311665103799, f_q), f_q), result, f_q) -mstore(12480, result) - } -mstore(0x30e0, mulmod(mload(0x3000), mload(0x2f20), f_q)) -{ - let result := mulmod(mload(0xb20), mulmod(mload(0x5a0), 16962650269846620577512114154870527443683566727810288380890833831998684679711, f_q), f_q) -result := addmod(mulmod(mload(0x5a0), mulmod(mload(0x5a0), 4925592601992654644734291590386747644864797672605745962807370354577123815906, f_q), f_q), result, f_q) -mstore(12544, result) - } -{ - let result := mulmod(mload(0xb20), mulmod(mload(0x5a0), 4925592601992654644734291590386747644864797672605745962807370354577123815906, f_q), f_q) -result := addmod(mulmod(mload(0x5a0), mulmod(mload(0x5a0), 664623189640884330400307346618971907426870604454565164570654825418724962734, f_q), f_q), result, f_q) -mstore(12576, result) - } -mstore(0x3140, mulmod(mload(0x2fe0), mload(0x2ee0), f_q)) -{ - let result := mulmod(mload(0xb20), 1, f_q) -result := addmod(mulmod(mload(0x5a0), 21888242871839275222246405745257275088548364400416034343698204186575808495616, f_q), result, f_q) -mstore(12640, result) - } -{ - let prod := mload(0x2f80) - - prod := mulmod(mload(0x2fa0), prod, f_q) - mstore(0x3180, prod) - - prod := mulmod(mload(0x2fc0), prod, f_q) - mstore(0x31a0, prod) - - prod := mulmod(mload(0x3040), prod, f_q) - mstore(0x31c0, prod) - - prod := mulmod(mload(0x3060), prod, f_q) - mstore(0x31e0, prod) - - prod := mulmod(mload(0x3000), prod, f_q) - mstore(0x3200, prod) - - prod := mulmod(mload(0x3080), prod, f_q) - mstore(0x3220, prod) - - prod := mulmod(mload(0x30a0), prod, f_q) - mstore(0x3240, prod) - - prod := mulmod(mload(0x30c0), prod, f_q) - mstore(0x3260, prod) - - prod := mulmod(mload(0x30e0), prod, f_q) - mstore(0x3280, prod) - - prod := mulmod(mload(0x3100), prod, f_q) - mstore(0x32a0, prod) - - prod := mulmod(mload(0x3120), prod, f_q) - mstore(0x32c0, prod) - - prod := mulmod(mload(0x3140), prod, f_q) - mstore(0x32e0, prod) - - prod := mulmod(mload(0x3160), prod, f_q) - mstore(0x3300, prod) - - prod := mulmod(mload(0x2fe0), prod, f_q) - mstore(0x3320, prod) - - } -mstore(0x3360, 32) -mstore(0x3380, 32) -mstore(0x33a0, 32) -mstore(0x33c0, mload(0x3320)) -mstore(0x33e0, 21888242871839275222246405745257275088548364400416034343698204186575808495615) -mstore(0x3400, 21888242871839275222246405745257275088548364400416034343698204186575808495617) -success := and(eq(staticcall(gas(), 0x5, 0x3360, 0xc0, 0x3340, 0x20), 1), success) -{ - - let inv := mload(0x3340) - let v - - v := mload(0x2fe0) - mstore(12256, mulmod(mload(0x3300), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x3160) - mstore(12640, mulmod(mload(0x32e0), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x3140) - mstore(12608, mulmod(mload(0x32c0), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x3120) - mstore(12576, mulmod(mload(0x32a0), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x3100) - mstore(12544, mulmod(mload(0x3280), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x30e0) - mstore(12512, mulmod(mload(0x3260), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x30c0) - mstore(12480, mulmod(mload(0x3240), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x30a0) - mstore(12448, mulmod(mload(0x3220), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x3080) - mstore(12416, mulmod(mload(0x3200), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x3000) - mstore(12288, mulmod(mload(0x31e0), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x3060) - mstore(12384, mulmod(mload(0x31c0), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x3040) - mstore(12352, mulmod(mload(0x31a0), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x2fc0) - mstore(12224, mulmod(mload(0x3180), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x2fa0) - mstore(12192, mulmod(mload(0x2f80), inv, f_q)) - inv := mulmod(v, inv, f_q) - mstore(0x2f80, inv) - - } -{ - let result := mload(0x2f80) -result := addmod(mload(0x2fa0), result, f_q) -result := addmod(mload(0x2fc0), result, f_q) -mstore(13344, result) - } -mstore(0x3440, mulmod(mload(0x3020), mload(0x3000), f_q)) -{ - let result := mload(0x3040) -result := addmod(mload(0x3060), result, f_q) -mstore(13408, result) - } -mstore(0x3480, mulmod(mload(0x3020), mload(0x30e0), f_q)) -{ - let result := mload(0x3080) -result := addmod(mload(0x30a0), result, f_q) -result := addmod(mload(0x30c0), result, f_q) -mstore(13472, result) - } -mstore(0x34c0, mulmod(mload(0x3020), mload(0x3140), f_q)) -{ - let result := mload(0x3100) -result := addmod(mload(0x3120), result, f_q) -mstore(13536, result) - } -mstore(0x3500, mulmod(mload(0x3020), mload(0x2fe0), f_q)) -{ - let result := mload(0x3160) -mstore(13600, result) - } -{ - let prod := mload(0x3420) - - prod := mulmod(mload(0x3460), prod, f_q) - mstore(0x3540, prod) - - prod := mulmod(mload(0x34a0), prod, f_q) - mstore(0x3560, prod) - - prod := mulmod(mload(0x34e0), prod, f_q) - mstore(0x3580, prod) - - prod := mulmod(mload(0x3520), prod, f_q) - mstore(0x35a0, prod) - - } -mstore(0x35e0, 32) -mstore(0x3600, 32) -mstore(0x3620, 32) -mstore(0x3640, mload(0x35a0)) -mstore(0x3660, 21888242871839275222246405745257275088548364400416034343698204186575808495615) -mstore(0x3680, 21888242871839275222246405745257275088548364400416034343698204186575808495617) -success := and(eq(staticcall(gas(), 0x5, 0x35e0, 0xc0, 0x35c0, 0x20), 1), success) -{ - - let inv := mload(0x35c0) - let v - - v := mload(0x3520) - mstore(13600, mulmod(mload(0x3580), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x34e0) - mstore(13536, mulmod(mload(0x3560), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x34a0) - mstore(13472, mulmod(mload(0x3540), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x3460) - mstore(13408, mulmod(mload(0x3420), inv, f_q)) - inv := mulmod(v, inv, f_q) - mstore(0x3420, inv) - - } -mstore(0x36a0, mulmod(mload(0x3440), mload(0x3460), f_q)) -mstore(0x36c0, mulmod(mload(0x3480), mload(0x34a0), f_q)) -mstore(0x36e0, mulmod(mload(0x34c0), mload(0x34e0), f_q)) -mstore(0x3700, mulmod(mload(0x3500), mload(0x3520), f_q)) -mstore(0x3720, mulmod(mload(0xa20), mload(0xa20), f_q)) -mstore(0x3740, mulmod(mload(0x3720), mload(0xa20), f_q)) -mstore(0x3760, mulmod(mload(0x3740), mload(0xa20), f_q)) -mstore(0x3780, mulmod(mload(0x3760), mload(0xa20), f_q)) -mstore(0x37a0, mulmod(mload(0x3780), mload(0xa20), f_q)) -mstore(0x37c0, mulmod(mload(0x37a0), mload(0xa20), f_q)) -mstore(0x37e0, mulmod(mload(0x37c0), mload(0xa20), f_q)) -mstore(0x3800, mulmod(mload(0x37e0), mload(0xa20), f_q)) -mstore(0x3820, mulmod(mload(0x3800), mload(0xa20), f_q)) -mstore(0x3840, mulmod(mload(0x3820), mload(0xa20), f_q)) -mstore(0x3860, mulmod(mload(0x3840), mload(0xa20), f_q)) -mstore(0x3880, mulmod(mload(0x3860), mload(0xa20), f_q)) -mstore(0x38a0, mulmod(mload(0x3880), mload(0xa20), f_q)) -mstore(0x38c0, mulmod(mload(0x38a0), mload(0xa20), f_q)) -mstore(0x38e0, mulmod(mload(0x38c0), mload(0xa20), f_q)) -mstore(0x3900, mulmod(mload(0x38e0), mload(0xa20), f_q)) -mstore(0x3920, mulmod(mload(0xa80), mload(0xa80), f_q)) -mstore(0x3940, mulmod(mload(0x3920), mload(0xa80), f_q)) -mstore(0x3960, mulmod(mload(0x3940), mload(0xa80), f_q)) -mstore(0x3980, mulmod(mload(0x3960), mload(0xa80), f_q)) -{ - let result := mulmod(mload(0x5e0), mload(0x2f80), f_q) -result := addmod(mulmod(mload(0x620), mload(0x2fa0), f_q), result, f_q) -result := addmod(mulmod(mload(0x6a0), mload(0x2fc0), f_q), result, f_q) -mstore(14752, result) - } -mstore(0x39c0, mulmod(mload(0x39a0), mload(0x3420), f_q)) -mstore(0x39e0, mulmod(sub(f_q, mload(0x39c0)), 1, f_q)) -{ - let result := mulmod(mload(0x600), mload(0x2f80), f_q) -result := addmod(mulmod(mload(0x640), mload(0x2fa0), f_q), result, f_q) -result := addmod(mulmod(mload(0x680), mload(0x2fc0), f_q), result, f_q) -mstore(14848, result) - } -mstore(0x3a20, mulmod(mload(0x3a00), mload(0x3420), f_q)) -mstore(0x3a40, mulmod(sub(f_q, mload(0x3a20)), mload(0xa20), f_q)) -mstore(0x3a60, mulmod(1, mload(0xa20), f_q)) -mstore(0x3a80, addmod(mload(0x39e0), mload(0x3a40), f_q)) -mstore(0x3aa0, mulmod(mload(0x3a80), 1, f_q)) -mstore(0x3ac0, mulmod(mload(0x3a60), 1, f_q)) -mstore(0x3ae0, mulmod(1, mload(0x3440), f_q)) -{ - let result := mulmod(mload(0x660), mload(0x3040), f_q) -result := addmod(mulmod(mload(0x6c0), mload(0x3060), f_q), result, f_q) -mstore(15104, result) - } -mstore(0x3b20, mulmod(mload(0x3b00), mload(0x36a0), f_q)) -mstore(0x3b40, mulmod(sub(f_q, mload(0x3b20)), 1, f_q)) -mstore(0x3b60, mulmod(mload(0x3ae0), 1, f_q)) -{ - let result := mulmod(mload(0x920), mload(0x3040), f_q) -result := addmod(mulmod(mload(0x940), mload(0x3060), f_q), result, f_q) -mstore(15232, result) - } -mstore(0x3ba0, mulmod(mload(0x3b80), mload(0x36a0), f_q)) -mstore(0x3bc0, mulmod(sub(f_q, mload(0x3ba0)), mload(0xa20), f_q)) -mstore(0x3be0, mulmod(mload(0x3ae0), mload(0xa20), f_q)) -mstore(0x3c00, addmod(mload(0x3b40), mload(0x3bc0), f_q)) -{ - let result := mulmod(mload(0x960), mload(0x3040), f_q) -result := addmod(mulmod(mload(0x980), mload(0x3060), f_q), result, f_q) -mstore(15392, result) - } -mstore(0x3c40, mulmod(mload(0x3c20), mload(0x36a0), f_q)) -mstore(0x3c60, mulmod(sub(f_q, mload(0x3c40)), mload(0x3720), f_q)) -mstore(0x3c80, mulmod(mload(0x3ae0), mload(0x3720), f_q)) -mstore(0x3ca0, addmod(mload(0x3c00), mload(0x3c60), f_q)) -mstore(0x3cc0, mulmod(mload(0x3ca0), mload(0xa80), f_q)) -mstore(0x3ce0, mulmod(mload(0x3b60), mload(0xa80), f_q)) -mstore(0x3d00, mulmod(mload(0x3be0), mload(0xa80), f_q)) -mstore(0x3d20, mulmod(mload(0x3c80), mload(0xa80), f_q)) -mstore(0x3d40, addmod(mload(0x3aa0), mload(0x3cc0), f_q)) -mstore(0x3d60, mulmod(1, mload(0x3480), f_q)) -{ - let result := mulmod(mload(0x8c0), mload(0x3080), f_q) -result := addmod(mulmod(mload(0x8e0), mload(0x30a0), f_q), result, f_q) -result := addmod(mulmod(mload(0x900), mload(0x30c0), f_q), result, f_q) -mstore(15744, result) - } -mstore(0x3da0, mulmod(mload(0x3d80), mload(0x36c0), f_q)) -mstore(0x3dc0, mulmod(sub(f_q, mload(0x3da0)), 1, f_q)) -mstore(0x3de0, mulmod(mload(0x3d60), 1, f_q)) -mstore(0x3e00, mulmod(mload(0x3dc0), mload(0x3920), f_q)) -mstore(0x3e20, mulmod(mload(0x3de0), mload(0x3920), f_q)) -mstore(0x3e40, addmod(mload(0x3d40), mload(0x3e00), f_q)) -mstore(0x3e60, mulmod(1, mload(0x34c0), f_q)) -{ - let result := mulmod(mload(0x9a0), mload(0x3100), f_q) -result := addmod(mulmod(mload(0x9c0), mload(0x3120), f_q), result, f_q) -mstore(16000, result) - } -mstore(0x3ea0, mulmod(mload(0x3e80), mload(0x36e0), f_q)) -mstore(0x3ec0, mulmod(sub(f_q, mload(0x3ea0)), 1, f_q)) -mstore(0x3ee0, mulmod(mload(0x3e60), 1, f_q)) -mstore(0x3f00, mulmod(mload(0x3ec0), mload(0x3940), f_q)) -mstore(0x3f20, mulmod(mload(0x3ee0), mload(0x3940), f_q)) -mstore(0x3f40, addmod(mload(0x3e40), mload(0x3f00), f_q)) -mstore(0x3f60, mulmod(1, mload(0x3500), f_q)) -{ - let result := mulmod(mload(0x9e0), mload(0x3160), f_q) -mstore(16256, result) - } -mstore(0x3fa0, mulmod(mload(0x3f80), mload(0x3700), f_q)) -mstore(0x3fc0, mulmod(sub(f_q, mload(0x3fa0)), 1, f_q)) -mstore(0x3fe0, mulmod(mload(0x3f60), 1, f_q)) -{ - let result := mulmod(mload(0x6e0), mload(0x3160), f_q) -mstore(16384, result) - } -mstore(0x4020, mulmod(mload(0x4000), mload(0x3700), f_q)) -mstore(0x4040, mulmod(sub(f_q, mload(0x4020)), mload(0xa20), f_q)) -mstore(0x4060, mulmod(mload(0x3f60), mload(0xa20), f_q)) -mstore(0x4080, addmod(mload(0x3fc0), mload(0x4040), f_q)) -{ - let result := mulmod(mload(0x700), mload(0x3160), f_q) -mstore(16544, result) - } -mstore(0x40c0, mulmod(mload(0x40a0), mload(0x3700), f_q)) -mstore(0x40e0, mulmod(sub(f_q, mload(0x40c0)), mload(0x3720), f_q)) -mstore(0x4100, mulmod(mload(0x3f60), mload(0x3720), f_q)) -mstore(0x4120, addmod(mload(0x4080), mload(0x40e0), f_q)) -{ - let result := mulmod(mload(0x720), mload(0x3160), f_q) -mstore(16704, result) - } -mstore(0x4160, mulmod(mload(0x4140), mload(0x3700), f_q)) -mstore(0x4180, mulmod(sub(f_q, mload(0x4160)), mload(0x3740), f_q)) -mstore(0x41a0, mulmod(mload(0x3f60), mload(0x3740), f_q)) -mstore(0x41c0, addmod(mload(0x4120), mload(0x4180), f_q)) -{ - let result := mulmod(mload(0x740), mload(0x3160), f_q) -mstore(16864, result) - } -mstore(0x4200, mulmod(mload(0x41e0), mload(0x3700), f_q)) -mstore(0x4220, mulmod(sub(f_q, mload(0x4200)), mload(0x3760), f_q)) -mstore(0x4240, mulmod(mload(0x3f60), mload(0x3760), f_q)) -mstore(0x4260, addmod(mload(0x41c0), mload(0x4220), f_q)) -{ - let result := mulmod(mload(0x760), mload(0x3160), f_q) -mstore(17024, result) - } -mstore(0x42a0, mulmod(mload(0x4280), mload(0x3700), f_q)) -mstore(0x42c0, mulmod(sub(f_q, mload(0x42a0)), mload(0x3780), f_q)) -mstore(0x42e0, mulmod(mload(0x3f60), mload(0x3780), f_q)) -mstore(0x4300, addmod(mload(0x4260), mload(0x42c0), f_q)) -{ - let result := mulmod(mload(0x780), mload(0x3160), f_q) -mstore(17184, result) - } -mstore(0x4340, mulmod(mload(0x4320), mload(0x3700), f_q)) -mstore(0x4360, mulmod(sub(f_q, mload(0x4340)), mload(0x37a0), f_q)) -mstore(0x4380, mulmod(mload(0x3f60), mload(0x37a0), f_q)) -mstore(0x43a0, addmod(mload(0x4300), mload(0x4360), f_q)) -{ - let result := mulmod(mload(0x7a0), mload(0x3160), f_q) -mstore(17344, result) - } -mstore(0x43e0, mulmod(mload(0x43c0), mload(0x3700), f_q)) -mstore(0x4400, mulmod(sub(f_q, mload(0x43e0)), mload(0x37c0), f_q)) -mstore(0x4420, mulmod(mload(0x3f60), mload(0x37c0), f_q)) -mstore(0x4440, addmod(mload(0x43a0), mload(0x4400), f_q)) -{ - let result := mulmod(mload(0x7c0), mload(0x3160), f_q) -mstore(17504, result) - } -mstore(0x4480, mulmod(mload(0x4460), mload(0x3700), f_q)) -mstore(0x44a0, mulmod(sub(f_q, mload(0x4480)), mload(0x37e0), f_q)) -mstore(0x44c0, mulmod(mload(0x3f60), mload(0x37e0), f_q)) -mstore(0x44e0, addmod(mload(0x4440), mload(0x44a0), f_q)) -{ - let result := mulmod(mload(0x800), mload(0x3160), f_q) -mstore(17664, result) - } -mstore(0x4520, mulmod(mload(0x4500), mload(0x3700), f_q)) -mstore(0x4540, mulmod(sub(f_q, mload(0x4520)), mload(0x3800), f_q)) -mstore(0x4560, mulmod(mload(0x3f60), mload(0x3800), f_q)) -mstore(0x4580, addmod(mload(0x44e0), mload(0x4540), f_q)) -{ - let result := mulmod(mload(0x820), mload(0x3160), f_q) -mstore(17824, result) - } -mstore(0x45c0, mulmod(mload(0x45a0), mload(0x3700), f_q)) -mstore(0x45e0, mulmod(sub(f_q, mload(0x45c0)), mload(0x3820), f_q)) -mstore(0x4600, mulmod(mload(0x3f60), mload(0x3820), f_q)) -mstore(0x4620, addmod(mload(0x4580), mload(0x45e0), f_q)) -{ - let result := mulmod(mload(0x840), mload(0x3160), f_q) -mstore(17984, result) - } -mstore(0x4660, mulmod(mload(0x4640), mload(0x3700), f_q)) -mstore(0x4680, mulmod(sub(f_q, mload(0x4660)), mload(0x3840), f_q)) -mstore(0x46a0, mulmod(mload(0x3f60), mload(0x3840), f_q)) -mstore(0x46c0, addmod(mload(0x4620), mload(0x4680), f_q)) -{ - let result := mulmod(mload(0x860), mload(0x3160), f_q) -mstore(18144, result) - } -mstore(0x4700, mulmod(mload(0x46e0), mload(0x3700), f_q)) -mstore(0x4720, mulmod(sub(f_q, mload(0x4700)), mload(0x3860), f_q)) -mstore(0x4740, mulmod(mload(0x3f60), mload(0x3860), f_q)) -mstore(0x4760, addmod(mload(0x46c0), mload(0x4720), f_q)) -{ - let result := mulmod(mload(0x880), mload(0x3160), f_q) -mstore(18304, result) - } -mstore(0x47a0, mulmod(mload(0x4780), mload(0x3700), f_q)) -mstore(0x47c0, mulmod(sub(f_q, mload(0x47a0)), mload(0x3880), f_q)) -mstore(0x47e0, mulmod(mload(0x3f60), mload(0x3880), f_q)) -mstore(0x4800, addmod(mload(0x4760), mload(0x47c0), f_q)) -{ - let result := mulmod(mload(0x8a0), mload(0x3160), f_q) -mstore(18464, result) - } -mstore(0x4840, mulmod(mload(0x4820), mload(0x3700), f_q)) -mstore(0x4860, mulmod(sub(f_q, mload(0x4840)), mload(0x38a0), f_q)) -mstore(0x4880, mulmod(mload(0x3f60), mload(0x38a0), f_q)) -mstore(0x48a0, addmod(mload(0x4800), mload(0x4860), f_q)) -mstore(0x48c0, mulmod(mload(0x2dc0), mload(0x3500), f_q)) -mstore(0x48e0, mulmod(mload(0x2de0), mload(0x3500), f_q)) -mstore(0x4900, mulmod(mload(0x2e00), mload(0x3500), f_q)) -mstore(0x4920, mulmod(mload(0x2e20), mload(0x3500), f_q)) -{ - let result := mulmod(mload(0x2e40), mload(0x3160), f_q) -mstore(18752, result) - } -mstore(0x4960, mulmod(mload(0x4940), mload(0x3700), f_q)) -mstore(0x4980, mulmod(sub(f_q, mload(0x4960)), mload(0x38c0), f_q)) -mstore(0x49a0, mulmod(mload(0x3f60), mload(0x38c0), f_q)) -mstore(0x49c0, mulmod(mload(0x48c0), mload(0x38c0), f_q)) -mstore(0x49e0, mulmod(mload(0x48e0), mload(0x38c0), f_q)) -mstore(0x4a00, mulmod(mload(0x4900), mload(0x38c0), f_q)) -mstore(0x4a20, mulmod(mload(0x4920), mload(0x38c0), f_q)) -mstore(0x4a40, addmod(mload(0x48a0), mload(0x4980), f_q)) -{ - let result := mulmod(mload(0x7e0), mload(0x3160), f_q) -mstore(19040, result) - } -mstore(0x4a80, mulmod(mload(0x4a60), mload(0x3700), f_q)) -mstore(0x4aa0, mulmod(sub(f_q, mload(0x4a80)), mload(0x38e0), f_q)) -mstore(0x4ac0, mulmod(mload(0x3f60), mload(0x38e0), f_q)) -mstore(0x4ae0, addmod(mload(0x4a40), mload(0x4aa0), f_q)) -mstore(0x4b00, mulmod(mload(0x4ae0), mload(0x3960), f_q)) -mstore(0x4b20, mulmod(mload(0x3fe0), mload(0x3960), f_q)) -mstore(0x4b40, mulmod(mload(0x4060), mload(0x3960), f_q)) -mstore(0x4b60, mulmod(mload(0x4100), mload(0x3960), f_q)) -mstore(0x4b80, mulmod(mload(0x41a0), mload(0x3960), f_q)) -mstore(0x4ba0, mulmod(mload(0x4240), mload(0x3960), f_q)) -mstore(0x4bc0, mulmod(mload(0x42e0), mload(0x3960), f_q)) -mstore(0x4be0, mulmod(mload(0x4380), mload(0x3960), f_q)) -mstore(0x4c00, mulmod(mload(0x4420), mload(0x3960), f_q)) -mstore(0x4c20, mulmod(mload(0x44c0), mload(0x3960), f_q)) -mstore(0x4c40, mulmod(mload(0x4560), mload(0x3960), f_q)) -mstore(0x4c60, mulmod(mload(0x4600), mload(0x3960), f_q)) -mstore(0x4c80, mulmod(mload(0x46a0), mload(0x3960), f_q)) -mstore(0x4ca0, mulmod(mload(0x4740), mload(0x3960), f_q)) -mstore(0x4cc0, mulmod(mload(0x47e0), mload(0x3960), f_q)) -mstore(0x4ce0, mulmod(mload(0x4880), mload(0x3960), f_q)) -mstore(0x4d00, mulmod(mload(0x49a0), mload(0x3960), f_q)) -mstore(0x4d20, mulmod(mload(0x49c0), mload(0x3960), f_q)) -mstore(0x4d40, mulmod(mload(0x49e0), mload(0x3960), f_q)) -mstore(0x4d60, mulmod(mload(0x4a00), mload(0x3960), f_q)) -mstore(0x4d80, mulmod(mload(0x4a20), mload(0x3960), f_q)) -mstore(0x4da0, mulmod(mload(0x4ac0), mload(0x3960), f_q)) -mstore(0x4dc0, addmod(mload(0x3f40), mload(0x4b00), f_q)) -mstore(0x4de0, mulmod(1, mload(0x3020), f_q)) -mstore(0x4e00, mulmod(1, mload(0xb20), f_q)) -mstore(0x4e20, 0x0000000000000000000000000000000000000000000000000000000000000001) - mstore(0x4e40, 0x0000000000000000000000000000000000000000000000000000000000000002) -mstore(0x4e60, mload(0x4dc0)) -success := and(eq(staticcall(gas(), 0x7, 0x4e20, 0x60, 0x4e20, 0x40), 1), success) -mstore(0x4e80, mload(0x4e20)) - mstore(0x4ea0, mload(0x4e40)) -mstore(0x4ec0, mload(0x80)) - mstore(0x4ee0, mload(0xa0)) -success := and(eq(staticcall(gas(), 0x6, 0x4e80, 0x80, 0x4e80, 0x40), 1), success) -mstore(0x4f00, mload(0xc0)) - mstore(0x4f20, mload(0xe0)) -mstore(0x4f40, mload(0x3ac0)) -success := and(eq(staticcall(gas(), 0x7, 0x4f00, 0x60, 0x4f00, 0x40), 1), success) -mstore(0x4f60, mload(0x4e80)) - mstore(0x4f80, mload(0x4ea0)) -mstore(0x4fa0, mload(0x4f00)) - mstore(0x4fc0, mload(0x4f20)) -success := and(eq(staticcall(gas(), 0x6, 0x4f60, 0x80, 0x4f60, 0x40), 1), success) -mstore(0x4fe0, mload(0x100)) - mstore(0x5000, mload(0x120)) -mstore(0x5020, mload(0x3ce0)) -success := and(eq(staticcall(gas(), 0x7, 0x4fe0, 0x60, 0x4fe0, 0x40), 1), success) -mstore(0x5040, mload(0x4f60)) - mstore(0x5060, mload(0x4f80)) -mstore(0x5080, mload(0x4fe0)) - mstore(0x50a0, mload(0x5000)) -success := and(eq(staticcall(gas(), 0x6, 0x5040, 0x80, 0x5040, 0x40), 1), success) -mstore(0x50c0, mload(0x320)) - mstore(0x50e0, mload(0x340)) -mstore(0x5100, mload(0x3d00)) -success := and(eq(staticcall(gas(), 0x7, 0x50c0, 0x60, 0x50c0, 0x40), 1), success) -mstore(0x5120, mload(0x5040)) - mstore(0x5140, mload(0x5060)) -mstore(0x5160, mload(0x50c0)) - mstore(0x5180, mload(0x50e0)) -success := and(eq(staticcall(gas(), 0x6, 0x5120, 0x80, 0x5120, 0x40), 1), success) -mstore(0x51a0, mload(0x360)) - mstore(0x51c0, mload(0x380)) -mstore(0x51e0, mload(0x3d20)) -success := and(eq(staticcall(gas(), 0x7, 0x51a0, 0x60, 0x51a0, 0x40), 1), success) -mstore(0x5200, mload(0x5120)) - mstore(0x5220, mload(0x5140)) -mstore(0x5240, mload(0x51a0)) - mstore(0x5260, mload(0x51c0)) -success := and(eq(staticcall(gas(), 0x6, 0x5200, 0x80, 0x5200, 0x40), 1), success) -mstore(0x5280, mload(0x2e0)) - mstore(0x52a0, mload(0x300)) -mstore(0x52c0, mload(0x3e20)) -success := and(eq(staticcall(gas(), 0x7, 0x5280, 0x60, 0x5280, 0x40), 1), success) -mstore(0x52e0, mload(0x5200)) - mstore(0x5300, mload(0x5220)) -mstore(0x5320, mload(0x5280)) - mstore(0x5340, mload(0x52a0)) -success := and(eq(staticcall(gas(), 0x6, 0x52e0, 0x80, 0x52e0, 0x40), 1), success) -mstore(0x5360, mload(0x1a0)) - mstore(0x5380, mload(0x1c0)) -mstore(0x53a0, mload(0x3f20)) -success := and(eq(staticcall(gas(), 0x7, 0x5360, 0x60, 0x5360, 0x40), 1), success) -mstore(0x53c0, mload(0x52e0)) - mstore(0x53e0, mload(0x5300)) -mstore(0x5400, mload(0x5360)) - mstore(0x5420, mload(0x5380)) -success := and(eq(staticcall(gas(), 0x6, 0x53c0, 0x80, 0x53c0, 0x40), 1), success) -mstore(0x5440, mload(0x1e0)) - mstore(0x5460, mload(0x200)) -mstore(0x5480, mload(0x4b20)) -success := and(eq(staticcall(gas(), 0x7, 0x5440, 0x60, 0x5440, 0x40), 1), success) -mstore(0x54a0, mload(0x53c0)) - mstore(0x54c0, mload(0x53e0)) -mstore(0x54e0, mload(0x5440)) - mstore(0x5500, mload(0x5460)) -success := and(eq(staticcall(gas(), 0x6, 0x54a0, 0x80, 0x54a0, 0x40), 1), success) -mstore(0x5520, 0x0655d62979dc062dc1e8c15334bf17cf5c7e6352219c0bd0b0cc039598d9a669) - mstore(0x5540, 0x019589d3002f6a0ded46a69053f0c05e7a7ca45339b3d2bae85d00f26ac0f33e) -mstore(0x5560, mload(0x4b40)) -success := and(eq(staticcall(gas(), 0x7, 0x5520, 0x60, 0x5520, 0x40), 1), success) -mstore(0x5580, mload(0x54a0)) - mstore(0x55a0, mload(0x54c0)) -mstore(0x55c0, mload(0x5520)) - mstore(0x55e0, mload(0x5540)) -success := and(eq(staticcall(gas(), 0x6, 0x5580, 0x80, 0x5580, 0x40), 1), success) -mstore(0x5600, 0x1eab999592385208b42a12dcc047763ee5cf12052eb4b0d9a7a286e043f3df9f) - mstore(0x5620, 0x18887eb3c646ffd4961226c91bac9661004798b1fc6a39c43f9a6d1d03767194) -mstore(0x5640, mload(0x4b60)) -success := and(eq(staticcall(gas(), 0x7, 0x5600, 0x60, 0x5600, 0x40), 1), success) -mstore(0x5660, mload(0x5580)) - mstore(0x5680, mload(0x55a0)) -mstore(0x56a0, mload(0x5600)) - mstore(0x56c0, mload(0x5620)) -success := and(eq(staticcall(gas(), 0x6, 0x5660, 0x80, 0x5660, 0x40), 1), success) -mstore(0x56e0, 0x076806058b02e1b5e99b2f2e8eee613194f8df73481659f5938baacd64e4d7b8) - mstore(0x5700, 0x05ae81644b244dceea5350a84a79cbf0a199bbd6c1fad9887423f8c8048e6039) -mstore(0x5720, mload(0x4b80)) -success := and(eq(staticcall(gas(), 0x7, 0x56e0, 0x60, 0x56e0, 0x40), 1), success) -mstore(0x5740, mload(0x5660)) - mstore(0x5760, mload(0x5680)) -mstore(0x5780, mload(0x56e0)) - mstore(0x57a0, mload(0x5700)) -success := and(eq(staticcall(gas(), 0x6, 0x5740, 0x80, 0x5740, 0x40), 1), success) -mstore(0x57c0, 0x0438873fc00231ae527efcf74d9092f5059dfa05275cbe7381e948fe3b7b61d8) - mstore(0x57e0, 0x03c0f314c93403792ba86ec3841b3080f2f7cd092c6bcb632cdbc2122a13cdef) -mstore(0x5800, mload(0x4ba0)) -success := and(eq(staticcall(gas(), 0x7, 0x57c0, 0x60, 0x57c0, 0x40), 1), success) -mstore(0x5820, mload(0x5740)) - mstore(0x5840, mload(0x5760)) -mstore(0x5860, mload(0x57c0)) - mstore(0x5880, mload(0x57e0)) -success := and(eq(staticcall(gas(), 0x6, 0x5820, 0x80, 0x5820, 0x40), 1), success) -mstore(0x58a0, 0x2b12b132a6261ad60249147599cae0d5b3547e510deabba65526d130f04f2402) - mstore(0x58c0, 0x13e3c02ce00f6b7e00d5b099eda5f1155956ae83899a2d59cefcb00dad66a7f6) -mstore(0x58e0, mload(0x4bc0)) -success := and(eq(staticcall(gas(), 0x7, 0x58a0, 0x60, 0x58a0, 0x40), 1), success) -mstore(0x5900, mload(0x5820)) - mstore(0x5920, mload(0x5840)) -mstore(0x5940, mload(0x58a0)) - mstore(0x5960, mload(0x58c0)) -success := and(eq(staticcall(gas(), 0x6, 0x5900, 0x80, 0x5900, 0x40), 1), success) -mstore(0x5980, 0x0ed83370277e0fd35e8258dd5630ce0ce3491fdf7e166d293745635b0cc4eba5) - mstore(0x59a0, 0x03994e7e77ad58072b78757700ab0b79247f2e462b0cf6a5a18b607414c095db) -mstore(0x59c0, mload(0x4be0)) -success := and(eq(staticcall(gas(), 0x7, 0x5980, 0x60, 0x5980, 0x40), 1), success) -mstore(0x59e0, mload(0x5900)) - mstore(0x5a00, mload(0x5920)) -mstore(0x5a20, mload(0x5980)) - mstore(0x5a40, mload(0x59a0)) -success := and(eq(staticcall(gas(), 0x6, 0x59e0, 0x80, 0x59e0, 0x40), 1), success) -mstore(0x5a60, 0x23ba4bc49baab563e9d5bf07da231a4496d35b6bc782dbbd76135fe44c31cbcf) - mstore(0x5a80, 0x28b88097e7edf1b2904f6c25c1e30ca0e38c74a52125b2995c4cf0a750683854) -mstore(0x5aa0, mload(0x4c00)) -success := and(eq(staticcall(gas(), 0x7, 0x5a60, 0x60, 0x5a60, 0x40), 1), success) -mstore(0x5ac0, mload(0x59e0)) - mstore(0x5ae0, mload(0x5a00)) -mstore(0x5b00, mload(0x5a60)) - mstore(0x5b20, mload(0x5a80)) -success := and(eq(staticcall(gas(), 0x6, 0x5ac0, 0x80, 0x5ac0, 0x40), 1), success) -mstore(0x5b40, 0x17dcbba0bbe2d24566de620998f62ff54ea5302ada6aeb18028b61b4dc19bbd3) - mstore(0x5b60, 0x0e1f908007a5c2cc934426c26de7206c53fa10c79d63ef52ee91bfb568c72fbd) -mstore(0x5b80, mload(0x4c20)) -success := and(eq(staticcall(gas(), 0x7, 0x5b40, 0x60, 0x5b40, 0x40), 1), success) -mstore(0x5ba0, mload(0x5ac0)) - mstore(0x5bc0, mload(0x5ae0)) -mstore(0x5be0, mload(0x5b40)) - mstore(0x5c00, mload(0x5b60)) -success := and(eq(staticcall(gas(), 0x6, 0x5ba0, 0x80, 0x5ba0, 0x40), 1), success) -mstore(0x5c20, 0x2c7823f99cb2adb716becb960914ffa1d6e2f257875fdd03c2bee52ef571d41e) - mstore(0x5c40, 0x09ab83ebc6803e65b2e6831c8706d9a134f6b1cf6f6ec5c2697afee095320ee1) -mstore(0x5c60, mload(0x4c40)) -success := and(eq(staticcall(gas(), 0x7, 0x5c20, 0x60, 0x5c20, 0x40), 1), success) -mstore(0x5c80, mload(0x5ba0)) - mstore(0x5ca0, mload(0x5bc0)) -mstore(0x5cc0, mload(0x5c20)) - mstore(0x5ce0, mload(0x5c40)) -success := and(eq(staticcall(gas(), 0x6, 0x5c80, 0x80, 0x5c80, 0x40), 1), success) -mstore(0x5d00, 0x2dc12c205152bfacc354c838225571172c215e01a64de6a2ca6665c70937ac58) - mstore(0x5d20, 0x0449eb529f0038a2bd2f6c0b6213a70488b69b479d90a953579e4c9d45621b85) -mstore(0x5d40, mload(0x4c60)) -success := and(eq(staticcall(gas(), 0x7, 0x5d00, 0x60, 0x5d00, 0x40), 1), success) -mstore(0x5d60, mload(0x5c80)) - mstore(0x5d80, mload(0x5ca0)) -mstore(0x5da0, mload(0x5d00)) - mstore(0x5dc0, mload(0x5d20)) -success := and(eq(staticcall(gas(), 0x6, 0x5d60, 0x80, 0x5d60, 0x40), 1), success) -mstore(0x5de0, 0x09a2ab3913a4770fd50f6ba9ec4ce0b3178b54d7bfca5b42fe7e8e1ce5cf60a9) - mstore(0x5e00, 0x13ec31f94026569c4bb76e2c4ceaa3e6525438ffb4a7a69cde61f6d77b93bad7) -mstore(0x5e20, mload(0x4c80)) -success := and(eq(staticcall(gas(), 0x7, 0x5de0, 0x60, 0x5de0, 0x40), 1), success) -mstore(0x5e40, mload(0x5d60)) - mstore(0x5e60, mload(0x5d80)) -mstore(0x5e80, mload(0x5de0)) - mstore(0x5ea0, mload(0x5e00)) -success := and(eq(staticcall(gas(), 0x6, 0x5e40, 0x80, 0x5e40, 0x40), 1), success) -mstore(0x5ec0, 0x0743ea40f14084db2673217283aa053f986896ee7c181f52118442e99c452974) - mstore(0x5ee0, 0x0203e3493a2594ece57d22cc75dd081ac68271ec7c758153cfd2152bfb5c19e3) -mstore(0x5f00, mload(0x4ca0)) -success := and(eq(staticcall(gas(), 0x7, 0x5ec0, 0x60, 0x5ec0, 0x40), 1), success) -mstore(0x5f20, mload(0x5e40)) - mstore(0x5f40, mload(0x5e60)) -mstore(0x5f60, mload(0x5ec0)) - mstore(0x5f80, mload(0x5ee0)) -success := and(eq(staticcall(gas(), 0x6, 0x5f20, 0x80, 0x5f20, 0x40), 1), success) -mstore(0x5fa0, 0x02a9a77d8e6f6fd89f33ba56fe45f6f5c907e50ba28cdd4198acfb562eb21103) - mstore(0x5fc0, 0x23abee68d83c2d6632576e5e95ca01a61e5e3e6d9438c2ac6428c8b6edfe2aba) -mstore(0x5fe0, mload(0x4cc0)) -success := and(eq(staticcall(gas(), 0x7, 0x5fa0, 0x60, 0x5fa0, 0x40), 1), success) -mstore(0x6000, mload(0x5f20)) - mstore(0x6020, mload(0x5f40)) -mstore(0x6040, mload(0x5fa0)) - mstore(0x6060, mload(0x5fc0)) -success := and(eq(staticcall(gas(), 0x6, 0x6000, 0x80, 0x6000, 0x40), 1), success) -mstore(0x6080, 0x2cf36d36bfacbe1a8d8c626aefa75ac0feb46b725989a8e6fabed074bf9f6af7) - mstore(0x60a0, 0x060fded3ed5ec56d9e0064d73c472245a4c4b6ec371e9808692bf7f259d3b730) -mstore(0x60c0, mload(0x4ce0)) -success := and(eq(staticcall(gas(), 0x7, 0x6080, 0x60, 0x6080, 0x40), 1), success) -mstore(0x60e0, mload(0x6000)) - mstore(0x6100, mload(0x6020)) -mstore(0x6120, mload(0x6080)) - mstore(0x6140, mload(0x60a0)) -success := and(eq(staticcall(gas(), 0x6, 0x60e0, 0x80, 0x60e0, 0x40), 1), success) -mstore(0x6160, mload(0x440)) - mstore(0x6180, mload(0x460)) -mstore(0x61a0, mload(0x4d00)) -success := and(eq(staticcall(gas(), 0x7, 0x6160, 0x60, 0x6160, 0x40), 1), success) -mstore(0x61c0, mload(0x60e0)) - mstore(0x61e0, mload(0x6100)) -mstore(0x6200, mload(0x6160)) - mstore(0x6220, mload(0x6180)) -success := and(eq(staticcall(gas(), 0x6, 0x61c0, 0x80, 0x61c0, 0x40), 1), success) -mstore(0x6240, mload(0x480)) - mstore(0x6260, mload(0x4a0)) -mstore(0x6280, mload(0x4d20)) -success := and(eq(staticcall(gas(), 0x7, 0x6240, 0x60, 0x6240, 0x40), 1), success) -mstore(0x62a0, mload(0x61c0)) - mstore(0x62c0, mload(0x61e0)) -mstore(0x62e0, mload(0x6240)) - mstore(0x6300, mload(0x6260)) -success := and(eq(staticcall(gas(), 0x6, 0x62a0, 0x80, 0x62a0, 0x40), 1), success) -mstore(0x6320, mload(0x4c0)) - mstore(0x6340, mload(0x4e0)) -mstore(0x6360, mload(0x4d40)) -success := and(eq(staticcall(gas(), 0x7, 0x6320, 0x60, 0x6320, 0x40), 1), success) -mstore(0x6380, mload(0x62a0)) - mstore(0x63a0, mload(0x62c0)) -mstore(0x63c0, mload(0x6320)) - mstore(0x63e0, mload(0x6340)) -success := and(eq(staticcall(gas(), 0x6, 0x6380, 0x80, 0x6380, 0x40), 1), success) -mstore(0x6400, mload(0x500)) - mstore(0x6420, mload(0x520)) -mstore(0x6440, mload(0x4d60)) -success := and(eq(staticcall(gas(), 0x7, 0x6400, 0x60, 0x6400, 0x40), 1), success) -mstore(0x6460, mload(0x6380)) - mstore(0x6480, mload(0x63a0)) -mstore(0x64a0, mload(0x6400)) - mstore(0x64c0, mload(0x6420)) -success := and(eq(staticcall(gas(), 0x6, 0x6460, 0x80, 0x6460, 0x40), 1), success) -mstore(0x64e0, mload(0x540)) - mstore(0x6500, mload(0x560)) -mstore(0x6520, mload(0x4d80)) -success := and(eq(staticcall(gas(), 0x7, 0x64e0, 0x60, 0x64e0, 0x40), 1), success) -mstore(0x6540, mload(0x6460)) - mstore(0x6560, mload(0x6480)) -mstore(0x6580, mload(0x64e0)) - mstore(0x65a0, mload(0x6500)) -success := and(eq(staticcall(gas(), 0x6, 0x6540, 0x80, 0x6540, 0x40), 1), success) -mstore(0x65c0, mload(0x3a0)) - mstore(0x65e0, mload(0x3c0)) -mstore(0x6600, mload(0x4da0)) -success := and(eq(staticcall(gas(), 0x7, 0x65c0, 0x60, 0x65c0, 0x40), 1), success) -mstore(0x6620, mload(0x6540)) - mstore(0x6640, mload(0x6560)) -mstore(0x6660, mload(0x65c0)) - mstore(0x6680, mload(0x65e0)) -success := and(eq(staticcall(gas(), 0x6, 0x6620, 0x80, 0x6620, 0x40), 1), success) -mstore(0x66a0, mload(0xac0)) - mstore(0x66c0, mload(0xae0)) -mstore(0x66e0, sub(f_q, mload(0x4de0))) -success := and(eq(staticcall(gas(), 0x7, 0x66a0, 0x60, 0x66a0, 0x40), 1), success) -mstore(0x6700, mload(0x6620)) - mstore(0x6720, mload(0x6640)) -mstore(0x6740, mload(0x66a0)) - mstore(0x6760, mload(0x66c0)) -success := and(eq(staticcall(gas(), 0x6, 0x6700, 0x80, 0x6700, 0x40), 1), success) -mstore(0x6780, mload(0xb60)) - mstore(0x67a0, mload(0xb80)) -mstore(0x67c0, mload(0x4e00)) -success := and(eq(staticcall(gas(), 0x7, 0x6780, 0x60, 0x6780, 0x40), 1), success) -mstore(0x67e0, mload(0x6700)) - mstore(0x6800, mload(0x6720)) -mstore(0x6820, mload(0x6780)) - mstore(0x6840, mload(0x67a0)) -success := and(eq(staticcall(gas(), 0x6, 0x67e0, 0x80, 0x67e0, 0x40), 1), success) -mstore(0x6860, mload(0x67e0)) - mstore(0x6880, mload(0x6800)) -mstore(0x68a0, 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2) - mstore(0x68c0, 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed) - mstore(0x68e0, 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b) - mstore(0x6900, 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa) -mstore(0x6920, mload(0xb60)) - mstore(0x6940, mload(0xb80)) -mstore(0x6960, 0x26186a2d65ee4d2f9c9a5b91f86597d35f192cd120caf7e935d8443d1938e23d) - mstore(0x6980, 0x30441fd1b5d3370482c42152a8899027716989a6996c2535bc9f7fee8aaef79e) - mstore(0x69a0, 0x16f363f103c80d7bbc8ad3c6867e0822bbc6000be91a4689755c7df40221c145) - mstore(0x69c0, 0x2b1cbb3e521edf5a622d82762a44a5e63f1e50b332d71154a4a7958d6011deff) -success := and(eq(staticcall(gas(), 0x8, 0x6860, 0x180, 0x6860, 0x20), 1), success) -success := and(eq(mload(0x6860), 1), success) - - if not(success) { revert(0, 0) } - return(0, 0) - - } - } - } diff --git a/contracts/src/Summa.sol b/contracts/src/Summa.sol index 2134c4f0..cff887d3 100644 --- a/contracts/src/Summa.sol +++ b/contracts/src/Summa.sol @@ -4,9 +4,7 @@ pragma solidity ^0.8.18; // Uncomment this line to use console.log //import "hardhat/console.sol"; -import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; -import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "./interfaces/IVerifier.sol"; @@ -29,43 +27,66 @@ contract Summa is Ownable { * @dev Struct representing an asset owned by the CEX * @param assetName The name of the asset * @param chain The name of the chain name where the asset lives (e.g., ETH, BTC) - * @param amount The total amount of the asset that the CEX holds on a given chain */ struct Asset { string assetName; string chain; - uint256 amount; } - //Verifier contracts - IVerifier private immutable solvencyVerifier; + /** + * @dev Struct representing a commitment submitted by the CEX. + * @param mstRoot Merkle sum tree root of the CEX's liabilities + * @param rootSums The total sums of the assets included in the tree + * @param assetChains The chains where the CEX holds the assets included into the tree + * @param assetNames The names of the assets included into the tree + */ + struct Commitment { + uint256 mstRoot; + uint256[] rootSums; + string[] assetNames; + string[] assetChains; + } + + // User inclusion proof verifier IVerifier private immutable inclusionVerifier; - //All address ownership proofs submitted by the CEX + // List of all address ownership proofs submitted by the CEX AddressOwnershipProof[] public addressOwnershipProofs; - //Convenience mapping to check if an address has already been verified - mapping(bytes32 => uint256) public ownershipProofByAddress; + function getAddressOwnershipProof( + bytes32 addressHash + ) public view returns (AddressOwnershipProof memory) { + require( + _ownershipProofByAddress[addressHash] > 0, + "Address not verified" + ); + // -1 comes from the fact that 0 is reserved to distinguish the case when the proof has not yet been submitted + return + addressOwnershipProofs[_ownershipProofByAddress[addressHash] - 1]; + } + + // Convenience mapping to check if an address has already been verified + mapping(bytes32 => uint256) private _ownershipProofByAddress; - // MST roots corresponding to successfully verified solvency proofs by timestamp - mapping(uint256 => uint256) public mstRoots; + // Solvency commitments by timestamp submitted by the CEX + mapping(uint256 => Commitment) public commitments; event AddressOwnershipProofSubmitted( AddressOwnershipProof[] addressOwnershipProofs ); - event SolvencyProofSubmitted( + event LiabilitiesCommitmentSubmitted( uint256 indexed timestamp, uint256 mstRoot, + uint256[] rootSums, Asset[] assets ); - constructor(IVerifier _solvencyVerifier, IVerifier _inclusionVerifier) { - solvencyVerifier = _solvencyVerifier; + constructor(IVerifier _inclusionVerifier) { inclusionVerifier = _inclusionVerifier; } /** - * @dev Submit an optimistic proof of address ownership for a CEX. The proof is subject to an off-chain verification as it's not feasible to verify the signatures of non-EVM chains in an Ethereum smart contract. + * @dev Submit an optimistic proof of multiple address ownership for a CEX. The proof is subject to an off-chain verification as it's not feasible to verify the signatures of non-EVM chains in an Ethereum smart contract. * @param _addressOwnershipProofs The list of address ownership proofs */ function submitProofOfAddressOwnership( @@ -73,12 +94,14 @@ contract Summa is Ownable { ) public onlyOwner { for (uint i = 0; i < _addressOwnershipProofs.length; i++) { bytes32 addressHash = keccak256( - abi.encode(_addressOwnershipProofs[i].cexAddress) + abi.encodePacked(_addressOwnershipProofs[i].cexAddress) ); - uint256 index = ownershipProofByAddress[addressHash]; - require(index == 0, "Address already verified"); - ownershipProofByAddress[addressHash] = i + 1; + uint256 proofIndex = _ownershipProofByAddress[addressHash]; + require(proofIndex == 0, "Address already verified"); + addressOwnershipProofs.push(_addressOwnershipProofs[i]); + _ownershipProofByAddress[addressHash] = addressOwnershipProofs + .length; require( bytes(_addressOwnershipProofs[i].cexAddress).length != 0 && bytes(_addressOwnershipProofs[i].chain).length != 0 && @@ -94,47 +117,50 @@ contract Summa is Ownable { /** * @dev Submit proof of solvency for a CEX * @param mstRoot Merkle sum tree root of the CEX's liabilities - * @param assets The list of assets owned by the CEX - * @param proof The ZK proof + * @param rootSums The total sums of the assets included into the Merkle sum tree + * @param assets The assets included into the Merkle sum tree * @param timestamp The timestamp at which the CEX took the snapshot of its assets and liabilities */ - function submitProofOfSolvency( + function submitCommitment( uint256 mstRoot, + uint256[] memory rootSums, Asset[] memory assets, - bytes memory proof, uint256 timestamp ) public onlyOwner { + require(mstRoot != 0, "Invalid MST root"); require( - addressOwnershipProofs.length != 0, - "The CEX has not submitted any address ownership proofs" + rootSums.length == assets.length, + "Root asset sums and asset number mismatch" ); - uint256[] memory inputs = new uint256[](assets.length + 1); - inputs[0] = mstRoot; + string[] memory assetNames = new string[](assets.length); + string[] memory assetChains = new string[](assets.length); for (uint i = 0; i < assets.length; i++) { require( bytes(assets[i].chain).length != 0 && bytes(assets[i].assetName).length != 0, "Invalid asset" ); - inputs[i + 1] = assets[i].amount; + require( + rootSums[i] != 0, + "All root sums should be greater than zero" + ); + assetNames[i] = assets[i].assetName; + assetChains[i] = assets[i].chain; } - require(verifySolvencyProof(proof, inputs), "Invalid ZK proof"); - mstRoots[timestamp] = mstRoot; - - emit SolvencyProofSubmitted(timestamp, inputs[0], assets); - } + commitments[timestamp] = Commitment( + mstRoot, + rootSums, + assetNames, + assetChains + ); - /** - * Verify the proof of CEX solvency - * @param proof ZK proof - * @param publicInputs proof inputs - */ - function verifySolvencyProof( - bytes memory proof, - uint256[] memory publicInputs - ) public view returns (bool) { - return solvencyVerifier.verify(publicInputs, proof); + emit LiabilitiesCommitmentSubmitted( + timestamp, + mstRoot, + rootSums, + assets + ); } /** @@ -147,7 +173,10 @@ contract Summa is Ownable { uint256[] memory publicInputs, uint256 timestamp ) public view returns (bool) { - require(mstRoots[timestamp] == publicInputs[1], "Invalid MST root"); + require( + commitments[timestamp].mstRoot == publicInputs[1], + "Invalid MST root" + ); return inclusionVerifier.verify(publicInputs, proof); } } diff --git a/contracts/test/Summa.ts b/contracts/test/Summa.ts index 2ab793f1..d8d6da15 100644 --- a/contracts/test/Summa.ts +++ b/contracts/test/Summa.ts @@ -9,27 +9,21 @@ import * as fs from "fs"; import * as path from "path"; describe("Summa Contract", () => { - function submitProofOfSolvency( + function submitCommitment( summa: Summa, mstRoot: BigNumber, - proof: string, + rootSums: BigNumber[], assets = [ { chain: "ETH", assetName: "ETH", - amount: BigNumber.from(556863), - }, - { - chain: "ETH", - assetName: "USDT", - amount: BigNumber.from(556863), }, ] ): any { - return summa.submitProofOfSolvency( + return summa.submitCommitment( mstRoot, + rootSums, assets, - proof, BigNumber.from(1693559255) ); } @@ -52,18 +46,12 @@ describe("Summa Contract", () => { const [owner, addr1, addr2, addr3]: SignerWithAddress[] = await ethers.getSigners(); - const solvencyVerifier = await ethers.deployContract( - "src/SolvencyVerifier.sol:Verifier" - ); - await solvencyVerifier.deployed(); - const inclusionVerifier = await ethers.deployContract( "src/InclusionVerifier.sol:Verifier" ); await inclusionVerifier.deployed(); const summa = await ethers.deployContract("Summa", [ - solvencyVerifier.address, inclusionVerifier.address, ]); await summa.deployed(); @@ -112,14 +100,14 @@ describe("Summa Contract", () => { ownedAddresses = [ { chain: "ETH", - cexAddress: defaultAbiCoder.encode(["address"], [account1.address]), + cexAddress: account1.address.toString(), signature: "0x089b32327d332c295dc3b8873c205b72153211de6dc1c51235782b091cefb9d06d6df2661b86a7d441cd322f125b84901486b150e684221a7b7636eb8182af551b", message: message, }, { chain: "ETH", - cexAddress: defaultAbiCoder.encode(["address"], [account2.address]), + cexAddress: account2.address.toString(), signature: "0xb17a9e25265d3b88de7bfad81e7accad6e3d5612308ff83cc0fef76a34152b0444309e8fc3dea5139e49b6fc83a8553071a7af3d0cfd3fb8c1aea2a4c171729c1c", message: message, @@ -133,38 +121,44 @@ describe("Summa Contract", () => { .withArgs((ownedAddresses: any) => { return ( ownedAddresses[0].chain == "ETH" && - ownedAddresses[0].cexAddress == - defaultAbiCoder.encode(["address"], [account1.address]) && + ownedAddresses[0].cexAddress == account1.address && ownedAddresses[0].signature == "0x089b32327d332c295dc3b8873c205b72153211de6dc1c51235782b091cefb9d06d6df2661b86a7d441cd322f125b84901486b150e684221a7b7636eb8182af551b" && ownedAddresses[0].message == message && ownedAddresses[1].chain == "ETH" && - ownedAddresses[1].cexAddress == - defaultAbiCoder.encode(["address"], [account2.address]) && + ownedAddresses[1].cexAddress == account2.address && ownedAddresses[1].signature == "0xb17a9e25265d3b88de7bfad81e7accad6e3d5612308ff83cc0fef76a34152b0444309e8fc3dea5139e49b6fc83a8553071a7af3d0cfd3fb8c1aea2a4c171729c1c" && ownedAddresses[1].message == message ); }); - let proofOfAddressOwnership0 = await summa.addressOwnershipProofs(0); - expect(proofOfAddressOwnership0.chain).to.be.equal("ETH"); - expect(proofOfAddressOwnership0.cexAddress).to.be.equal( - defaultAbiCoder.encode(["address"], [account1.address]) + const addr1Hash = ethers.utils.solidityKeccak256( + ["string"], + [account1.address] ); - expect(proofOfAddressOwnership0.signature).to.be.equal( - "0x089b32327d332c295dc3b8873c205b72153211de6dc1c51235782b091cefb9d06d6df2661b86a7d441cd322f125b84901486b150e684221a7b7636eb8182af551b" + let proofOfAddressOwnership1 = await summa.getAddressOwnershipProof( + addr1Hash ); - expect(proofOfAddressOwnership0.message).to.be.equal(message); - let proofOfAddressOwnership1 = await summa.addressOwnershipProofs(1); expect(proofOfAddressOwnership1.chain).to.be.equal("ETH"); - expect(proofOfAddressOwnership1.cexAddress).to.be.equal( - defaultAbiCoder.encode(["address"], [account2.address]) - ); + expect(proofOfAddressOwnership1.cexAddress).to.be.equal(account1.address); expect(proofOfAddressOwnership1.signature).to.be.equal( - "0xb17a9e25265d3b88de7bfad81e7accad6e3d5612308ff83cc0fef76a34152b0444309e8fc3dea5139e49b6fc83a8553071a7af3d0cfd3fb8c1aea2a4c171729c1c" + "0x089b32327d332c295dc3b8873c205b72153211de6dc1c51235782b091cefb9d06d6df2661b86a7d441cd322f125b84901486b150e684221a7b7636eb8182af551b" ); expect(proofOfAddressOwnership1.message).to.be.equal(message); + const addr2Hash = ethers.utils.solidityKeccak256( + ["string"], + [account2.address] + ); + let proofOfAddressOwnership2 = await summa.getAddressOwnershipProof( + addr2Hash + ); + expect(proofOfAddressOwnership2.chain).to.be.equal("ETH"); + expect(proofOfAddressOwnership2.cexAddress).to.be.equal(account2.address); + expect(proofOfAddressOwnership2.signature).to.be.equal( + "0xb17a9e25265d3b88de7bfad81e7accad6e3d5612308ff83cc0fef76a34152b0444309e8fc3dea5139e49b6fc83a8553071a7af3d0cfd3fb8c1aea2a4c171729c1c" + ); + expect(proofOfAddressOwnership2.message).to.be.equal(message); }); it("should revert if the caller is not the owner", async () => { @@ -207,14 +201,24 @@ describe("Summa Contract", () => { summa.submitProofOfAddressOwnership(ownedAddresses) ).to.be.revertedWith("Invalid proof of address ownership"); }); + + it("should revert if requesting proof for unverified address", async () => { + const addr1Hash = ethers.utils.solidityKeccak256( + ["string"], + [account1.address] + ); + await expect( + summa.getAddressOwnershipProof(addr1Hash) + ).to.be.revertedWith("Address not verified"); + }); }); describe("verify proof of solvency", () => { let mstRoot: BigNumber; + let rootSum: BigNumber; let summa: Summa; let account1: SignerWithAddress; let account2: SignerWithAddress; - let proof: string; //let ethAccount3; let ownedAddresses: Summa.AddressOwnershipProofStruct[]; const message = ethers.utils.defaultAbiCoder.encode( @@ -231,143 +235,120 @@ describe("Summa Contract", () => { ownedAddresses = [ { chain: "ETH", - cexAddress: defaultAbiCoder.encode(["address"], [account1.address]), + cexAddress: account1.address.toString(), signature: "0x089b32327d332c295dc3b8873c205b72153211de6dc1c51235782b091cefb9d06d6df2661b86a7d441cd322f125b84901486b150e684221a7b7636eb8182af551b", message: message, }, { chain: "ETH", - cexAddress: defaultAbiCoder.encode(["address"], [account2.address]), + cexAddress: account2.address.toString(), signature: "0xb17a9e25265d3b88de7bfad81e7accad6e3d5612308ff83cc0fef76a34152b0444309e8fc3dea5139e49b6fc83a8553071a7af3d0cfd3fb8c1aea2a4c171729c1c", message: message, }, ]; - const jsonData = fs.readFileSync( - path.resolve( - __dirname, - "../../zk_prover/examples/solvency_proof_solidity_calldata.json" - ), - "utf-8" + mstRoot = BigNumber.from( + "0x2e021d9bf99c5bd7267488b6a7a5cf5f7d00222a41b6a9b971899c44089e0c5" ); - const calldata: any = JSON.parse(jsonData); - - mstRoot = calldata.public_inputs[0]; - proof = calldata.proof; + rootSum = BigNumber.from(10000000); }); it("should verify the proof of solvency for the given public input", async () => { await summa.submitProofOfAddressOwnership(ownedAddresses); - await expect(submitProofOfSolvency(summa, mstRoot, proof)) - .to.emit(summa, "SolvencyProofSubmitted") + await expect(submitCommitment(summa, mstRoot, [rootSum])) + .to.emit(summa, "LiabilitiesCommitmentSubmitted") .withArgs( BigNumber.from(1693559255), mstRoot, - (assets: Summa.AssetStruct[]) => { - return ( - assets[0].chain == "ETH" && - assets[0].assetName == "ETH" && - BigNumber.from(556863).eq(assets[0].amount as BigNumber) && - assets[1].chain == "ETH" && - assets[1].assetName == "USDT" && - BigNumber.from(556863).eq(assets[1].amount as BigNumber) - ); + [rootSum], + (assets: [Summa.AssetStruct]) => { + return assets[0].chain == "ETH" && assets[0].assetName == "ETH"; } ); }); it("should revert if the caller is not the owner", async () => { await expect( - summa.connect(account2).submitProofOfSolvency( + summa.connect(account2).submitCommitment( mstRoot, + [BigNumber.from(1000000000)], [ { chain: "ETH", assetName: "ETH", - amount: BigNumber.from(556863), - }, - { - chain: "ETH", - assetName: "USDT", - amount: BigNumber.from(556863), }, ], - proof, BigNumber.from(1693559255) ) ).to.be.revertedWith("Ownable: caller is not the owner"); }); - it("should not verify the proof of solvency if the CEX hasn't proven the address ownership", async () => { - await expect( - submitProofOfSolvency(summa, mstRoot, proof) - ).to.be.revertedWith( - "The CEX has not submitted any address ownership proofs" - ); - }); - - it("should revert with invalid MST root", async () => { - mstRoot = BigNumber.from(0); + it("should revert with invalid root sum", async () => { + rootSum = BigNumber.from(0); await summa.submitProofOfAddressOwnership(ownedAddresses); await expect( - submitProofOfSolvency(summa, mstRoot, proof) - ).to.be.revertedWith("Invalid ZK proof"); + submitCommitment(summa, mstRoot, [rootSum]) + ).to.be.revertedWith("All root sums should be greater than zero"); }); it("should revert with invalid assets", async () => { await summa.submitProofOfAddressOwnership(ownedAddresses); await expect( - submitProofOfSolvency(summa, mstRoot, proof, [ - { - chain: "", - assetName: "ETH", - amount: BigNumber.from(556863), - }, - ]) + submitCommitment( + summa, + mstRoot, + [rootSum], + [ + { + chain: "", + assetName: "ETH", + }, + ] + ) ).to.be.revertedWith("Invalid asset"); await expect( - submitProofOfSolvency(summa, mstRoot, proof, [ - { - chain: "ETH", - assetName: "", - amount: BigNumber.from(556863), - }, - ]) + submitCommitment( + summa, + mstRoot, + [rootSum], + [ + { + chain: "ETH", + assetName: "", + }, + ] + ) ).to.be.revertedWith("Invalid asset"); }); - it("should revert with invalid proof", async () => { - await summa.submitProofOfAddressOwnership(ownedAddresses); - - proof = proof.replace("1", "2"); - + it("should not submit invalid root", async () => { await expect( - submitProofOfSolvency(summa, mstRoot, proof) - ).to.be.revertedWith("Invalid ZK proof"); - - proof = "0x000000"; + submitCommitment(summa, BigNumber.from(0), [rootSum]) + ).to.be.revertedWith("Invalid MST root"); + }); + it("should revert if asset and sum count don't match", async () => { await expect( - submitProofOfSolvency(summa, mstRoot, proof) - ).to.be.revertedWithoutReason(); + submitCommitment(summa, mstRoot, [rootSum, rootSum]) + ).to.be.revertedWith("Root asset sums and asset number mismatch"); }); }); describe("verify proof of inclusion", () => { let mstRoot: BigNumber; + let rootSum: BigNumber; let leafHash: BigNumber; let summa: Summa; let account1: SignerWithAddress; let account2: SignerWithAddress; let inclusionProof: string; - let solvencyProof: string; let ownedAddresses: Summa.AddressOwnershipProofStruct[]; const message = ethers.utils.defaultAbiCoder.encode( ["string"], @@ -397,18 +378,6 @@ describe("Summa Contract", () => { }, ]; - const solvencyJson = fs.readFileSync( - path.resolve( - __dirname, - "../../zk_prover/examples/solvency_proof_solidity_calldata.json" - ), - "utf-8" - ); - const solvencyCalldata: any = JSON.parse(solvencyJson); - - mstRoot = solvencyCalldata.public_inputs[0]; - solvencyProof = solvencyCalldata.proof; - const inclusionJson = fs.readFileSync( path.resolve( __dirname, @@ -421,11 +390,12 @@ describe("Summa Contract", () => { leafHash = inclusionCalldata.public_inputs[0]; mstRoot = inclusionCalldata.public_inputs[1]; inclusionProof = inclusionCalldata.proof; + rootSum = BigNumber.from(10000000); }); it("should verify the proof of inclusion for the given public input", async () => { await summa.submitProofOfAddressOwnership(ownedAddresses); - await submitProofOfSolvency(summa, mstRoot, solvencyProof); + await submitCommitment(summa, mstRoot, [rootSum]); expect( await verifyInclusionProof(summa, inclusionProof, leafHash, mstRoot) ).to.be.equal(true); @@ -433,7 +403,7 @@ describe("Summa Contract", () => { it("should not verify with invalid MST root", async () => { await summa.submitProofOfAddressOwnership(ownedAddresses); - await submitProofOfSolvency(summa, mstRoot, solvencyProof); + await submitCommitment(summa, mstRoot, [rootSum]); mstRoot = BigNumber.from(0); await expect( verifyInclusionProof(summa, inclusionProof, leafHash, mstRoot) @@ -451,7 +421,7 @@ describe("Summa Contract", () => { leafHash = BigNumber.from(0); await summa.submitProofOfAddressOwnership(ownedAddresses); - await submitProofOfSolvency(summa, mstRoot, solvencyProof); + await submitCommitment(summa, mstRoot, [rootSum]); expect( await verifyInclusionProof(summa, inclusionProof, leafHash, mstRoot) ).to.be.equal(false); @@ -461,7 +431,7 @@ describe("Summa Contract", () => { inclusionProof = inclusionProof.replace("1", "2"); await summa.submitProofOfAddressOwnership(ownedAddresses); - await submitProofOfSolvency(summa, mstRoot, solvencyProof); + await submitCommitment(summa, mstRoot, [rootSum]); expect( await verifyInclusionProof(summa, inclusionProof, leafHash, mstRoot) ).to.be.equal(false); diff --git a/zk_prover/README.md b/zk_prover/README.md index 704b5866..af77f651 100644 --- a/zk_prover/README.md +++ b/zk_prover/README.md @@ -38,7 +38,7 @@ The script will generate a new `SolvencyVerifier.sol` and `SolvencyVerifier.yul` Note that the generic parameters of the circuits `N_ASSETS` and `N_BYTES` are set to `2` and `14`. This means that the circuit is tuned to verify the proof of solvency for an exchange with 2 assets and a balances in a range of 14 bytes. These parameters can be changed in the script. -Furthermore, the verifier are generated based on an unsafe setup. For a production ready verifier, the setup should be generated by providing a `ptau` file generated after a trusted setup ceremony to `generate_setup_artifacts` function. +Furthermore, the verifier is generated based on a specified `ptau` file, `hermez-raw-11`, for the generic parameters (`N_ASSETS`, `N_BYTES`), using the `generate_setup_artifacts` function. If you try to use different generic parameters, you may have to choose a different `ptau` file for that. On top of that the script will also generate a `solvency_proof_solidity_calldata.json` file that contains some testing calldata to be used within `contracts` and `backend` to test the verifier. Again, in the example, the proof is generated based on the `src/merkle_sum_tree/csv/entry_16.csv` file. If you want to generate a proof for a different file, you can change the path in the script. diff --git a/zk_prover/examples/gen_solvency_verifier.rs b/zk_prover/examples/gen_solvency_verifier.rs index 5bfc9430..2680fdd8 100644 --- a/zk_prover/examples/gen_solvency_verifier.rs +++ b/zk_prover/examples/gen_solvency_verifier.rs @@ -26,7 +26,9 @@ fn main() { let circuit = SolvencyCircuit::::init_empty(); // generate a universal trusted setup for testing, along with the verification key (vk) and the proving key (pk). - let (params, pk, _) = generate_setup_artifacts(10, None, circuit.clone()).unwrap(); + let (params, pk, _) = + generate_setup_artifacts(10, Some("../backend/ptau/hermez-raw-11"), circuit.clone()) + .unwrap(); let num_instances = circuit.num_instance(); diff --git a/zk_prover/src/merkle_sum_tree/utils/generate_leaf_hash.rs b/zk_prover/src/merkle_sum_tree/utils/generate_leaf_hash.rs index 8effd32a..13b264ba 100644 --- a/zk_prover/src/merkle_sum_tree/utils/generate_leaf_hash.rs +++ b/zk_prover/src/merkle_sum_tree/utils/generate_leaf_hash.rs @@ -1,9 +1,9 @@ -use halo2_proofs::halo2curves::bn256::Fr as Fp; +use ethers::types::U256; use num_bigint::BigUint; use crate::merkle_sum_tree::Entry; -pub fn generate_leaf_hash(user_name: String, balances: Vec) -> Fp +pub fn generate_leaf_hash(user_name: String, balances: Vec) -> U256 where [usize; N_ASSETS + 1]: Sized, { @@ -13,5 +13,7 @@ where let entry: Entry = Entry::new(user_name, balances_big_uint.try_into().unwrap()).unwrap(); - entry.compute_leaf().hash + // Convert Fp to U256 + let hash_str = format!("{:?}", entry.compute_leaf().hash); + U256::from_str_radix(&hash_str, 16).unwrap() }