Skip to content

Commit

Permalink
Add GG20 examples (#159)
Browse files Browse the repository at this point in the history
  • Loading branch information
Denis Varlakov authored Jan 4, 2022
1 parent b1448e2 commit c480a8a
Show file tree
Hide file tree
Showing 13 changed files with 629 additions and 94 deletions.
28 changes: 28 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Build

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
schedule:
- cron: '0 5 * * *'
workflow_call:

env:
CARGO_TERM_COLOR: always

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: Swatinem/rust-cache@v1
- name: Build
run: cargo build --verbose
- name: Run tests
run: cargo test --verbose
- name: Check formatting
run: cargo fmt --all -- --check
- name: Run clippy
run: cargo clippy -- -D clippy::all
19 changes: 19 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: Publish

on:
push:
tags:
- v*.*.*

jobs:
build:
uses: ZenGo-X/multi-party-ecdsa/.github/workflows/build.yml@master
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Publish crate
env:
TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
run: |
cargo publish --token "$TOKEN"
21 changes: 0 additions & 21 deletions .travis.yml

This file was deleted.

11 changes: 8 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,18 +56,23 @@ optional = true
criterion = "0.3"
rust-crypto = "0.2"
hex = "0.4"
rocket = { version = "0.4.2", default-features = false }
rocket_contrib = "0.4.2"
tokio = { version = "1", default-features = false, features = ["macros"] }
futures = "0.3"
rocket = { version = "0.5.0-rc.1", default-features = false, features = ["json"] }
reqwest = { version = "0.9", default-features = false }
uuid = { version = "0.8", features = ["v4"] }
serde_json = "1.0"
rand = "0.7"
surf = "2"
async-sse = "5"
anyhow = "1"
structopt = "0.3"

thiserror = "1.0.23"
round-based = { version = "0.1.4", features = ["dev"] }

[[example]]
name = "sm_manager"
name = "gg18_sm_manager"

[[example]]
name = "gg18_sign_client"
Expand Down
73 changes: 60 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,64 @@ The library implements four different protocols for threshold ECDSA. The protoco
|Castagnos et. al. 19 [3]| Currently enabled as a feature in this library. To Enable, build with `--features=cclst`. to Test, use `cargo test --features=cclst -- --test-threads=1` |
| Gennaro, Goldfeder 20 [4] | A full threshold protocol that supports identifying malicious parties. If signing fails - a list of malicious parties is returned. The protocol requires only a broadcast channel (all messages are broadcasted)|

## Run Demo
## Run GG20 Demo

In the following steps we will generate 2-of-3 threshold signing key and sign a message with 2 parties.

### Setup

1. You need [Rust](https://rustup.rs/) and [GMP library](https://gmplib.org) (optionally) to be installed on your computer.
2. - Run `cargo build --release --examples`
- Don't have GMP installed? Use this command instead:
```bash
cargo build --release --examples --no-default-features --features curv-kzen/num-bigint
```
But keep in mind that it will be less efficient.

Either of commands will produce binaries into `./target/release/examples/` folder.
3. `cd ./target/release/examples/`

### Start an SM server

`./gg20_sm_manager`

That will start an HTTP server on `http://127.0.0.1:8000`. Other parties will use that server in order to communicate with
each other. Note that communication channels are neither encrypted nor authenticated. In production, you must encrypt and
authenticate parties messages.

### Run Keygen

Open 3 terminal tabs for each party. Run:

1. `./gg20_keygen -t 1 -n 3 -i 1 --output local-share1.json`
2. `./gg20_keygen -t 1 -n 3 -i 2 --output local-share2.json`
3. `./gg20_keygen -t 1 -n 3 -i 3 --output local-share3.json`

Each command corresponds to one party. Once keygen is completed, you'll have 3 new files:
`local-share1.json`, `local-share2.json`, `local-share3.json` corresponding to local secret
share of each party.
### Run Signing
Since we use 2-of-3 scheme (`t=1 n=3`), any two parties can sign a message. Run:
1. `./gg20_signing -p 1,2 -d "hello" -l local-share1.json`
2. `./gg20_signing -p 1,2 -d "hello" -l local-share2.json`
Each party will produce a resulting signature. `-p 1,2` specifies indexes of parties
who attends in signing (each party has an associated index given at keygen, see argument
`-i`), `-l file.json` sets a path to a file with secret local share, and `-d "hello"`
is a message being signed.
### Running Demo on different computers
While previous steps show how to run keygen & signing on local computer, you actually can
run each party on dedicated machine. To do this, you should ensure that parties can reach
SM Server, and specify its address via command line argument, eg:
`./gg20_keygen --address http://10.0.1.9:8000/ ...`
## Run GG18 Demo
The following steps are for setup, key generation with `n` parties and signing with `t+1` parties.
Expand All @@ -44,7 +101,7 @@ The following steps are for setup, key generation with `n` parties and signing w
2. Install [Rust](https://rustup.rs/). Run `cargo build --release --examples` (it will build into `/target/release/examples/`)
3. Run the shared state machine: `./sm_manager`. Currently configured to be in `127.0.0.1:8001`, this can be changed in `Rocket.toml` file. The `Rocket.toml` file should be in the same folder you run `sm_manager` from.
3. Run the shared state machine: `./gg18_sm_manager`. Currently configured to be in `127.0.0.1:8001`, this can be changed in `Rocket.toml` file. The `Rocket.toml` file should be in the same folder you run `sm_manager` from.
### KeyGen
Expand All @@ -71,21 +128,11 @@ Then, run:

Run `./run.sh` (located in `/demo` folder) in the main folder. Move `params` file to the same folder as the executables (usually `/target/release/examples`). The script will spawn a shared state machine, clients in the number of parties and signing requests for the `threshold + 1` first parties.

`sm_manager` rocket server runs in _production_ mode by default. You may modify the `./run.sh` to config it to run in different environments. For example, to run rocket server in _development_:
`gg18_sm_manager` rocket server runs in _production_ mode by default. You may modify the `./run.sh` to config it to run in different environments. For example, to run rocket server in _development_:
```
ROCKET_ENV=development ./target/release/examples/sm_manager
```
### GG20 demo
Run `./demo/run20.sh`. You would need nightly rust toolchain and libgmp to be installed and available. The `params.json` file should be changed in case you want to change the default split of 2-of-3. The script starts sm_manager which exposes a shared state over http for message passing between parties. Multiple instances of the gg20_keygen_client and gg20_sign_client communicate via the sm_manager. This demo does not implement the identifiable abort portion of the protocol yet.


| !["Multiparty ECDSA Demo"][demo] |
| :------------------------------------------------: |
| _A 5 parties setup with 3 signers (threshold = 2)_ |

[demo]: https://raw.githubusercontent.com/KZen-networks/multi-party-ecdsa/master/demo/MP-ECDSA%20demo.gif

## Contributions & Development Process
The contribution workflow is described in [CONTRIBUTING.md](CONTRIBUTING.md), in addition **the [Rust utilities wiki](https://github.com/KZen-networks/rust-utils/wiki) contains information on workflow and environment set-up**.
Expand Down
6 changes: 3 additions & 3 deletions demo/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ echo "Multi-party ECDSA parties:$n threshold:$t"
sleep 1

rm keys?.store
killall sm_manager gg18_keygen_client gg18_sign_client 2> /dev/null
killall gg18_sm_manager gg18_keygen_client gg18_sign_client 2> /dev/null

./target/release/examples/sm_manager &
./target/release/examples/gg18_sm_manager &

sleep 2
echo "keygen part"
Expand All @@ -37,4 +37,4 @@ do
sleep 3
done

killall sm_manager 2> /dev/null
killall gg18_sm_manager 2> /dev/null
41 changes: 0 additions & 41 deletions demo/run20.sh

This file was deleted.

23 changes: 11 additions & 12 deletions examples/sm_manager.rs → examples/gg18_sm_manager.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
#![feature(proc_macro_hygiene, decl_macro)]

use std::collections::HashMap;
use std::fs;
use std::sync::RwLock;

use rocket::serde::json::Json;
use rocket::{post, routes, State};
use rocket_contrib::json::Json;
use uuid::Uuid;

mod common;
use common::{Entry, Index, Key, Params, PartySignup};

#[post("/get", format = "json", data = "<request>")]
fn get(
db_mtx: State<RwLock<HashMap<Key, String>>>,
db_mtx: &State<RwLock<HashMap<Key, String>>>,
request: Json<Index>,
) -> Json<Result<Entry, ()>> {
let index: Index = request.0;
Expand All @@ -31,15 +29,15 @@ fn get(
}

#[post("/set", format = "json", data = "<request>")]
fn set(db_mtx: State<RwLock<HashMap<Key, String>>>, request: Json<Entry>) -> Json<Result<(), ()>> {
fn set(db_mtx: &State<RwLock<HashMap<Key, String>>>, request: Json<Entry>) -> Json<Result<(), ()>> {
let entry: Entry = request.0;
let mut hm = db_mtx.write().unwrap();
hm.insert(entry.key.clone(), entry.value);
Json(Ok(()))
}

#[post("/signupkeygen", format = "json")]
fn signup_keygen(db_mtx: State<RwLock<HashMap<Key, String>>>) -> Json<Result<PartySignup, ()>> {
fn signup_keygen(db_mtx: &State<RwLock<HashMap<Key, String>>>) -> Json<Result<PartySignup, ()>> {
let data = fs::read_to_string("params.json")
.expect("Unable to read params, make sure config file is present in the same folder ");
let params: Params = serde_json::from_str(&data).unwrap();
Expand Down Expand Up @@ -70,7 +68,7 @@ fn signup_keygen(db_mtx: State<RwLock<HashMap<Key, String>>>) -> Json<Result<Par
}

#[post("/signupsign", format = "json")]
fn signup_sign(db_mtx: State<RwLock<HashMap<Key, String>>>) -> Json<Result<PartySignup, ()>> {
fn signup_sign(db_mtx: &State<RwLock<HashMap<Key, String>>>) -> Json<Result<PartySignup, ()>> {
//read parameters:
let data = fs::read_to_string("params.json")
.expect("Unable to read params, make sure config file is present in the same folder ");
Expand Down Expand Up @@ -100,9 +98,8 @@ fn signup_sign(db_mtx: State<RwLock<HashMap<Key, String>>>) -> Json<Result<Party
Json(Ok(party_signup))
}

//refcell, arc

fn main() {
#[tokio::main]
async fn main() {
// let mut my_config = Config::development();
// my_config.set_port(18001);
let db: HashMap<Key, String> = HashMap::new();
Expand Down Expand Up @@ -137,8 +134,10 @@ fn main() {
hm.insert(sign_key, serde_json::to_string(&party_signup_sign).unwrap());
}
/////////////////////////////////////////////////////////////////
rocket::ignite()
rocket::build()
.mount("/", routes![get, set, signup_keygen, signup_sign])
.manage(db_mtx)
.launch();
.launch()
.await
.unwrap();
}
58 changes: 58 additions & 0 deletions examples/gg20_keygen.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
use anyhow::{anyhow, ensure, Context, Result};
use futures::StreamExt;
use std::path::PathBuf;
use structopt::StructOpt;

use multi_party_ecdsa::protocols::multi_party_ecdsa::gg_2020::state_machine::keygen::Keygen;
use round_based::async_runtime::AsyncProtocol;

mod gg20_sm_client;
use gg20_sm_client::join_computation;

#[derive(Debug, StructOpt)]
struct Cli {
#[structopt(short, long, default_value = "http://localhost:8000/")]
address: surf::Url,
#[structopt(short, long, default_value = "default-keygen")]
room: String,
#[structopt(short, long)]
output: PathBuf,

#[structopt(short, long)]
index: u16,
#[structopt(short, long)]
threshold: u16,
#[structopt(short, long)]
number_of_parties: u16,
}

#[tokio::main]
async fn main() -> Result<()> {
let args: Cli = Cli::from_args();
let mut output_file = tokio::fs::OpenOptions::new()
.write(true)
.create_new(true)
.open(args.output)
.await
.context("cannot create output file")?;

let (_i, incoming, outgoing) = join_computation(args.address, &args.room)
.await
.context("join computation")?;

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

let keygen = Keygen::new(args.index, args.threshold, args.number_of_parties)?;
let output = AsyncProtocol::new(keygen, incoming, outgoing)
.run()
.await
.map_err(|e| anyhow!("protocol execution terminated with error: {}", e))?;
let output = serde_json::to_vec_pretty(&output).context("serialize output")?;
tokio::io::copy(&mut output.as_slice(), &mut output_file)
.await
.context("save output to file")?;

Ok(())
}
Loading

0 comments on commit c480a8a

Please sign in to comment.