Skip to content

Commit

Permalink
feat: modify local signer from remote one; removed private keys from …
Browse files Browse the repository at this point in the history
…'contracts/signer'
  • Loading branch information
sifnoc committed Sep 15, 2023
1 parent bcea57b commit a36f076
Show file tree
Hide file tree
Showing 11 changed files with 93 additions and 231 deletions.
16 changes: 16 additions & 0 deletions backend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,19 @@ On successful execution, you'll observe a message indicating the verification ou
```
Verifying the proof result for User #0: true
```

### Generating Message Signatures

This example demonstrates how to generate a CSV file containing signatures derived from a given message, essential for `AddressOwnership`.

This demonstration is adaptable for various scenarios. For instance, you could compile this example into an executable binary, an operator can run it using a hardware wallet.
This operation should ideally occur in a highly secure environment within a CEX, where the system is isolated from any online networks, ensuring maximum security against potential external threats.

The resulting signatures are saved in a CSV file using a custom delimiter, allowing for straightforward parsing and verification. You can find the output file at `src/apis/csv/signatures.csv`.

To run the example:
```
cargo run --example generate_signatures
```

Note: This example uses hardcoded private keys for simplicity. In real-world applications, it's imperative never to expose private keys in such a manner. Instead, consider crate your own `message_signer` to utilize secure mechanisms, such as hardware wallets or secure key vaults.
67 changes: 4 additions & 63 deletions backend/examples/generate_signatures/main.rs
Original file line number Diff line number Diff line change
@@ -1,77 +1,18 @@
use std::{
error::Error,
fs::File,
io::{Read, Write},
net::{TcpListener, TcpStream},
str::FromStr,
thread::{sleep, spawn},
};
use std::{error::Error, fs::File};

use csv::WriterBuilder;
use ethers::{
abi::{encode, Token},
prelude::SignerMiddleware,
signers::{LocalWallet, Signer, WalletError},
types::Signature,
utils::{keccak256, to_checksum},
};
use serde_json::{from_str, to_string_pretty};

mod remote_signer;
use remote_signer::start_server;
use summa_backend::apis::csv_parser::SignatureRecord;

// We provide simple request function for getting signatures from the signer server
fn send_request(message: &str) -> Result<String, std::io::Error> {
let mut stream = TcpStream::connect("127.0.0.1:8080")?;

let request = format!(
"POST /sign HTTP/1.1\r\nContent-Length: {}\r\n\r\n{}",
message.len(),
message
);

stream.write_all(request.as_bytes())?;

let mut response = String::new();
stream.read_to_string(&mut response)?;

Ok(response)
}

fn parse_response(response: &str) -> Result<Vec<SignatureRecord>, Box<dyn std::error::Error>> {
// Split the response into HTTP headers and body
let parts: Vec<&str> = response.split("\r\n\r\n").collect();

if parts.len() != 2 {
return Err("Invalid response format".into());
}

let json_str = parts[1];

// Parse the JSON response into a vector of SignatureRecord
let signatures: Vec<SignatureRecord> = serde_json::from_str(json_str)?;

Ok(signatures)
}
mod message_signer;
use message_signer::sign_message;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
// Let's assume the CEX has multiple private keys for generating message signatures for AddressOwnershipProof
// Start the server in a separate thread
spawn(|| start_server());

// Give the server a little time to start
sleep(std::time::Duration::from_secs(1));

// Given message to sign
// Note that, the message length are fixed for the server.
let message = "Summa proof of solvency for CryptoExchange";
let path = "src/apis/csv/signatures.csv";

// Request signatures with `message` to the signer server
let response = send_request(message)?;
let signatures = parse_response(&response)?;
let signatures = sign_message(message).await?;

// Write the signatures to a CSV file
let file = File::create(path)?;
Expand Down
48 changes: 48 additions & 0 deletions backend/examples/generate_signatures/message_signer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
use std::{error::Error, str::FromStr};

use ethers::{
abi::{encode, Token},
signers::{LocalWallet, Signer},
utils::{keccak256, to_checksum},
};

use summa_backend::apis::csv_parser::SignatureRecord;

pub async fn sign_message(message: &str) -> Result<Vec<SignatureRecord>, Box<dyn Error>> {
// Using private keys directly is insecure.
// Instead, consider leveraging hardware wallet support.
// `ethers-rs` provides support for both Ledger and Trezor hardware wallets.
//
// For example, you could use the Ledger wallet as shown below:
// let signing_wallets = (0..2).map(|index| Ledger::new(HDPath::LedgerLive(index), 1).await.unwrap()).collect();
//
// Refers to: https://docs.rs/ethers/latest/ethers/signers/index.html
let private_keys = &[
"0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d",
"0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a",
];

let signing_wallets: Vec<LocalWallet> = private_keys
.iter()
.map(|private_key| LocalWallet::from_str(private_key).unwrap())
.collect();

let encoded_message = encode(&[Token::String(message.to_owned())]);
let hashed_message = keccak256(encoded_message);

let mut signatures: Vec<SignatureRecord> = Vec::new();

// Iterating signing wallets and generate signature to put `signatures` vector
for wallet in signing_wallets {
let signature = wallet.sign_message(hashed_message).await.unwrap();
let record = SignatureRecord::new(
"ETH".to_string(),
to_checksum(&wallet.address(), None), //
format!("0x{}", signature.to_string()),
message.to_string(),
);
signatures.push(record);
}

Ok(signatures)
}
98 changes: 0 additions & 98 deletions backend/examples/generate_signatures/remote_signer.rs

This file was deleted.

2 changes: 1 addition & 1 deletion backend/src/apis/address_ownership.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ impl AddressOwnership {

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_sc_address),
})
}

Expand Down
6 changes: 3 additions & 3 deletions backend/src/apis/csv/signatures.csv
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
chain;address;signature;message
ETH;0x70997970C51812dc3A010C7d01b50e0d17dc79C8;0x089b32327d332c295dc3b8873c205b72153211de6dc1c51235782b091cefb9d06d6df2661b86a7d441cd322f125b84901486b150e684221a7b7636eb8182af551b;Summa proof of solvency for CryptoExchange
ETH;0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC;0xb17a9e25265d3b88de7bfad81e7accad6e3d5612308ff83cc0fef76a34152b0444309e8fc3dea5139e49b6fc83a8553071a7af3d0cfd3fb8c1aea2a4c171729c1c;Summa proof of solvency for CryptoExchange
chain;address;signature;message
ETH;0x70997970C51812dc3A010C7d01b50e0d17dc79C8;0x089b32327d332c295dc3b8873c205b72153211de6dc1c51235782b091cefb9d06d6df2661b86a7d441cd322f125b84901486b150e684221a7b7636eb8182af551b;Summa proof of solvency for CryptoExchange
ETH;0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC;0xb17a9e25265d3b88de7bfad81e7accad6e3d5612308ff83cc0fef76a34152b0444309e8fc3dea5139e49b6fc83a8553071a7af3d0cfd3fb8c1aea2a4c171729c1c;Summa proof of solvency for CryptoExchange
17 changes: 14 additions & 3 deletions backend/src/apis/csv_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,29 @@ use ethers::{
abi::AbiEncode,
types::{Bytes, U256},
};
use serde::Deserialize;
use serde::{Deserialize, Serialize};

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

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

impl SignatureRecord {
pub fn new(chain: String, address: String, signature: String, message: String) -> Self {
Self {
chain,
address,
signature,
message,
}
}
}

pub fn parse_signature_csv<P: AsRef<Path>>(
path: P,
) -> Result<Vec<AddressOwnershipProof>, Box<dyn Error>> {
Expand Down
2 changes: 1 addition & 1 deletion backend/src/apis/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
pub mod address_ownership;
mod csv_parser;
pub mod csv_parser;
pub mod round;
2 changes: 1 addition & 1 deletion backend/src/apis/round.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ where
params_path,
)
.unwrap(),
signer: SummaSigner::new(&vec![], signer_key, chain_id, rpc_url, summa_sc_address),
signer: SummaSigner::new(signer_key, chain_id, rpc_url, summa_sc_address),
})
}

Expand Down
4 changes: 2 additions & 2 deletions backend/src/contracts/generated/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
pub mod inclusion_verifier;
pub mod solvency_verifier;
pub mod summa_contract;
pub mod solvency_verifier;
pub mod inclusion_verifier;
Loading

0 comments on commit a36f076

Please sign in to comment.