From 8ce54fd127ea5a64a0ab36cc0c1d5f08fb54b501 Mon Sep 17 00:00:00 2001 From: Thor <7041313+thor314@users.noreply.github.com> Date: Sun, 13 Feb 2022 10:58:48 -0800 Subject: [PATCH] Crypto protospec (#89) * 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 --- crypto/protocol/README.md | 33 ++++++---- crypto/protocol/src/keygen.rs | 9 +-- crypto/protocol/src/main.rs | 13 ++-- crypto/protocol/src/sign.rs | 119 ++++++++++++++++------------------ crypto/protocol/src/user.rs | 10 +-- 5 files changed, 93 insertions(+), 91 deletions(-) diff --git a/crypto/protocol/README.md b/crypto/protocol/README.md index baf137d13..cc286d859 100644 --- a/crypto/protocol/README.md +++ b/crypto/protocol/README.md @@ -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 -``` \ No newline at end of file +./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. \ No newline at end of file diff --git a/crypto/protocol/src/keygen.rs b/crypto/protocol/src/keygen.rs index 7c0f02ca4..a744dce88 100644 --- a/crypto/protocol/src/keygen.rs +++ b/crypto/protocol/src/keygen.rs @@ -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 @@ -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 diff --git a/crypto/protocol/src/main.rs b/crypto/protocol/src/main.rs index 69d2d03d6..1d74e0cec 100644 --- a/crypto/protocol/src/main.rs +++ b/crypto/protocol/src/main.rs @@ -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!(), diff --git a/crypto/protocol/src/sign.rs b/crypto/protocol/src/sign.rs index 69d956a30..fe22d43b5 100644 --- a/crypto/protocol/src/sign.rs +++ b/crypto/protocol/src/sign.rs @@ -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, - #[structopt(short, long)] - data_to_sign: String, + #[structopt(short, long, default_value = "1")] + index: u16, + #[structopt(short, long, use_delimiter(true))] + parties: Vec, + #[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(()) -} \ No newline at end of file + Ok(()) +} diff --git a/crypto/protocol/src/user.rs b/crypto/protocol/src/user.rs index e5b9e21e9..17afa58d5 100644 --- a/crypto/protocol/src/user.rs +++ b/crypto/protocol/src/user.rs @@ -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}; @@ -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> { @@ -69,8 +69,8 @@ impl User { } /// User sends an extrinsic requesting account creation - pub async fn send_registration(&self) -> Result<(), Box> { - + #[allow(dead_code)] + async fn send_registration(&self) -> Result<(), Box> { println!("register is called"); let signer = PairSigner::new(AccountKeyring::Alice.pair());