diff --git a/backend/README.md b/backend/README.md index ae48b96f..3a3eec46 100644 --- a/backend/README.md +++ b/backend/README.md @@ -63,36 +63,37 @@ The following steps are optional and are only required if you need to update the By completing these steps, the backend will be primed with the essential verifiers for its tasks. -## Example +## Summa solvency flow example -The sequence in which the examples are introduced closely relates to the steps of the Summa protocol. -These examples will help to understand how the Summa works with the Summa contract and the user side. +This example illustrates how Summa interacts with the Summa contract and the user side. + +To execute this example, use the command: + +``` +cargo run --release --example summa_solvency_flow +``` ### 1. Submitting Address Ownership to the Summa Contract -This example demonstrates the process of submitting proof of address ownership to the Summa contract. After generating signatures for asset ownership (as shown in the `generate_signature` example), this step is essential to register those proofs on-chain, facilitating the validation of asset ownership within Summa. +First, we submit proof of address ownership to the Summa contract. This is a critical step to register these proofs on-chain, facilitating the validation of asset ownership within Summa. -In this example, a test environment is set up with the anvil instance by invoking the `initialize_test_env` method. This environment is also utilized in other examples such as `submit_solvency` and `verify_inclusion_on_contracts`. +Key points: -Key points to note: +- An instance of `AddressOwnership`, named `address_ownership_client`, is initialized with the `signatures.csv` file, which contains the signature data. -The instance of `AddressOwnership` is initialized with `signatures.csv`, and is named `address_ownership_client`. This instance has already loaded the signature data. +- The `dispatch_proof_of_address_ownership` function sends a transaction to the Summa contract to register CEX-owned addresses. -The `dispatch_proof_of_address_ownership` function sends a transaction to the Summa contract, registering the addresses owned by the CEX on the contract. +- After dispatching the transaction, the example computes the hashed addresses (address_hashes) to verify they've been correctly registered in the Summa contract -After dispatching the transaction via `dispatch_proof_of_address_ownerhip`, the example computes the hashed addresses (address_hashes) to verify they have been correctly registered on the Summa contract. -To execute this example: -``` -cargo run --release --example submit_ownership -``` +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: -Upon successful execution, you should see the message: ``` -Ownership proofs are submitted successfully! +1. Ownership proofs are submitted successfully! ``` -Reminder: 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. ### 2. Submit Proof of Solvency @@ -103,67 +104,45 @@ Without this process, It seems the user may not trust to the inclusion proof for In this example, we'll guide 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, several parameters are required, including paths to specific CSV files (`assets.csv` and `entry_16.csv`), as well as a path to the ptau file (`ptau/hermez-raw-11`). - -The roles of these files are as follows: -- `assets.csv`: This file is essential for calculating the total balance of assets for the solvency proof. Currently, only the CEX can generate this asset CSV file in its specific manner. +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: -- `entry_16.csv`: This file is used to build the Merkle sum tree, where each leaf element originates from sixteen entries in the CSV. -X -- `ptau/hermez-raw-11`: Contains the Powers of Tau trusted setup parameters, essential for constructing the zk circuits. +- `assets.csv`: Calculates the total balance of assets for the solvency proof. Only the CEX can generate this file. +- `entry_16.csv`: Used to build the Merkle sum tree, with each leaf element derived from sixteen entries in the CSV. +- `ptau/hermez-raw-11`: Contains parameters for constructing the zk circuits. -An instance of Round dispatches the solvency proof using the `dispatch_solvency_proof` method. - -To execute this example: -``` -cargo run --release --example submit_solvency -``` +Using the `Round` instance, the solvency proof is dispatched to the Summa contract with the `dispatch_solvency_proof` method. -Upon successful execution, you will see the message: +If this step successfully ran, you can see this message: ``` - "Solvency proof is submitted successfully!" +2. Solvency proof is submitted successfully! ``` ### 3. Generating and Exporting Inclusion Proofs -Assuming you are a CEX, let's say you've already committed the `solvency` and `ownership` proofs to the Summa contract. Now, you need to generate inclusion proofs for every user. - -In this example, we demonstrate how to generate and export user-specific inclusion proofs using the Round. This proof is crucial for users as it helps them validate the presence of specific elements within the Merkle sum tree, which forms a part of the solvency proof submitted. - -After generating the inclusion proof, end of example parts the inclusion proof is transformed into a JSON format, making it easily shareable. +Assuming you're a CEX, after committing the `solvency` and `ownership` proofs to the Summa contract, you should generate inclusion proofs for every user. This proof verifies the presence of specific elements in the Merkle sum tree, which is part of the solvency proof. -To execute this example: -``` -cargo run --release --example generate_inclusion -``` +After generating the inclusion proof, it's transformed into a JSON format for easy sharing. -Upon successful execution, you can see this message and exported file `user_0_proof.json`. +Upon successful execution, you'll find a file named `user_0_proof.json` and see the following message: ``` - "Exported proof to user #0, as `user_0_proof.json`" +3. Exported proof to user #0, as `user_0_proof.json` ``` ### 4. Verify Proof of Inclusion This is the final step in the Summa process and the only part that occurs on the user side. -The user will receive the proof for a specific Round. the user can use method on the Summa contract that already deployed on blockchain. It's important to note that the verifier function on the Summa contract is a view function, which means it doesn't require any gas or involve writing state on-chain. +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 the `verify_inclusion_on_contract` example, the procedure for verifying the inclusion proof using an on-chain method is illustrated. By leveraging the data from the Summa contract, users can effortlessly ascertain that the provided proof aligns with the data submitted by the CEX. +In this step, you'll see: -To elaborate: - -Retrieving the MST Root: The user fetches the `mst_root` from the Summa contract. This root should match the `root_hash` provided in the proof. This verification process is akin to ensuring that the `leaf_hash` corresponds with the anticipated hash based on the `username` and `balances` provided by the CEX. - -On-chain Function Verification: The user then invokes the `verify_inclusion_proof` method on the Summa contract. Since this is a view function, it returns a boolean value without incurring any gas fees, indicating the success or failure of the verification. - -To run the verify inclusion on contract example: -``` -cargo run --release --example verify_inclusion_on_contract -``` +- 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. -You will see the result like: +The result will display as: ``` -Verifying the proof on contract verifier for User #0: true +4. Verifying the proof on contract verifier for User #0: true ``` diff --git a/backend/examples/generate_inclusion.rs b/backend/examples/generate_inclusion.rs deleted file mode 100644 index 0d1a4fc6..00000000 --- a/backend/examples/generate_inclusion.rs +++ /dev/null @@ -1,55 +0,0 @@ -#![feature(generic_const_exprs)] -use serde_json::{json, to_writer}; -use std::fs; -use summa_backend::{apis::round::Round, tests::initialize_test_env}; - -const USER_INDEX: usize = 0; - -#[tokio::main] -async fn main() { - // Initialize test environment - let (anvil, _, _, _, summa_contract, mut address_ownership_client) = - initialize_test_env().await; - - address_ownership_client - .dispatch_proof_of_address_ownership() - .await - .unwrap(); - - // Initialize `Round` for submitting proof of solvency. - let asset_csv = "src/apis/csv/assets.csv"; - let entry_csv = "../zk_prover/src/merkle_sum_tree/csv/entry_16.csv"; - let params_path = "ptau/hermez-raw-11"; - - let round = Round::<4, 2, 14>::new( - "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", // anvil account [0] - anvil.chain_id(), - anvil.endpoint().as_str(), - summa_contract.address(), - entry_csv, - asset_csv, - params_path, - 1, - ) - .unwrap(); - - // In a production environment, the CEX should dispatch the solvency proof to update the root of the Merkle sum tree prior to generating inclusion proofs. - // Otherwise, users might distrust the provided `root_hash` in the inclusion proof, as it hasn't been published on-chain. - let inclusion_proof = round.get_proof_of_inclusion(USER_INDEX).unwrap(); - let public_input_vec = inclusion_proof.get_public_inputs(); - - // The structure of this output file may vary in production. - // For instance, the CEX might substitute `leaf_hash` with attributes like `username` and `balances`. - // Consequently, users would generate the `leaf_hash` on client-side before validating the proof. - let output = json!({ - "proof": serde_json::to_string(&inclusion_proof.get_proof()).unwrap(), - "leaf_hash": serde_json::to_string(&public_input_vec[0][0]).unwrap(), - "root_hash": serde_json::to_string(&public_input_vec[0][1]).unwrap() - }); - - let filename = format!("user_{}_proof.json", USER_INDEX); - let file = fs::File::create(filename.clone()).expect("Unable to create file"); - to_writer(file, &output).expect("Failed to write JSON to file"); - - println!("Exported proof to user #{}, as `{}`", USER_INDEX, filename); -} diff --git a/backend/examples/helpers/inclusion_proof.rs b/backend/examples/helpers/inclusion_proof.rs deleted file mode 100644 index 083ebedd..00000000 --- a/backend/examples/helpers/inclusion_proof.rs +++ /dev/null @@ -1,8 +0,0 @@ -use serde::Deserialize; - -#[derive(Debug, Deserialize)] -pub struct InclusionProof { - pub leaf_hash: String, - pub root_hash: String, - pub proof: String, -} diff --git a/backend/examples/helpers/mod.rs b/backend/examples/helpers/mod.rs deleted file mode 100644 index 55868e1e..00000000 --- a/backend/examples/helpers/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod inclusion_proof; diff --git a/backend/examples/submit_ownership.rs b/backend/examples/submit_ownership.rs deleted file mode 100644 index b1686604..00000000 --- a/backend/examples/submit_ownership.rs +++ /dev/null @@ -1,67 +0,0 @@ -use ethers::{ - abi::{encode, Token}, - types::U256, - utils::keccak256, -}; - -use summa_backend::{apis::address_ownership::AddressOwnership, tests::initialize_test_env}; - -// In this example, we will demonstrate how to submit ownership of address to the Summa contract. -#[tokio::main] -async fn main() { - // We have already demonstrated how to generate a CSV file containing the asset ownership proofs, `AddressOwnershipProof`. - // For more details on this, kindly refer to the "generate_signature" example. - - // Initialize test environment without `address_ownership` instance from `initialize_test_env` function. - let (anvil, _, _, _, summa_contract, _) = initialize_test_env().await; - - // For the current demonstration, we'll use the same CSV file produced in `generate_signature` example. - let signature_csv_path = "src/apis/csv/signatures.csv"; - let mut address_ownership_client = AddressOwnership::new( - "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", - anvil.chain_id(), - anvil.endpoint().as_str(), - summa_contract.address(), - signature_csv_path, - ) - .unwrap(); - - // Get 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::>(); - - // Dispatches the proof of address ownership. - // In the client, the `dispatch_proof_of_address_ownership` function sends a transaction to the Summa contract - address_ownership_client - .dispatch_proof_of_address_ownership() - .await - .unwrap(); - - // 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); - - // Check if the addresses are registered on 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!("Ownership proofs are submitted successfully!") -} diff --git a/backend/examples/submit_solvency.rs b/backend/examples/submit_solvency.rs deleted file mode 100644 index 512463f8..00000000 --- a/backend/examples/submit_solvency.rs +++ /dev/null @@ -1,46 +0,0 @@ -#![feature(generic_const_exprs)] -use summa_backend::{apis::round::Round, tests::initialize_test_env}; - -#[tokio::main] -async fn main() { - // Initialize test environment. - let (anvil, _, _, _, summa_contract, mut address_ownership_client) = - initialize_test_env().await; - - // Before submitting the solvency proof, it's crucial to submit the proof of address ownership. - // Ensure at least one address is registered in the `addressOwnershipProofs` mapping of the Summa contract. - address_ownership_client - .dispatch_proof_of_address_ownership() - .await - .unwrap(); - - // Initialize `Round` for submitting proof of solvency. - let asset_csv = "src/apis/csv/assets.csv"; - let entry_csv = "../zk_prover/src/merkle_sum_tree/csv/entry_16.csv"; - let params_path = "ptau/hermez-raw-11"; - - // Using the `round` instance, the solvency proof is dispatched to the Summa contract with the `dispatch_solvency_proof` method. - let mut round = Round::<4, 2, 14>::new( - "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", // anvil account [0] - anvil.chain_id(), - anvil.endpoint().as_str(), - summa_contract.address(), - entry_csv, - asset_csv, - params_path, - 1, - ) - .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();); - - println!("Solvency proof is submitted successfully!") -} diff --git a/backend/examples/summa_solvency_flow.rs b/backend/examples/summa_solvency_flow.rs new file mode 100644 index 00000000..76a0861d --- /dev/null +++ b/backend/examples/summa_solvency_flow.rs @@ -0,0 +1,190 @@ +#![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 serde_json::{from_reader, to_string_pretty}; + +use summa_backend::{ + apis::{ + address_ownership::AddressOwnership, + round::{MstInclusionProof, Round}, + }, + tests::initialize_test_env, +}; +use summa_solvency::merkle_sum_tree::utils::generate_leaf_hash; + +const N_ASSETS: usize = 2; +const USER_INDEX: usize = 0; + +#[tokio::main] +async fn main() -> Result<(), Box> { + // Initialize test environment without `address_ownership` instance from `initialize_test_env` function. + let (anvil, _, _, _, summa_contract) = initialize_test_env().await; + + // 1. Submit ownership proof + // + // Each CEX prepares its own `signature` CSV file. + let signature_csv_path = "src/apis/csv/signatures.csv"; + let mut address_ownership_client = AddressOwnership::new( + "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", + anvil.chain_id(), + anvil.endpoint().as_str(), + 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(); + + // 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 + // + // Initialize the `Round` instance to submit the proof of solvency. + let asset_csv = "src/apis/csv/assets.csv"; + let entry_csv = "../zk_prover/src/merkle_sum_tree/csv/entry_16.csv"; + let params_path = "ptau/hermez-raw-11"; + + // Using the `round` instance, the solvency proof is dispatched to the Summa contract with the `dispatch_solvency_proof` method. + let mut round = Round::<4, 2, 14>::new( + "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", // anvil account [0] + anvil.chain_id(), + anvil.endpoint().as_str(), + summa_contract.address(), + entry_csv, + asset_csv, + params_path, + 1, + ) + .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();); + + 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. + let inclusion_proof = round.get_proof_of_inclusion(USER_INDEX).unwrap(); + + let filename = format!("user_{}_proof.json", USER_INDEX); + let mut file = File::create(filename.clone()).expect("Unable to create file"); + let output = to_string_pretty(&inclusion_proof).unwrap(); + file.write_all(output.as_bytes()) + .expect("Failed to write JSON to file"); + + println!( + "3. Exported proof to user #{}, as `{}`", + USER_INDEX, filename + ); + + // 4. Verify Inclusion Proof + // + // The `snapshot_time` denotes the specific moment when entries were created for the Merkle sum tree. + // This timestamp is established during the initialization of the Round instance. + let snapshot_time = U256::from(1); + + // When verifying the inclusion proof from the user's perspective, the user have to fetch `proof`. + // Assume that the `proof` file has been downloaded from the CEX. + let proof_file = File::open(format!("user_{}_proof.json", USER_INDEX))?; + let reader = BufReader::new(proof_file); + let downloaded_inclusion_proof: MstInclusionProof = from_reader(reader)?; + + let public_inputs = downloaded_inclusion_proof.get_public_inputs(); + + // Verify the `leaf_hash` from the proof file. + // It's assumed that both `user_name` and `balances` are provided by the CEX. + let user_name = "dxGaEAii".to_string(); + let balances = vec![11888, 41163]; + + let leaf_hash = public_inputs[0][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(); + + // 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 verification_result = summa_contract + .verify_inclusion_proof(proof, public_inputs, snapshot_time) + .await + .unwrap(); + + println!( + "4. Verifying the proof on contract veirifer for User #{}: {}", + USER_INDEX, verification_result + ); + + Ok(()) +} diff --git a/backend/examples/verify_inclusion_on_contract.rs b/backend/examples/verify_inclusion_on_contract.rs deleted file mode 100644 index 08799377..00000000 --- a/backend/examples/verify_inclusion_on_contract.rs +++ /dev/null @@ -1,99 +0,0 @@ -#![feature(generic_const_exprs)] -use std::{error::Error, fs::File, io::BufReader}; - -use ethers::types::{Bytes, U256}; -use halo2_proofs::halo2curves::bn256::Fr as Fp; -use serde_json::from_reader; - -use summa_backend::{apis::round::Round, tests::initialize_test_env}; -use summa_solvency::merkle_sum_tree::utils::generate_leaf_hash; -mod helpers; -use helpers::inclusion_proof::InclusionProof; - -#[tokio::main] -async fn main() -> Result<(), Box> { - // Let assume the user can get instance of `summa_contract` and the CEX already submit solvency at timestamp `1`. - let (anvil, _, _, _, summa_contract) = initialize_test_env().await; - - address_ownership_client - .dispatch_proof_of_address_ownership() - .await - .unwrap(); - - // Initialize `Round` for submitting proof of solvency. - let asset_csv = "src/apis/csv/assets.csv"; - let entry_csv = "../zk_prover/src/merkle_sum_tree/csv/entry_16.csv"; - let params_path = "ptau/hermez-raw-11"; - - let mut round = Round::<4, 2, 14>::new( - "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", // anvil account [0] - anvil.chain_id(), - anvil.endpoint().as_str(), - summa_contract.address(), - entry_csv, - asset_csv, - params_path, - 1, - ) - .unwrap(); - - assert_eq!(round.dispatch_solvency_proof().await.unwrap(), ()); - - // The user will know these constants before or when they receive the proof. - const N_ASSETS: usize = 2; - const USER_INDEX: usize = 0; - - let snapshot_time = U256::from(1); // specific time to dispatch solvency proof - - // When verifying the inclusion proof on the user side,-raw-11"; - let proof_path = format!("user_{}_proof.json", USER_INDEX); - - let file = File::open(proof_path)?; - let reader = BufReader::new(file); - let proof_data: InclusionProof = from_reader(reader)?; - let proof: Vec = serde_json::from_str(&proof_data.proof).unwrap(); - - // These `user_name` and `balances` be assumed that are given from the CEX. - let user_name = "dxGaEAii".to_string(); - let balances = vec![11888, 41163]; - - let leaf_hash: Fp = serde_json::from_str(&proof_data.leaf_hash).unwrap(); - assert_eq!( - leaf_hash, - generate_leaf_hash::(user_name.clone(), balances.clone()) - ); - - // Make public_input from `leaf_hash` and `root_hash` iter and convert it to `Fr` - let root_hash: Fp = serde_json::from_str(&proof_data.root_hash).unwrap(); - let public_inputs: Vec = vec![leaf_hash, root_hash] - .iter() - .map(|x| { - let mut bytes = x.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(); - - // Compare `mst_root` with `root_hash` from proof. - assert_eq!(mst_root, public_inputs[1]); - - // Verify inclusion proof on contract verifier - let verification_result = summa_contract - .verify_inclusion_proof(Bytes::from(proof), public_inputs, snapshot_time) - .await - .unwrap(); - - println!( - "Verifying the proof on contract veirifer for User #{}: {}", - USER_INDEX, verification_result - ); - - Ok(()) -} diff --git a/backend/src/apis/round.rs b/backend/src/apis/round.rs index 2c8a3c3f..a344c84a 100644 --- a/backend/src/apis/round.rs +++ b/backend/src/apis/round.rs @@ -7,6 +7,7 @@ use halo2_proofs::{ 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; @@ -43,7 +44,7 @@ impl SolvencyProof { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct MstInclusionProof { public_inputs: Vec>, proof: Vec, diff --git a/backend/src/tests.rs b/backend/src/tests.rs index 04aab334..faf41cee 100644 --- a/backend/src/tests.rs +++ b/backend/src/tests.rs @@ -9,7 +9,6 @@ use ethers::{ }; use tokio::time; -use crate::apis::address_ownership::AddressOwnership; use crate::contracts::generated::{ inclusion_verifier::InclusionVerifier, solvency_verifier::SolvencyVerifier, summa_contract::Summa, @@ -109,7 +108,7 @@ mod test { utils::to_checksum, }; - use crate::apis::round::Round; + use crate::apis::{address_ownership::AddressOwnership, round::Round}; use crate::contracts::generated::summa_contract::{ AddressOwnershipProof, AddressOwnershipProofSubmittedFilter, Asset, SolvencyProofSubmittedFilter, @@ -118,8 +117,16 @@ mod test { #[tokio::test] async fn test_round_features() { - let (anvil, cex_addr_1, cex_addr_2, _, summa_contract, mut address_ownership_client) = - initialize_test_env().await; + 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(), + "src/apis/csv/signatures.csv", + ) + .unwrap(); let ownership_submitted_result = address_ownership_client .dispatch_proof_of_address_ownership() diff --git a/zk_prover/src/circuits/utils.rs b/zk_prover/src/circuits/utils.rs index 617cf84d..96bb2bcb 100644 --- a/zk_prover/src/circuits/utils.rs +++ b/zk_prover/src/circuits/utils.rs @@ -17,7 +17,7 @@ use halo2_proofs::{ kzg::{ commitment::{KZGCommitmentScheme, ParamsKZG}, multiopen::{ProverSHPLONK, VerifierSHPLONK}, - strategy::{AccumulatorStrategy, SingleStrategy}, + strategy::SingleStrategy, }, }, transcript::{ @@ -29,7 +29,7 @@ use regex_simple::Regex; use snark_verifier::{ cost::CostEstimation, pcs::kzg::{Bdfg21, KzgAs}, - system::halo2::{compile, transcript::evm::EvmTranscript, Config}, + system::halo2::{compile, Config}, verifier::plonk::PlonkSuccinctVerifier, }; use snark_verifier_sdk::{evm::gen_evm_proof_shplonk, CircuitExt}; @@ -134,27 +134,6 @@ pub fn full_verifier( .is_ok() } -/// Verifies a proof that generated by the `gen_evm_proof_shplonk` function. -pub fn full_evm_verifier( - params: &ParamsKZG, - vk: &VerifyingKey, - proof: Vec, - public_inputs: Vec>, -) -> bool { - let instance: Vec<&[Fp]> = public_inputs.iter().map(|input| &input[..]).collect(); - let instances = &[&instance[..]]; - - let mut transcript = TranscriptReadBuffer::<_, G1Affine, _>::init(proof.as_slice()); - verify_proof::<_, VerifierSHPLONK<'_, Bn256>, _, EvmTranscript<_, _, _, _>, _>( - params, - vk, - AccumulatorStrategy::new(params.verifier_params()), - instances, - &mut transcript, - ) - .is_ok() -} - /// Generate a solidity verifier contract starting from its yul code. /// patterned after https://github.com/zkonduit/ezkl/blob/main/src/eth.rs#L326-L602 fn fix_verifier_sol(yul_code_path: PathBuf) -> Result> { 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 new file mode 100644 index 00000000..8effd32a --- /dev/null +++ b/zk_prover/src/merkle_sum_tree/utils/generate_leaf_hash.rs @@ -0,0 +1,17 @@ +use halo2_proofs::halo2curves::bn256::Fr as Fp; +use num_bigint::BigUint; + +use crate::merkle_sum_tree::Entry; + +pub fn generate_leaf_hash(user_name: String, balances: Vec) -> Fp +where + [usize; N_ASSETS + 1]: Sized, +{ + // Convert usize to BigInt for the `Entry` struct + let balances_big_uint: Vec = balances.into_iter().map(BigUint::from).collect(); + + let entry: Entry = + Entry::new(user_name, balances_big_uint.try_into().unwrap()).unwrap(); + + entry.compute_leaf().hash +}