Skip to content

Commit

Permalink
Crypto protospec (#89)
Browse files Browse the repository at this point in the history
* setup clap CLI

* keygen async setup from Clap

* keygen async

* typechecks pass

* yoink, structopt

* reorg subdirs

* sm-manager

* keygen + signing - compilation errors

* pending keygen ownership bug

* async ownership bugfix

* blocking: handling error on 6 of 7 keygen

* keygen bug documented
  • Loading branch information
thor314 authored Feb 13, 2022
1 parent dda911a commit 8ce54fd
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 91 deletions.
33 changes: 21 additions & 12 deletions crypto/protocol/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,33 @@ See [HackMD spec](https://hackmd.io/kLiqrFYETOiONBYXIdqdMA?view) for details.
## Keygen
```sh
# Terminal 1, from project root
cargo build --release
cargo build --release -p protocol
cd target/release
# starts an HTTP server on http://127.0.0.1:8000. This server relays all communication between nodes.
./protocol sm-manager
# Terminal 2, from target/release; Alice generates keys
export M=6 # M of N
export N=6
./protocol keygen -t $M -n $N
rm local-share* # if there are already local-share files in the directory
# TODO BLOCKING: the following generates a pre-validation error
./protocol keygen # defaults to 6 of 7, the last of which is an ignorable extra
```

## Sign, assume 6 of 6
```sh
# Terminal 1..6, from target/release
let TX="immaculate"
./protocol -p 1,2,3,4,5,6 -d $TX -l local-share0.json
./protocol -p 1,2,3,4,5,6 -d $TX -l local-share1.json
./protocol -p 1,2,3,4,5,6 -d $TX -l local-share2.json
./protocol -p 1,2,3,4,5,6 -d $TX -l local-share3.json
./protocol -p 1,2,3,4,5,6 -d $TX -l local-share4.json
./protocol -p 1,2,3,4,5,6 -d $TX -l local-share5.json
```
./protocol sign -p 1,2,3,4,5,6 -i 1
./protocol sign -p 1,2,3,4,5,6 -i 2
./protocol sign -p 1,2,3,4,5,6 -i 3
./protocol sign -p 1,2,3,4,5,6 -i 4
./protocol sign -p 1,2,3,4,5,6 -i 5
./protocol sign -p 1,2,3,4,5,6 -i 6
```


## Note dump
### tk: On nightly, `cannot find macro asm in this scope`
Note that switching to stable is undesireable for other reasons. Need an older version of the wasmtime release. https://github.com/phil-opp/blog_os/issues/1066
quickfix:
`rustup override set nightly-2021-12-13`
Now your wasm toolchain is broken. Sorry.
`rustup target add wasm32-unknown-unknown --toolchain nightly-2021-12-13`
Eventually this will be unnecessary when the upstreamers get their lives together.
9 changes: 5 additions & 4 deletions crypto/protocol/src/keygen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@ pub struct KeygenCli {
address: surf::Url,
#[structopt(short, long, default_value = "default-keygen")]
room: String,
#[structopt(short, long)]
#[structopt(short, long, default_value = "6")]
pub threshold: u16,
#[structopt(short, long)]
/// HACK: number_of_parties needs to be greater than threshold, for
/// unclear reasons. It's temporarily acceptible for Alice to create one burner key share.
#[structopt(short, long, default_value = "7")]
number_of_parties: u16,
}


/// In the example https://github.com/ZenGo-X/multi-party-ecdsa/blob/master/examples/gg20_keygen.rs,
/// the key generator:
/// 1. asyncronously opens an output file
Expand All @@ -40,7 +41,7 @@ pub async fn keygen_cli(args: &KeygenCli, index: u16) -> Result<()> {
.create_new(true)
.open(output)
.await
.context("cannot create output file")?;
.context(format!("cannot create output file: {}", index))?;

let (_i, incoming, outgoing) = join_computation(args.address.clone(), &args.room)
.await
Expand Down
13 changes: 7 additions & 6 deletions crypto/protocol/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,13 @@ async fn main() -> Result<()> {
Command::SmClient(cli) => gg20_sm_client::sm_client_cli(cli).await,
Command::SmManager => gg20_sm_manager::sm_manager_cli().await,
Command::Keygen(cli) => {
let ids = 0..cli.threshold;
let _: Vec<_> =
futures::future::try_join_all(ids.map(|id| keygen::keygen_cli(&cli, id)))
.await
.unwrap();
Ok(())
// library requires indices start at 1
// TODO: tk alice can't send messages to herself in round_based dep
let ids = 1..=(cli.threshold + 1);
futures::future::try_join_all(ids.map(|id| keygen::keygen_cli(&cli, id)))
.await
.unwrap();
Ok(())
},
Command::Sign(cli) => sign::sign(cli).await,
Command::DeleteAccount => todo!(),
Expand Down
119 changes: 55 additions & 64 deletions crypto/protocol/src/sign.rs
Original file line number Diff line number Diff line change
@@ -1,85 +1,76 @@
use std::path::PathBuf;
use anyhow::{anyhow, Context, Result};
use curv::{arithmetic::Converter, BigInt};
use futures::{SinkExt, StreamExt, TryStreamExt};
use structopt::StructOpt;
use curv::arithmetic::Converter;
use curv::BigInt;
use multi_party_ecdsa::protocols::multi_party_ecdsa::gg_2020::state_machine::sign::{
OfflineStage, SignManual,
OfflineStage, SignManual,
};
use round_based::async_runtime::AsyncProtocol;
use round_based::Msg;
use round_based::{async_runtime::AsyncProtocol, Msg};
use std::path::PathBuf;
use structopt::StructOpt;

use crate::gg20_sm_client::join_computation;

#[derive(Debug, StructOpt,Clone)]
#[derive(Debug, StructOpt, Clone)]
pub struct SignCli {
#[structopt(short, long, default_value = "http://localhost:8000/")]
address: surf::Url,
#[structopt(short, long, default_value = "default-signing")]
room: String,
#[structopt(short, long, default_value = "http://localhost:8000/")]
address: surf::Url,
#[structopt(short, long, default_value = "default-signing")]
room: String,
/// Index of the party
#[structopt(short, long, default_value = "0")]
index: u16,
#[structopt(short, long, use_delimiter(true))]
parties: Vec<u16>,
#[structopt(short, long)]
data_to_sign: String,
#[structopt(short, long, default_value = "1")]
index: u16,
#[structopt(short, long, use_delimiter(true))]
parties: Vec<u16>,
#[structopt(short, long, default_value = "vibes be immaculate")]
data_to_sign: String,
}

pub async fn sign(args: SignCli ) -> Result<()> {
pub async fn sign(args: SignCli) -> Result<()> {
let local_share = PathBuf::from(format!("local-share{}.json", args.index));
let local_share = tokio::fs::read(local_share)
.await
.context(format!("cannot read local share at index {}",args.index))?;
let local_share = serde_json::from_slice(&local_share).context("parse local share")?;
let number_of_parties = args.parties.len();
let local_share = tokio::fs::read(local_share)
.await
.context(format!("cannot read local share at index {}", args.index))?;
let local_share = serde_json::from_slice(&local_share).context("parse local share")?;
let number_of_parties = args.parties.len();

let (i, incoming, outgoing) =
join_computation(args.address.clone(), &format!("{}-offline", args.room))
.await
.context("join offline computation")?;
let (i, incoming, outgoing) =
join_computation(args.address.clone(), &format!("{}-offline", args.room))
.await
.context("join offline computation")?;

let incoming = incoming.fuse();
tokio::pin!(incoming);
tokio::pin!(outgoing);
let incoming = incoming.fuse();
tokio::pin!(incoming);
tokio::pin!(outgoing);

let signing = OfflineStage::new(i, args.parties, local_share)?;
let completed_offline_stage = AsyncProtocol::new(signing, incoming, outgoing)
.run()
.await
.map_err(|e| anyhow!("protocol execution terminated with error: {}", e))?;
let signing = OfflineStage::new(i, args.parties, local_share)?;
let completed_offline_stage = AsyncProtocol::new(signing, incoming, outgoing)
.run()
.await
// TODO: tk alice can't send messages to herself in round_based dep
.map_err(|e| anyhow!("protocol execution terminated with error: {}", e))?;

let (_i, incoming, outgoing) = join_computation(args.address, &format!("{}-online", args.room))
.await
.context("join online computation")?;
let (_i, incoming, outgoing) = join_computation(args.address, &format!("{}-online", args.room))
.await
.context("join online computation")?;

tokio::pin!(incoming);
tokio::pin!(outgoing);
tokio::pin!(incoming);
tokio::pin!(outgoing);

let (signing, partial_signature) = SignManual::new(
BigInt::from_bytes(args.data_to_sign.as_bytes()),
completed_offline_stage,
)?;
let (signing, partial_signature) =
SignManual::new(BigInt::from_bytes(args.data_to_sign.as_bytes()), completed_offline_stage)?;

outgoing
.send(Msg {
sender: i,
receiver: None,
body: partial_signature,
})
.await?;
outgoing
.send(Msg { sender: i, receiver: None, body: partial_signature })
.await?;

let partial_signatures: Vec<_> = incoming
.take(number_of_parties - 1)
.map_ok(|msg| msg.body)
.try_collect()
.await?;
let signature = signing
.complete(&partial_signatures)
.context("online stage failed")?;
let signature = serde_json::to_string(&signature).context("serialize signature")?;
println!("{}", signature);
let partial_signatures: Vec<_> = incoming
.take(number_of_parties - 1)
.map_ok(|msg| msg.body)
.try_collect()
.await?;
let signature = signing.complete(&partial_signatures).context("online stage failed")?;
let signature = serde_json::to_string(&signature).context("serialize signature")?;
println!("{}", signature);

Ok(())
}
Ok(())
}
10 changes: 5 additions & 5 deletions crypto/protocol/src/user.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@

#![allow(unused_imports)]
use std::panic::take_hook;

use async_trait::async_trait;
use sp_keyring::AccountKeyring;
use subxt::{ClientBuilder, DefaultConfig, DefaultExtra, PairSigner};
Expand All @@ -18,7 +17,8 @@ pub struct User {
impl User {
/// User sends an extrinsic requesting the endpoints of the signer nodes to generate a signature
/// User expects a reply
/// This reply contains the endpoint of the current signer-node or an error message. Or read the endpoints on-chain??
/// This reply contains the endpoint of the current signer-node or an error message. Or read the
/// endpoints on-chain??
// Todo: how can the signer node endpoints passed to the user in the reply?
// Todo: handle the result message and forward the Signer's endpoint
pub async fn request_sig_gen(&self) -> Result<() , Box<dyn std::error::Error>> {
Expand Down Expand Up @@ -69,8 +69,8 @@ impl User {
}

/// User sends an extrinsic requesting account creation
pub async fn send_registration(&self) -> Result<(), Box<dyn std::error::Error>> {

#[allow(dead_code)]
async fn send_registration(&self) -> Result<(), Box<dyn std::error::Error>> {
println!("register is called");
let signer = PairSigner::new(AccountKeyring::Alice.pair());

Expand Down

0 comments on commit 8ce54fd

Please sign in to comment.