Skip to content

Commit

Permalink
Refactor backend following updated contracts (#152)
Browse files Browse the repository at this point in the history
  • Loading branch information
sifnoc authored Sep 12, 2023
1 parent c4edbc6 commit bbf4363
Show file tree
Hide file tree
Showing 30 changed files with 5,233 additions and 5,259 deletions.
4 changes: 2 additions & 2 deletions backend/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ futures = "0.3.28"
num-bigint = "0.4.3"
serde = { version = "1.0.166", features = ["derive"] }
snark-verifier-sdk = { git = "https://github.com/privacy-scaling-explorations/snark-verifier", version = "0.1.1" }
ethers = { version = "2.0.7", default_features = false, features = ["ethers-solc"] }
ethers = { version = "2.0.7", default-features = false, features = ["ethers-solc"] }
reqwest = { version = "0.11", features = ["json"] }
serde_json = "1.0.64"
tokio = { version = "1.7.1", features = ["full"] }
base64 = "0.13"
bincode = "1.3.3"

[build-dependencies]
ethers = { version = "2.0.7", default_features = false, features = ["ethers-solc"] }
ethers = { version = "2.0.7", default-features = false, features = ["ethers-solc"] }
88 changes: 51 additions & 37 deletions backend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,26 @@

This directory contains the backend implementation for the Summa Proof of Solvency protocol.

The core datastructure is the `Snapshot` struct, a data container for:
## Core Components

- the CEX liabilities, represented via a `MerkleSumTree`
- the CEX wallets, represented via the `WalletOwnershipProof` struct.
- the Trusted Setup parameters for the `MstInclusionCircuit` and `SolvencyCircuit` zk circuits.
### Round

Furthermore, the `Snapshot` struct contains the following methods:
The `Round` component represents a specific period or cycle in the Summa Proof of Solvency protocol. It encapsulates the state of the system at a given time, including the snapshot of assets and liabilities, as well as the associated proofs.
The `Round` struct integrates with the `Snapshot` and `SummaSigner` to facilitate the generation and submission of proofs to the contract.

- `generate_solvency_verifier` -> write the Solidity Verifier contract (for the `SolvencyProof`) to a file
- `generate_proof_of_solvency` -> generate the `SolvencyProof` for the current snapshot to be verified on-chain
- `generate_proof_of_inclusion` -> generate the `MstInclusionProof` for a specific user for the current snapshot to be verified off-chain
- `get_proof_of_address_ownership` -> generate the `AddressOwnershipProof` for a specific user for the current snapshot to be verified off-chain
Key Features:
- Initialization of a new round with specific parameters.
- Building a snapshot of the current state.
- Dispatching solvency proofs to the contract.
- Retrieving proofs of inclusion for specific users.

### AddressOwnership

The `AddressOwnership` component is responsible for managing and verifying the ownership of addresses. It ensures that addresses used in the protocol owned by the respective participants. This component interacts with the `SummaSigner` to submit proofs of address ownership to on-chain.

Key Features:
- Initialization with specific signer details.
- Dispatching proofs of address ownership to the contract.

## Prerequisites

Expand All @@ -27,49 +35,55 @@ wget https://trusted-setup-halo2kzg.s3.eu-central-1.amazonaws.com/hermez-raw-11

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.

## Important Notices

### For Proof of Solvency
## Running Test

As of the current implementation, the `generate_proof_of_solvency` method does not directly fetch data about the balances of the wallets of the CEX. Instead, you can use the `fetch_asset_sums` function to retrieve balance information from the blockchain. Here's an example of how you might utilize it:
To build the binary executable and test it

```Rust
let asset_sums = fetch_asset_sums(client, token_contracts, exchange_addresses).await?;
```
cargo build
SIGNATURE_VERIFICATION_MESSAGE="Summa proof of solvency for CryptoExchange" cargo test --release -- --nocapture
```

Please note that the first element in the `asset_sums` array represents the ETH balance.

Alternatively, you can create your own custom fetcher to retrieve the balances.

### For Proof of Ownership
## Important Notices

To generate a signed message, you must first initialize the `SummaSigner` and use the `generate_signatures` method:
### Generating Verifiers for Backend

```Rust
let signatures = signer.generate_signatures().await.unwrap();
```
The following steps are optional and are only required if you need to update the verifier contracts for the backend:

The content of the message can be specified with the local variable `SIGNATURE_VERIFICATION_MESSAGE`.
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`.

### For Generating Solvency Verifier
By completing these steps, the backend will be primed with the essential verifiers for its tasks.

The provided verifier found at `src/contracts/Verifier.json` is based on the trusted setup, `hermez-raw-11`. If you are working with a higher number of entries, you will need to generate a new verifier contract by using the `generate_solvency_verifier` method.
## Examples

Here's a brief example of how you might invoke this method:
### Running the Inclusion Verification

```Rust
Snapshot::generate_solvency_verifier("SolvencyVerifier.yul", "SolvencyVerifier.sol");
```
This example demonstrates how a user can verify the inclusion of their account in the Merkle Sum Tree.
In this example, the CEX provides the user with their `balances` and `username`, but not the `leaf_hash`.

This method creates two files, `SolvencyVerifier.yul` and `SolvencyVerifier.sol`, which will be used in `Summa.sol`.
The user will generate the `leaf_hash` themselves and then verify its inclusion in the tree.

## Usage
Make sure you have the required files:
- `backend/ptau/hermez-raw-11`
- `backend/src/apis/csv/assets.csv`
- `zk_prover/src/merkle_sum_tree/csv/entry_16.csv`

To build the binary executable and test it

To run the example:
```
cargo build
SIGNATURE_VERIFICATION_MESSAGE="Summa proof of solvency for CryptoExchange" cargo test --release -- --nocapture
cargo run --example verify_inclusion
```

The [buildscript](./build.rs) will automatically build the contract Rust interfaces from the [JSON ABIs](./src/contracts/abi/) and place them into [./src/contracts/generated](./src/contracts/generated) directory. The ABIs are updated on contract deployment from the [contracts subproject](./../contracts/README.md) by the [contract deployment script](./../contracts/scripts/deploy.ts).
On successful execution, you'll observe a message indicating the verification outcome:
```
Verifying the proof result for User #0: true
```
1 change: 0 additions & 1 deletion backend/examples/signatures.csv

This file was deleted.

14 changes: 5 additions & 9 deletions backend/examples/verify_inclusion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
use halo2_proofs::halo2curves::{bn256::Fr as Fp, ff::PrimeField};
use num_bigint::BigUint;

use summa_backend::apis::snapshot::Snapshot;
use summa_backend::apis::round::Snapshot;
use summa_solvency::{
circuits::{
merkle_sum_tree::MstInclusionCircuit,
Expand Down Expand Up @@ -32,18 +32,14 @@ fn main() {
const N_BYTES: usize = 8;

let ptau_path = "./ptau/hermez-raw-11";
let signature_csv_path = "./examples/signatures.csv";
let asset_csv_path = "src/apis/csv/assets.csv";
let entry_csv_path = "../zk_prover/src/merkle_sum_tree/csv/entry_16.csv";

// CEX Generate the Merkle Sum Tree and then initialize the circuit.
// Note that `signature_csv` is empty because this is only needed to generate π of Solvency, which is not the case here.
let snapshot = Snapshot::<LEVELS, N_ASSETS, N_BYTES>::new(
&entry_csv_path,
&signature_csv_path,
"Summa proof of solvency for CryptoExchange".to_string(),
&ptau_path,
)
.unwrap();
let snapshot =
Snapshot::<LEVELS, N_ASSETS, N_BYTES>::new(&asset_csv_path, &entry_csv_path, &ptau_path)
.unwrap();

let inclusion_proof = snapshot.generate_proof_of_inclusion(0 as usize).unwrap();

Expand Down
38 changes: 38 additions & 0 deletions backend/src/apis/address_ownership.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use crate::contracts::{generated::summa_contract::AddressOwnershipProof, signer::SummaSigner};
use ethers::types::Address;
use std::{error::Error, result::Result};

use super::csv_parser::parse_signature_csv;

pub struct AddressOwnership {
address_ownership_proofs: Vec<AddressOwnershipProof>,
signer: SummaSigner,
}

impl AddressOwnership {
pub fn new(
signer_key: &str,
chain_id: u64,
rpc_url: &str,
summa_sc_address: Address,
signature_csv_path: &str,
) -> Result<AddressOwnership, Box<dyn Error>> {
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),
})
}

// This function dispatches the proof of address ownership. Before calling this function,
// ensure externally that the provided `addresses` in `address_ownership_proof` are not already registered
// on the Summa contract.
pub async fn dispatch_proof_of_address_ownership(&mut self) -> Result<(), Box<dyn Error>> {
self.signer
.submit_proof_of_address_ownership(self.address_ownership_proofs.clone())
.await?;

Ok(())
}
}
3 changes: 3 additions & 0 deletions backend/src/apis/csv/assets.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
chain;asset_name;amount
ETH;ETH;556863
ETH;USDT;556863
7 changes: 3 additions & 4 deletions backend/src/apis/csv/signatures.csv
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
address;signature
0x70997970C51812dc3A010C7d01b50e0d17dc79C8;0xb17a9e25265d3b88de7bfad81e7accad6e3d5612308ff83cc0fef76a34152b0444309e8fc3dea5139e49b6fc83a8553071a7af3d0cfd3fb8c1aea2a4c171729c1c
0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC;0x089b32327d332c295dc3b8873c205b72153211de6dc1c51235782b091cefb9d06d6df2661b86a7d441cd322f125b84901486b150e684221a7b7636eb8182af551b
0x90F79bf6EB2c4f870365E785982E1f101E93b906;0xeb648c7409f45ba9064707d22bdae23dff15517aaf0942b8507b60b9a924bbeb4c8f2ceafc26ede9fd9eb3232cc138500ded3e3c7b8555fa43b995bd15c234ff1c
chain;address;signature;message
ETH;0x70997970C51812dc3A010C7d01b50e0d17dc79C8;0x089b32327d332c295dc3b8873c205b72153211de6dc1c51235782b091cefb9d06d6df2661b86a7d441cd322f125b84901486b150e684221a7b7636eb8182af551b;Summa proof of solvency for CryptoExchange
ETH;0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC;0xb17a9e25265d3b88de7bfad81e7accad6e3d5612308ff83cc0fef76a34152b0444309e8fc3dea5139e49b6fc83a8553071a7af3d0cfd3fb8c1aea2a4c171729c1c;Summa proof of solvency for CryptoExchange
103 changes: 86 additions & 17 deletions backend/src/apis/csv_parser.rs
Original file line number Diff line number Diff line change
@@ -1,49 +1,118 @@
use std::error::Error;
use std::fs::File;
use std::path::Path;
use std::{error::Error, fs::File, path::Path};

use ethers::{
abi::AbiEncode,
types::{Bytes, U256},
};
use serde::Deserialize;

use crate::contracts::generated::summa_contract::{AddressOwnershipProof, Asset};

#[derive(Debug, Deserialize)]
struct Record {
struct SignatureRecord {
chain: String,
address: String,
signature: String,
message: String,
}

pub fn parse_signature_csv<P: AsRef<Path>>(
path: P,
) -> Result<(Vec<String>, Vec<String>), Box<dyn Error>> {
) -> Result<Vec<AddressOwnershipProof>, Box<dyn Error>> {
let file = File::open(path)?;
let mut rdr = csv::ReaderBuilder::new().delimiter(b';').from_reader(file);

let mut address_ownership_proofs = Vec::<AddressOwnershipProof>::new();

for result in rdr.deserialize() {
let record: SignatureRecord = result?;

address_ownership_proofs.push(AddressOwnershipProof {
cex_address: record.address.to_string(),
chain: record.chain.to_string(),
signature: record.signature.parse()?,
message: Bytes::from(record.message.encode()),
});
}

Ok(address_ownership_proofs)
}

#[derive(Debug, Deserialize)]
struct AssetRecord {
chain: String,
asset_name: String,
amount: String,
}

pub fn parse_asset_csv<P: AsRef<Path>, const N_ASSETS: usize>(
path: P,
) -> Result<[Asset; N_ASSETS], Box<dyn Error>> {
let file = File::open(path)?;
let mut rdr = csv::ReaderBuilder::new().delimiter(b';').from_reader(file);

let mut signatures = Vec::<String>::new();
let mut addresses = Vec::<String>::new();
let mut assets_vec = Vec::with_capacity(N_ASSETS);

for result in rdr.deserialize() {
let record: Record = result?;
let record: AssetRecord = result?;

signatures.push(record.signature);
addresses.push(record.address);
assets_vec.push(Asset {
asset_name: record.asset_name,
chain: record.chain,
amount: U256::from_dec_str(&record.amount)?,
});
}

Ok((addresses, signatures))
let assets_array: [Asset; N_ASSETS] = assets_vec.try_into().map_err(|v: Vec<Asset>| {
format!(
"The number of assets in CSV file does not match the expected count {:?}",
v
)
})?;

Ok(assets_array)
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_parse_csv_to_assets() {
// these signatures are from contracts/test/Summa.ts
fn test_parse_csv_to_signature() {
let path = "src/apis/csv/signatures.csv";
let (addresses, signatures) = parse_signature_csv(path).unwrap();
let address_ownership = parse_signature_csv(path).unwrap();

let first_address_ownership = AddressOwnershipProof {
chain: "ETH".to_string(),
cex_address: "0x70997970C51812dc3A010C7d01b50e0d17dc79C8".to_string(),
signature:
("0x089b32327d332c295dc3b8873c205b72153211de6dc1c51235782b091cefb9d06d6df2661b86a7d441cd322f125b84901486b150e684221a7b7636eb8182af551b").parse().unwrap(),
message: "Summa proof of solvency for CryptoExchange".encode().into(),
};

assert_eq!(addresses[0], "0x70997970C51812dc3A010C7d01b50e0d17dc79C8");
assert_eq!(address_ownership[0], first_address_ownership);
}

#[test]
fn test_parse_csv_to_assets() {
let path = "src/apis/csv/assets.csv";
let assets = parse_asset_csv::<&str, 2>(path).unwrap();

assert_eq!(
signatures[0],
"0xb17a9e25265d3b88de7bfad81e7accad6e3d5612308ff83cc0fef76a34152b0444309e8fc3dea5139e49b6fc83a8553071a7af3d0cfd3fb8c1aea2a4c171729c1c"
assets[0],
Asset {
chain: "ETH".to_string(),
asset_name: "ETH".to_string(),
amount: U256::from(556863),
}
);
assert_eq!(
assets[1],
Asset {
chain: "ETH".to_string(),
asset_name: "USDT".to_string(),
amount: U256::from(556863),
}
);
}
}
Loading

0 comments on commit bbf4363

Please sign in to comment.