Skip to content

Commit

Permalink
Separated signer from core components. (#174)
Browse files Browse the repository at this point in the history
* feat: added mutex lock to signer; used it as ref

* fix: mutex deadlock in signer

* chore: minor updates

* refactor: signer spawn provider internally; updated comments
  • Loading branch information
sifnoc authored Nov 8, 2023
1 parent 400fc49 commit 7489c53
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 97 deletions.
28 changes: 11 additions & 17 deletions backend/examples/summa_solvency_flow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use summa_backend::{
address_ownership::AddressOwnership,
round::{MstInclusionProof, Round},
},
contracts::signer::AddressInput,
contracts::signer::{AddressInput, SummaSigner},
tests::initialize_test_env,
};
use summa_solvency::merkle_sum_tree::utils::generate_leaf_hash;
Expand All @@ -20,24 +20,26 @@ const USER_INDEX: usize = 0;
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
// Initialize test environment without `address_ownership` instance from `initialize_test_env` function.
let (anvil, _, _, _, summa_contract) = initialize_test_env().await;
let (anvil, _, _, _, summa_contract) = initialize_test_env(None).await;

// 1. Submit ownership proof
//
// Each CEX prepares its own `signature` CSV file.
let signature_csv_path = "src/apis/csv/signatures.csv";

// The signer instance would be shared with `address_ownership` and `round` instances
//
// 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(
let signer = SummaSigner::new(
"0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
anvil.chain_id(),
anvil.endpoint().as_str(),
AddressInput::Address(summa_contract.address()),
signature_csv_path,
)
.unwrap();
.await?;

let mut address_ownership_client = AddressOwnership::new(&signer, signature_csv_path).unwrap();

// Dispatch the proof of address ownership.
// the `dispatch_proof_of_address_ownership` function sends a transaction to the Summa contract.
Expand All @@ -55,17 +57,9 @@ async fn main() -> Result<(), Box<dyn Error>> {
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(),
AddressInput::Address(summa_contract.address()),
entry_csv,
asset_csv,
params_path,
1,
)
.unwrap();
let timestamp = 1u64;
let mut round =
Round::<4, 2, 14>::new(&signer, entry_csv, asset_csv, params_path, timestamp).unwrap();

// Sends the solvency proof, which should ideally complete without errors.
round.dispatch_solvency_proof().await?;
Expand Down
22 changes: 8 additions & 14 deletions backend/src/apis/address_ownership.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,23 @@
use crate::contracts::{
generated::summa_contract::AddressOwnershipProof,
signer::{AddressInput, SummaSigner},
};
use crate::contracts::{generated::summa_contract::AddressOwnershipProof, signer::SummaSigner};
use std::{error::Error, result::Result};

use super::csv_parser::parse_signature_csv;

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

impl AddressOwnership {
pub fn new(
signer_key: &str,
chain_id: u64,
rpc_url: &str,
summa_address_input: AddressInput,
impl AddressOwnership<'_> {
pub fn new<'a>(
signer: &'a SummaSigner,
signature_csv_path: &str,
) -> Result<AddressOwnership, Box<dyn Error>> {
) -> Result<AddressOwnership<'a>, 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_address_input),
signer,
})
}

Expand Down
22 changes: 8 additions & 14 deletions backend/src/apis/round.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@ use serde::{Deserialize, Serialize};
use std::error::Error;

use super::csv_parser::parse_asset_csv;
use crate::contracts::{
generated::summa_contract::summa::Asset,
signer::{AddressInput, SummaSigner},
};
use crate::contracts::{generated::summa_contract::summa::Asset, signer::SummaSigner};
use summa_solvency::{
circuits::{
merkle_sum_tree::MstInclusionCircuit,
Expand Down Expand Up @@ -65,28 +62,25 @@ pub struct Snapshot<const LEVELS: usize, const N_ASSETS: usize, const N_BYTES: u
trusted_setup: [SetupArtifacts; 2],
}

pub struct Round<const LEVELS: usize, const N_ASSETS: usize, const N_BYTES: usize> {
pub struct Round<'a, const LEVELS: usize, const N_ASSETS: usize, const N_BYTES: usize> {
timestamp: u64,
snapshot: Snapshot<LEVELS, N_ASSETS, N_BYTES>,
signer: SummaSigner,
signer: &'a SummaSigner,
}

impl<const LEVELS: usize, const N_ASSETS: usize, const N_BYTES: usize>
Round<LEVELS, N_ASSETS, N_BYTES>
Round<'_, LEVELS, N_ASSETS, N_BYTES>
where
[usize; N_ASSETS + 1]: Sized,
[usize; 2 * (1 + N_ASSETS)]: Sized,
{
pub fn new(
signer_key: &str,
chain_id: u64,
rpc_url: &str,
summa_address_input: AddressInput,
pub fn new<'a>(
signer: &'a SummaSigner,
entry_csv_path: &str,
asset_csv_path: &str,
params_path: &str,
timestamp: u64,
) -> Result<Round<LEVELS, N_ASSETS, N_BYTES>, Box<dyn Error>> {
) -> Result<Round<'a, LEVELS, N_ASSETS, N_BYTES>, Box<dyn Error>> {
Ok(Round {
timestamp,
snapshot: Snapshot::<LEVELS, N_ASSETS, N_BYTES>::new(
Expand All @@ -95,7 +89,7 @@ where
params_path,
)
.unwrap(),
signer: SummaSigner::new(signer_key, chain_id, rpc_url, summa_address_input),
signer: &signer,
})
}

Expand Down
46 changes: 27 additions & 19 deletions backend/src/contracts/signer.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
use ethers::{
prelude::SignerMiddleware,
providers::{Http, Provider},
providers::{Http, Middleware, Provider},
signers::{LocalWallet, Signer},
types::Address,
};
use serde_json::Value;
use std::{
error::Error, fs::File, io::BufReader, path::Path, str::FromStr, sync::Arc, time::Duration,
};
use std::{error::Error, fs::File, io::BufReader, path::Path, str::FromStr, sync::Arc};
use tokio::sync::Mutex;

use super::generated::summa_contract::{AddressOwnershipProof, Asset};
use crate::contracts::generated::summa_contract::Summa;
Expand All @@ -19,27 +18,25 @@ pub enum AddressInput {

#[derive(Debug)]
pub struct SummaSigner {
summa_contract: Summa<SignerMiddleware<Provider<Http>, LocalWallet>>,
nonce_lock: Mutex<()>, // To prevent running `submit` methods concurrently
summa_contract: Summa<SignerMiddleware<Arc<Provider<Http>>, LocalWallet>>,
}

impl SummaSigner {
/// Creates a new SummaSigner instance
/// # Arguments
/// * `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_input` - Either the contract's direct address or a path to its config file.
pub fn new(
/// * `url` - The endpoint for connecting to the node
/// * `address` - The address of the Summa contract
pub async fn new(
signer_key: &str,
chain_id: u64,
rpc_url: &str,
url: &str,
address_input: AddressInput,
) -> Self {
) -> Result<Self, Box<dyn Error>> {
let wallet: LocalWallet = LocalWallet::from_str(signer_key).unwrap();

let provider = Provider::<Http>::try_from(rpc_url)
.unwrap()
.interval(Duration::from_millis(10u64));
let provider = Arc::new(Provider::try_from(url)?);
let chain_id = provider.get_chainid().await?.as_u64();
let client = Arc::new(SignerMiddleware::new(
provider,
wallet.with_chain_id(chain_id),
Expand All @@ -53,10 +50,10 @@ impl SummaSigner {
}
};

let contract = Summa::new(address, client);
Self {
summa_contract: contract,
}
Ok(Self {
nonce_lock: Mutex::new(()),
summa_contract: Summa::new(address, client),
})
}

pub fn get_summa_address(&self) -> Address {
Expand Down Expand Up @@ -91,14 +88,19 @@ impl SummaSigner {
&self,
address_ownership_proofs: Vec<AddressOwnershipProof>,
) -> Result<(), Box<dyn std::error::Error>> {
let lock_guard = self.nonce_lock.lock().await;

let submit_proof_of_address_ownership = &self
.summa_contract
.submit_proof_of_address_ownership(address_ownership_proofs);

// To prevent nonce collision, we lock the nonce before sending the transaction
let tx = submit_proof_of_address_ownership.send().await?;

// Wait for the pending transaction to be mined
tx.await?;

drop(lock_guard);
Ok(())
}

Expand All @@ -109,14 +111,20 @@ impl SummaSigner {
proof: ethers::types::Bytes,
timestamp: ethers::types::U256,
) -> Result<(), Box<dyn std::error::Error>> {
let lock_guard = self.nonce_lock.lock().await;

let submit_proof_of_solvency_call = &self
.summa_contract
.submit_proof_of_solvency(mst_root, assets, proof, timestamp);

// To prevent nonce collision, we lock the nonce before sending the transaction
let tx = submit_proof_of_solvency_call.send().await?;

// Wait for the pending transaction to be mined
tx.await?;

drop(lock_guard);

Ok(())
}
}
Loading

0 comments on commit 7489c53

Please sign in to comment.