Skip to content

Commit

Permalink
Refactored backend for updated v2 contracts (#270)
Browse files Browse the repository at this point in the history
  • Loading branch information
sifnoc authored Mar 4, 2024
1 parent 627e382 commit 66ebc91
Show file tree
Hide file tree
Showing 22 changed files with 36,637 additions and 59,369 deletions.
31 changes: 13 additions & 18 deletions backend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,15 @@ Key Features:

## Prerequisites

The `ptau` file, containing the Powers of Tau trusted setup parameters needed to build the zk circuits, is already included. However, if you wish to test or run the code with a higher number of entries, you may choose to download a different `ptau` file.
Before testing or running the Summa backend, the ptau file, containing the Powers of Tau trusted setup parameters essential for building the ZK circuits, must be downloaded. Specifically, the `hermez-raw-17` file is required for the [Summa flow](./examples/summa_solvency_flow.rs) example and its associated test case.

You can find the necessary files at https://github.com/han0110/halo2-kzg-srs. To download a specific file, you can use:
You can find this and other necessary files at https://github.com/han0110/halo2-kzg-srs. To download `hermez-raw-17`, use the command:

```
wget https://trusted-setup-halo2kzg.s3.eu-central-1.amazonaws.com/hermez-raw-11
wget https://trusted-setup-halo2kzg.s3.eu-central-1.amazonaws.com/hermez-raw-17
```

After downloading, pass the path to the desired file to the `Snapshot::new` method. If you are using the included `ptau` file, no additional steps are necessary.
Ensure this file is downloaded before proceeding with the example or test case.

## Running Test

Expand All @@ -50,7 +50,7 @@ cargo test --release -- --nocapture

### Generating and updating verifier contract for Backend

The verifier contract in the backend were generated using a predefined set of parameters: `N_CURRENCIES = 2` and `N_BYTES=14`, as indicated [here](https://github.com/summa-dev/summa-solvency/blob/master/zk_prover/examples/gen_inclusion_verifier.rs#L21-L22).
The verifier contract in the backend were generated using a predefined set of parameters: `N_CURRENCIES = 2`, `N_USERS = 16` and `K = 17` as indicated [here](../kzg_prover/bin/gen_verifier.rs#L26-L28)
If you intend to work with different parameters, you'll need to adjust these hard-coded values and then generate new verifier contract.

The process described below assists in both generating the verifier and updating the Summa contract, which integrates the new verifier as constructors.
Expand Down Expand Up @@ -95,17 +95,17 @@ If executed successfully, you'll see:

### 2. Submit Commitment

The CEX must submit a commitment to the Summa contract for each round. This commitment consists of a `timestamp`, the root hash of the Merkle Sum Tree (`mst_root`), and `balances`.
The CEX must submit a commitment to the Summa contract for each round. This commitment consists of a timestamp, a SNARK proof, a grand sum proof and total balances.

Without publishing the commitment, users cannot verify their inclusion proof on the Summa contract. This is because the inclusion verifier function internally requires the `mst_root`, but users only know the `timestamp` associated with the round and the verifier functions does not requre `mst_root` directly.
Without CEX publishing the commitment, users cannot verify their inclusion proof on the Summa contract. This is because the inclusion verifier function internally requires the SNARK proof, but users only know the timestamp associated with the round and the verifier functions does not requre the SNARK proof directly.

In here, we'll introduce you through the process of submitting a commitment 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 the liabilities CSV file (`entry_16.csv`) and the `ptau/hermez-raw-11` file. The files serve the following purpose:
To initialize the `Round` instance, you'll need paths to the liabilities CSV file (`entry_16.csv`) and the `ptau/hermez-raw-17` file. The files serve the following purpose:

- `entry_16.csv`: contains the username and liabilities entries for each CEX user (necessary to build the commitment). Liabilities column names have the following format: `balance_<CRYPTOCURRENCY>_<CHAIN>`, where <CRYPTOCURRENCY> and <CHAIN> are the names of the cryptocurrencies and their corresponding blockchains. <CHAIN> values are the same as in the Address Ownership Proof step;
- `ptau/hermez-raw-11`: contains parameters for constructing the zk circuits.
- `ptau/hermez-raw-17`: contains parameters for constructing the zk circuits.

Using the `Round` instance, the solvency proof is dispatched to the Summa contract with the `dispatch_solvency_proof` method.

Expand All @@ -117,7 +117,7 @@ If this step successfully ran, you can see this message:

### 3. Generating and Exporting Inclusion Proofs

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.
Assuming you're a CEX, after committing the commitment 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 polynomials encoding the username, balances.

After generating the inclusion proof, it's transformed into a JSON format for easy sharing.

Expand All @@ -135,18 +135,13 @@ Users receive the proof for a specific round and use methods available on the de

In this step, the user has to:

- Ensure the `leaf_hash` (public input of the proof) aligns with the Poseidon hash of the `username` and `balances` provided by the CEX.
- Submit the proof to the `verify_inclusion_proof` method on the Summa contract Which will:
- Retrieve the `mstRoot` from the Summa contract and match it with the `root_hash` in the proof.
- Retrieve the `rootBalances` from the Summa contract and match it with the `root_balances` in the proof
- Verify the zk Proof
- Ensure the user values in the proof file aligns with `username` and `balances` provided by the CEX.
- Submit the proof to the `verify_inclusion_proof` method on the Summa contract.

The result will display as:

```
4. Verifying the proof on contract verifier for User #0: true
```

**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.
**Note:** In a production environment, users can independently verify their proof using public interfaces, such as Etherscan.
15 changes: 15 additions & 0 deletions backend/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,21 @@ fn main() {
"Summa",
"Summa",
),
(
"src/contracts/generated/verifying_key.rs",
"Halo2VerifyingKey",
"VerifyingKey",
),
(
"src/contracts/generated/snark_verifier.rs",
"Verifier",
"SnarkVerifier",
),
(
"src/contracts/generated/grandsum_verifier.rs",
"GrandSumVerifier",
"GrandSumVerifier",
),
(
"src/contracts/generated/inclusion_verifier.rs",
"InclusionVerifier",
Expand Down
76 changes: 48 additions & 28 deletions backend/examples/summa_solvency_flow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@
use std::{error::Error, fs::File, io::BufReader, io::Write};

use ethers::types::U256;
use halo2_proofs::halo2curves::bn256::{Fr as Fp, G1Affine};
use halo2_proofs::halo2curves::bn256::Fr as Fp;
use serde_json::{from_reader, to_string_pretty};

use summa_backend::{
apis::{address_ownership::AddressOwnership, round::Round},
apis::{
address_ownership::AddressOwnership,
round::{KZGProof, Round},
},
contracts::signer::{AddressInput, SummaSigner},
tests::initialize_test_env,
};
Expand All @@ -17,7 +20,7 @@ use summa_solvency::{
},
cryptocurrency::Cryptocurrency,
entry::Entry,
utils::{big_uint_to_fp, parse_csv_to_entries},
utils::parse_csv_to_entries,
};

const K: u32 = 17;
Expand Down Expand Up @@ -65,7 +68,6 @@ async fn main() -> Result<(), Box<dyn Error>> {
// 2. Submit Commitment
//
// Initialize the `Round` instance to submit the liability commitment.
let params_path = "ptau/hermez-raw-17";
let entry_csv = "../csv/entry_16.csv";
let mut entries: Vec<Entry<N_CURRENCIES>> = vec![Entry::init_empty(); N_USERS];
let mut cryptos = vec![Cryptocurrency::init_empty(); N_CURRENCIES];
Expand All @@ -77,12 +79,15 @@ async fn main() -> Result<(), Box<dyn Error>> {
UnivariateGrandSumConfig<N_CURRENCIES, N_USERS>,
>::init(entries.to_vec());

// This ptau file is also utilized in the generation of the verifier contract.
// It corresponds to the same file used in the `gen_verifier.rs` script.
let params_path = "../backend/ptau/hermez-raw-17";
let (params, pk, vk) =
generate_setup_artifacts(K, None, &univariate_grand_sum_circuit).unwrap();
generate_setup_artifacts(K, Some(params_path), &univariate_grand_sum_circuit).unwrap();

// Create a proof
let instances = vec![Fp::one(); 1]; // This instance is necessary to verify proof on solidity verifier.
let (zk_snark_proof, advice_polys, _omega) = full_prover(
let (zk_snark_proof, advice_polys, _) = full_prover(
&params,
&pk,
univariate_grand_sum_circuit.clone(),
Expand All @@ -91,12 +96,17 @@ async fn main() -> Result<(), Box<dyn Error>> {

// Using the `round` instance, the commitment is dispatched to the Summa contract with the `dispatch_commitment` method.
let timestamp = 1u64;
let mut round =
Round::<N_CURRENCIES, N_POINTS, N_USERS>::new(&signer, advice_polys, params, vk, 1)
.unwrap();
let mut round = Round::<N_CURRENCIES, N_POINTS, N_USERS>::new(
&signer,
zk_snark_proof,
advice_polys,
params,
vk,
timestamp,
);

// // Sends the commitment, which should ideally complete without errors.
// round.dispatch_commitment().await?;
// Sends the commitment, which should ideally complete without errors.
round.dispatch_commitment().await?;

println!("2. Commitment is submitted successfully!");

Expand All @@ -118,33 +128,43 @@ async fn main() -> Result<(), Box<dyn Error>> {

// 4. Verify Inclusion Proof
//
// The `snapshot_time` denotes the specific moment when entries were created for the Merkle sum tree.
// The `snapshot_time` denotes the specific moment when entries were created for polynomal encoding.
// This timestamp is established during the initialization of the Round instance.
let snapshot_time = U256::from(1);
let snapshot_time = U256::from(timestamp);

// 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: KZGInclusionProof = from_reader(reader)?;

// TODO: fix after the contract is concrete
// // Get `mst_root` from contract. the `mst_root` is disptached by CEX with specific time `snapshot_time`.
// let commitment = summa_contract.commitments(snapshot_time).call().await?;
let downloaded_inclusion_proof: KZGProof = from_reader(reader)?;

// Fetch commitment data from the contract with timestamp, `snapshot_time`.
let commitment = summa_contract.commitments(snapshot_time).call().await?;

// // Match the `mst_root` with the `root_hash` derived from the proof.
// assert_eq!(commitment, public_inputs[1]);
// Ensure the length of the commitment matches the expected size for the number of points.
assert_eq!(commitment.to_vec().len(), 0x40 * N_POINTS);

// // Validate the inclusion proof using the contract verifier.
// let proof = inclusion_proof.get_proof();
// let verification_result = summa_contract
// .verify_inclusion_proof(proof.clone(), public_inputs.clone(), snapshot_time)
// .await?;
// Validate the inclusion proof using the contract verifier.
let mut verification_result = false;

// println!(
// "4. Verifying the proof on contract veirifer for User #{}: {}",
// USER_INDEX, verification_result
// );
if let Some(challenges) = downloaded_inclusion_proof.get_challenge().as_ref() {
verification_result = summa_contract
.verify_inclusion_proof(
snapshot_time,
inclusion_proof.get_proof().clone(),
challenges.clone(),
inclusion_proof.get_input_values().clone(),
)
.await?;
} else {
eprintln!("No challenges found in the proof, This may not a inclusion proof");
}

println!(
"4. Verifying the proof on contract veirifer for User #{}: {}",
USER_INDEX, verification_result
);

// Wrapping up
drop(anvil);
Expand Down
23 changes: 8 additions & 15 deletions backend/scripts/update_verifier_contract.sh
Original file line number Diff line number Diff line change
@@ -1,30 +1,23 @@
#!/bin/bash
set -e

# Build the verifier contracts
# Build the verifier contract
echo "1. Building verifier contracts"
cd ../zk_prover
cargo run --release --example gen_inclusion_verifier
cd ../kzg_prover
cargo run --release --bin generate_verifier

# Generate Commitment for Merkle Sum Tree
echo "2. Generate Commitment for Merkle Sum Tree"
cd ../zk_prover
cargo run --release --example gen_commitment
# Generate Commitment and Proofs for Encoded Polynomials
echo "2. Generate Commitment and Proofs for Encoded Polynomials"
cd ../kzg_prover
cargo run --release --bin generate_commitment_and_proofs

# Deploy contracts to local environment
echo "3. Deploying contracts to local environment"
cd ../contracts
npm install
npx hardhat node &
HARDHAT_PID=$!
sleep 5
npx hardhat run scripts/deploy.ts --network localhost
npx hardhat run scripts/deploy.ts

# Generate interface files for Backend
echo "4. Generating interface files for Backend"
cd ../backend
cargo build

# Wrap up
echo "Terminate hardhat node"
kill $HARDHAT_PID
Loading

0 comments on commit 66ebc91

Please sign in to comment.