Skip to content

Commit

Permalink
chore: testing
Browse files Browse the repository at this point in the history
  • Loading branch information
shekohex committed Oct 29, 2024
1 parent 030b7b8 commit d7d6d0f
Show file tree
Hide file tree
Showing 7 changed files with 134 additions and 21 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ thiserror = "1.0"
async-trait = "0.1"
color-eyre = "0.6"
structopt = "0.3.26"
hex = "0.4"
tokio = { version = "1.39", default-features = false, features = ["full"] }
tracing-subscriber = { version = "0.3", features = ["parking_lot", "env-filter"] }
derive_more = { version = "1", default-features = false, features = ["display", "from", "try_from"] }
Expand Down Expand Up @@ -62,7 +63,7 @@ blueprint-metadata = "0.1.6"
path = "src/lib.rs"

[[bin]]
name = "frost-gadget"
name = "frost-blueprint"
path = "src/main.rs"

[package.metadata.blueprint]
Expand All @@ -73,6 +74,6 @@ manager = { Evm = "FrostBlueprint" }
# repo = "frost-blueprint"
# tag = "0.1.0"
# binaries = [
# { arch = "Amd64", os = "Linux", name = "amd64-linux-frost-blueprint-gadget" },
# { arch = "Arm64", os = "Linux", name = "arm64-linux-frost-blueprint-gadget" },
# { arch = "Amd64", os = "Linux", name = "amd64-linux-frost-blueprint" },
# { arch = "Arm64", os = "Linux", name = "arm64-linux-frost-blueprint" },
# ]
1 change: 1 addition & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
fn main() {
println!("cargo:rerun-if-changed=src/main.rs");
println!("cargo:rerun-if-changed=src/lib.rs");
blueprint_metadata::generate_json();
}
39 changes: 39 additions & 0 deletions scripts/insert-dev-keys.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/bin/sh
set -e

function required_tool() {
if ! command -v $1 &> /dev/null
then
echo "$1 could not be found"
echo "hint: install $1 using $2"
exit 1
fi
}

required_tool subkey "cargo install -f subkey"
required_tool cargo-tangle "cargo install -f cargo-tangle"
required_tool jq "https://jqlang.github.io/jq/download/"

function insert_key() {
if [ "$#" -ne 3 ]; then
echo "Usage: insert_key <key> <scheme> <path>"
exit 1
fi
echo "Inserting key $1 (Scheme: $2) into $3"
# remove 0x prefix from seed
seed=$(subkey inspect "$1" --scheme $2 --output-type json | jq -r '.secretSeed' | sed 's/^0x//')
cargo tangle blueprint keygen -k $2 -s $seed -p $3
}

# Alice
insert_key "//Alice" "sr25519" "target/keystore/alice"
insert_key "//Alice" "ed25519" "target/keystore/alice"
insert_key "//Alice" "ecdsa" "target/keystore/alice"
# Bob
insert_key "//Bob" "sr25519" "target/keystore/bob"
insert_key "//Bob" "ed25519" "target/keystore/bob"
insert_key "//Bob" "ecdsa" "target/keystore/bob"
# Charlie
insert_key "//Charlie" "sr25519" "target/keystore/charlie"
insert_key "//Charlie" "ed25519" "target/keystore/charlie"
insert_key "//Charlie" "ecdsa" "target/keystore/charlie"
84 changes: 75 additions & 9 deletions src/keygen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,21 @@ impl<C: Ciphersuite> From<frost_core::Error<C>> for Error {
}
}

/// Run Keygen Protocol between the operators and return the public key.
///
/// # Parameters
/// - `ciphersuite`: The ciphersuite to use in the keygen protocol
/// - `threshold`: The threshold of the keygen protocol.
/// # Returns
/// The public key generated by the keygen protocol.
///
/// # Errors
/// - `UnknwonCiphersuite`: The ciphersuite is not supported.
/// - `SelfNotInOperators`: The current operator is not in the operators.
///
/// # Note
/// - `ciphersuite`: 0 for Ed25519, 1 for Secp256k1.
/// - `threshold`: The threshold of the keygen protocol should be less than the number of operators.
#[sdk::job(
id = 0,
params(ciphersuite, threshold),
Expand Down Expand Up @@ -72,6 +87,7 @@ pub async fn keygen(
.ok_or(Error::SelfNotInOperators)?
.saturating_add(1);

sdk::info!(%n, %i, %ciphersuite, "Keygen");
let net = context.gossip_network();
let rng = random::rand::rngs::OsRng;
let key = match ciphersuite {
Expand Down Expand Up @@ -110,7 +126,9 @@ async fn keygen_internal<C: Ciphersuite, R: random::RngCore + random::CryptoRng,
.collect::<Result<BTreeMap<_, _>, _>>()?;
let identifier = frost_core::Identifier::try_from(i)?;
assert_eq!(i, identifier_to_u16[&identifier]);
let required_msgs = usize::from(n - 1);
// Round 1 (Broadcast)
sdk::debug!(%i, "Round 1");
let (round1_secret_package, round1_package) = dkg::part1::<C, R>(identifier, n, t, rng)?;

let round1_identifier_info = IdentifierInfo {
Expand All @@ -130,21 +148,42 @@ async fn keygen_internal<C: Ciphersuite, R: random::RngCore + random::CryptoRng,
None,
);
net.send_message(round1).await?;

sdk::debug!(%i, "Sent Round 1 Package");
// Wait for all round1_package. (n - 1) round1_package.
let mut round1_packages = BTreeMap::new();

sdk::debug!(%i, "Waiting for Round 1 Messages");
while let Some(msg) = net.next_message().await {
let round1_package: Round1Package<C> = sdk::network::deserialize(&msg.payload)?;
let from = frost_core::Identifier::try_from(msg.sender.user_id)?;
round1_packages.insert(from, round1_package);
assert_ne!(from, identifier, "Received its own Round 1 Package");
let old = round1_packages.insert(from, round1_package);
assert!(
old.is_none(),
"Received duplicate Round 2 Package from {}",
msg.sender.user_id
);

sdk::debug!(
%i,
from = %msg.sender.user_id,
recv = %round1_packages.len(),
required = required_msgs,
remaining = required_msgs - round1_packages.len(),
"Received Round 1 Package"
);

if round1_packages.len() == n as usize - 1 {
if round1_packages.len() == required_msgs {
break;
}
}

// Round 1 is done.
sdk::debug!(%i, "Round 1 Done");

// Round 2 (P2P)
sdk::debug!(%i, "Round 2");
let (round2_secret_package, round2_packages) =
dkg::part2(round1_secret_package, &round1_packages)?;

Expand All @@ -156,7 +195,8 @@ async fn keygen_internal<C: Ciphersuite, R: random::RngCore + random::CryptoRng,
task_id: None,
};
let from = i;
let to = Some(identifier_to_u16[&to]);
let to = identifier_to_u16.get(&to).cloned();
assert!(to.is_some(), "Unknown identifier: {:?}", to);
let round2 = N::build_protocol_message::<Round2Package<C>>(
round2_identifier_info.clone(),
from,
Expand All @@ -166,24 +206,51 @@ async fn keygen_internal<C: Ciphersuite, R: random::RngCore + random::CryptoRng,
None,
);
net.send_message(round2).await?;
sdk::debug!(%i, to = ?to, "Sent Round 2 Package P2P");
}

// Wait for all round2_package. (n - 1) round2_package.
sdk::debug!(%i, "Waiting for Round 2 Messages");
let mut round2_packages = BTreeMap::new();
while let Some(msg) = net.next_message().await {
let round2_package: Round2Package<C> = sdk::network::deserialize(&msg.payload)?;
let from = frost_core::Identifier::try_from(msg.sender.user_id)?;
round2_packages.insert(from, round2_package);
assert_ne!(from, identifier, "Received its own Round 2 Package");
let old = round2_packages.insert(from, round2_package);
assert!(
old.is_none(),
"Received duplicate Round 2 Package from {}",
msg.sender.user_id
);

sdk::debug!(
%i,
from = %msg.sender.user_id,
recv = %round2_packages.len(),
required = required_msgs,
remaining = required_msgs - round2_packages.len(),
"Received Round 2 Package"
);

if round2_packages.len() == n as usize - 1 {
if round2_packages.len() == required_msgs {
break;
}
}

// Round 2 is done.
sdk::debug!(%i, "Round 2 Done");

// Part 3, Offline.
sdk::debug!(%i, "Round 3 (Offline)");
let (key_package, public_key_package) =
dkg::part3(&round2_secret_package, &round1_packages, &round2_packages)?;

sdk::debug!(%i, "Round 3 Done");
sdk::debug!(
%i,
pubkey = %hex::encode(public_key_package.verifying_key().serialize()?),
"Keygen Done"
);
// TODO: Store key_package somewhere by the public key package.
Ok(public_key_package.verifying_key().clone())
}
Expand Down Expand Up @@ -212,21 +279,20 @@ mod tests {
let manifest_path = base_path.join("Cargo.toml");

let opts = Opts {
pkg_name: option_env!("CARGO_PKG_NAME").map(ToOwned::to_owned),
pkg_name: option_env!("CARGO_BIN_NAME").map(ToOwned::to_owned),
http_rpc_url: "http://127.0.0.1:9944".to_string(),
ws_rpc_url: "ws://127.0.0.1:9944".to_string(),
manifest_path,
signer: None,
signer_evm: None,
};

new_test_ext_blueprint_manager::<5, 1, (), _, _>((), opts, run_test_blueprint_manager)
new_test_ext_blueprint_manager::<2, 1, (), _, _>((), opts, run_test_blueprint_manager)
.await
.execute_with_async(move |client, handles| async move {
// At this point, blueprint has been deployed, every node has registered
// as an operator for the relevant services, and, all gadgets are running

// What's left: Submit a job, wait for the job to finish, then assert the job results
let keypair = handles[0].sr25519_id().clone();

let service_id = get_next_service_id(client)
Expand All @@ -242,7 +308,7 @@ mod tests {

// Pass the arguments
let ciphersuite = Field::Uint8(CipherSuite::Ed25519 as u8);
let threshold = Field::Uint8(3);
let threshold = Field::Uint16(2);
let job_args = Args::from([ciphersuite, threshold]);

// Next step: submit a job under that service/job id
Expand Down
9 changes: 7 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
use gadget_sdk as sdk;
use gadget_sdk::ctx::GossipNetworkContext;

use derive_more::TryFrom;
use derive_more::{Display, TryFrom};
use sdk::ctx::{KeystoreContext, ServicesContext, TangleClientContext};

/// FROST Keygen module
pub mod keygen;

pub const NETWORK_PROTOCOL: &str = "/zcash/frost/1.0.0";

#[derive(Clone, KeystoreContext, TangleClientContext, ServicesContext)]
pub struct ServiceContext {
#[config]
Expand All @@ -16,18 +18,21 @@ pub struct ServiceContext {

impl GossipNetworkContext for ServiceContext {
fn gossip_network(&self) -> &gadget_sdk::network::gossip::GossipHandle {
// acts as a getter.
&self.gossip_handle
}
}

/// All supported ciphersuites
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFrom)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFrom, Display)]
#[try_from(repr)]
#[repr(u8)]
#[non_exhaustive]
pub enum CipherSuite {
/// Ed25519 Ciphersuite from [`frost_ed25519`](https://docs.rs/frost-ed25519)
#[display("Ed25519")]
Ed25519 = 0x00,
/// Secp256k1 Ciphersuite from [`frost_secp256k1`](https://docs.rs/frost-secp256k1)
#[display("Secp256k1")]
Secp256k1 = 0x01,
}
14 changes: 7 additions & 7 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ use tangle_testnet_runtime::api::{self, runtime_types::tangle_primitives};

#[sdk::main(env)]
async fn main() -> Result<()> {
let span = env.span.clone();
let _spanned = span.enter();
let signer = env.first_sr25519_signer()?;
let network_identity = {
let ed25519 = env.first_ed25519_signer()?.signer().clone();
Expand All @@ -21,8 +23,7 @@ async fn main() -> Result<()> {
env.bootnodes.clone(),
env.bind_addr,
env.bind_port,
// TODO: add version to the service name.
"frost",
blueprint::NETWORK_PROTOCOL,
);
let gossip_handle = sdk::network::setup::start_p2p_network(network_config)?;

Expand Down Expand Up @@ -56,16 +57,15 @@ async fn main() -> Result<()> {
};

// Create the event handler from the job
let say_hello_job = blueprint::keygen::KeygenEventHandler {
let keygen = blueprint::keygen::KeygenEventHandler {
service_id,
client,
signer,
context,
};

tracing::info!("Starting the event watcher ...");
MultiJobRunner::new(env).job(say_hello_job).run().await?;

tracing::info!("Exiting...");
sdk::info!("Starting the event watcher ...");
MultiJobRunner::new(env).job(keygen).run().await?;
sdk::info!("Exiting...");
Ok(())
}

0 comments on commit d7d6d0f

Please sign in to comment.