mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-13 14:39:25 +00:00
Update processor key gen tests to the eVRF DKG
This commit is contained in:
@@ -23,8 +23,8 @@ zeroize = { version = "1", default-features = false }
|
|||||||
rand_core = { version = "0.6", default-features = false, features = ["getrandom"] }
|
rand_core = { version = "0.6", default-features = false, features = ["getrandom"] }
|
||||||
|
|
||||||
curve25519-dalek = "4"
|
curve25519-dalek = "4"
|
||||||
ciphersuite = { path = "../../crypto/ciphersuite", default-features = false, features = ["secp256k1", "ristretto"] }
|
ciphersuite = { path = "../../crypto/ciphersuite", default-features = false, features = ["secp256k1", "ed25519", "ristretto"] }
|
||||||
dkg = { path = "../../crypto/dkg", default-features = false, features = ["tests"] }
|
dkg = { path = "../../crypto/dkg", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
bitcoin-serai = { path = "../../networks/bitcoin" }
|
bitcoin-serai = { path = "../../networks/bitcoin" }
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,16 @@
|
|||||||
use std::sync::{OnceLock, Mutex};
|
use std::sync::{OnceLock, Mutex};
|
||||||
|
|
||||||
use zeroize::Zeroizing;
|
use zeroize::Zeroizing;
|
||||||
use rand_core::{RngCore, OsRng};
|
use rand_core::OsRng;
|
||||||
|
|
||||||
use ciphersuite::{group::ff::PrimeField, Ciphersuite, Ristretto};
|
use ciphersuite::{
|
||||||
|
group::{
|
||||||
|
ff::{Field, PrimeField},
|
||||||
|
GroupEncoding,
|
||||||
|
},
|
||||||
|
Ciphersuite, Secp256k1, Ed25519, Ristretto,
|
||||||
|
};
|
||||||
|
use dkg::evrf::*;
|
||||||
|
|
||||||
use serai_client::primitives::NetworkId;
|
use serai_client::primitives::NetworkId;
|
||||||
use messages::{ProcessorMessage, CoordinatorMessage};
|
use messages::{ProcessorMessage, CoordinatorMessage};
|
||||||
@@ -24,13 +31,40 @@ mod tests;
|
|||||||
|
|
||||||
static UNIQUE_ID: OnceLock<Mutex<u16>> = OnceLock::new();
|
static UNIQUE_ID: OnceLock<Mutex<u16>> = OnceLock::new();
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct EvrfPublicKeys {
|
||||||
|
substrate: [u8; 32],
|
||||||
|
network: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn processor_instance(
|
pub fn processor_instance(
|
||||||
network: NetworkId,
|
network: NetworkId,
|
||||||
port: u32,
|
port: u32,
|
||||||
message_queue_key: <Ristretto as Ciphersuite>::F,
|
message_queue_key: <Ristretto as Ciphersuite>::F,
|
||||||
) -> Vec<TestBodySpecification> {
|
) -> (Vec<TestBodySpecification>, EvrfPublicKeys) {
|
||||||
let mut entropy = [0; 32];
|
let substrate_evrf_key =
|
||||||
OsRng.fill_bytes(&mut entropy);
|
<<Ristretto as EvrfCurve>::EmbeddedCurve as Ciphersuite>::F::random(&mut OsRng);
|
||||||
|
let substrate_evrf_pub_key =
|
||||||
|
(<Ristretto as EvrfCurve>::EmbeddedCurve::generator() * substrate_evrf_key).to_bytes();
|
||||||
|
let substrate_evrf_key = substrate_evrf_key.to_repr();
|
||||||
|
|
||||||
|
let (network_evrf_key, network_evrf_pub_key) = match network {
|
||||||
|
NetworkId::Serai => panic!("starting a processor for Serai"),
|
||||||
|
NetworkId::Bitcoin | NetworkId::Ethereum => {
|
||||||
|
let evrf_key =
|
||||||
|
<<Secp256k1 as EvrfCurve>::EmbeddedCurve as Ciphersuite>::F::random(&mut OsRng);
|
||||||
|
let pub_key =
|
||||||
|
(<Secp256k1 as EvrfCurve>::EmbeddedCurve::generator() * evrf_key).to_bytes().to_vec();
|
||||||
|
(evrf_key.to_repr(), pub_key)
|
||||||
|
}
|
||||||
|
NetworkId::Monero => {
|
||||||
|
let evrf_key = <<Ed25519 as EvrfCurve>::EmbeddedCurve as Ciphersuite>::F::random(&mut OsRng);
|
||||||
|
let pub_key =
|
||||||
|
(<Ed25519 as EvrfCurve>::EmbeddedCurve::generator() * evrf_key).to_bytes().to_vec();
|
||||||
|
(evrf_key.to_repr(), pub_key)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let network_str = match network {
|
let network_str = match network {
|
||||||
NetworkId::Serai => panic!("starting a processor for Serai"),
|
NetworkId::Serai => panic!("starting a processor for Serai"),
|
||||||
@@ -47,7 +81,8 @@ pub fn processor_instance(
|
|||||||
.replace_env(
|
.replace_env(
|
||||||
[
|
[
|
||||||
("MESSAGE_QUEUE_KEY".to_string(), hex::encode(message_queue_key.to_repr())),
|
("MESSAGE_QUEUE_KEY".to_string(), hex::encode(message_queue_key.to_repr())),
|
||||||
("ENTROPY".to_string(), hex::encode(entropy)),
|
("SUBSTRATE_EVRF_KEY".to_string(), hex::encode(substrate_evrf_key)),
|
||||||
|
("NETWORK_EVRF_KEY".to_string(), hex::encode(network_evrf_key)),
|
||||||
("NETWORK".to_string(), network_str.to_string()),
|
("NETWORK".to_string(), network_str.to_string()),
|
||||||
("NETWORK_RPC_LOGIN".to_string(), format!("{RPC_USER}:{RPC_PASS}")),
|
("NETWORK_RPC_LOGIN".to_string(), format!("{RPC_USER}:{RPC_PASS}")),
|
||||||
("NETWORK_RPC_PORT".to_string(), port.to_string()),
|
("NETWORK_RPC_PORT".to_string(), port.to_string()),
|
||||||
@@ -75,20 +110,25 @@ pub fn processor_instance(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
res
|
(res, EvrfPublicKeys { substrate: substrate_evrf_pub_key, network: network_evrf_pub_key })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ProcessorKeys {
|
||||||
|
coordinator: <Ristretto as Ciphersuite>::F,
|
||||||
|
evrf: EvrfPublicKeys,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Handles = (String, String, String, String);
|
pub type Handles = (String, String, String, String);
|
||||||
pub fn processor_stack(
|
pub fn processor_stack(
|
||||||
network: NetworkId,
|
network: NetworkId,
|
||||||
network_hostname_override: Option<String>,
|
network_hostname_override: Option<String>,
|
||||||
) -> (Handles, <Ristretto as Ciphersuite>::F, Vec<TestBodySpecification>) {
|
) -> (Handles, ProcessorKeys, Vec<TestBodySpecification>) {
|
||||||
let (network_composition, network_rpc_port) = network_instance(network);
|
let (network_composition, network_rpc_port) = network_instance(network);
|
||||||
|
|
||||||
let (coord_key, message_queue_keys, message_queue_composition) =
|
let (coord_key, message_queue_keys, message_queue_composition) =
|
||||||
serai_message_queue_tests::instance();
|
serai_message_queue_tests::instance();
|
||||||
|
|
||||||
let mut processor_compositions =
|
let (mut processor_compositions, evrf_keys) =
|
||||||
processor_instance(network, network_rpc_port, message_queue_keys[&network]);
|
processor_instance(network, network_rpc_port, message_queue_keys[&network]);
|
||||||
|
|
||||||
// Give every item in this stack a unique ID
|
// Give every item in this stack a unique ID
|
||||||
@@ -155,7 +195,7 @@ pub fn processor_stack(
|
|||||||
handles[2].clone(),
|
handles[2].clone(),
|
||||||
handles.get(3).cloned().unwrap_or(String::new()),
|
handles.get(3).cloned().unwrap_or(String::new()),
|
||||||
),
|
),
|
||||||
coord_key,
|
ProcessorKeys { coordinator: coord_key, evrf: evrf_keys },
|
||||||
compositions,
|
compositions,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -170,6 +210,8 @@ pub struct Coordinator {
|
|||||||
processor_handle: String,
|
processor_handle: String,
|
||||||
relayer_handle: String,
|
relayer_handle: String,
|
||||||
|
|
||||||
|
evrf_keys: EvrfPublicKeys,
|
||||||
|
|
||||||
next_send_id: u64,
|
next_send_id: u64,
|
||||||
next_recv_id: u64,
|
next_recv_id: u64,
|
||||||
queue: MessageQueue,
|
queue: MessageQueue,
|
||||||
@@ -180,7 +222,7 @@ impl Coordinator {
|
|||||||
network: NetworkId,
|
network: NetworkId,
|
||||||
ops: &DockerOperations,
|
ops: &DockerOperations,
|
||||||
handles: Handles,
|
handles: Handles,
|
||||||
coord_key: <Ristretto as Ciphersuite>::F,
|
keys: ProcessorKeys,
|
||||||
) -> Coordinator {
|
) -> Coordinator {
|
||||||
let rpc = ops.handle(&handles.1).host_port(2287).unwrap();
|
let rpc = ops.handle(&handles.1).host_port(2287).unwrap();
|
||||||
let rpc = rpc.0.to_string() + ":" + &rpc.1.to_string();
|
let rpc = rpc.0.to_string() + ":" + &rpc.1.to_string();
|
||||||
@@ -193,9 +235,11 @@ impl Coordinator {
|
|||||||
processor_handle: handles.2,
|
processor_handle: handles.2,
|
||||||
relayer_handle: handles.3,
|
relayer_handle: handles.3,
|
||||||
|
|
||||||
|
evrf_keys: keys.evrf,
|
||||||
|
|
||||||
next_send_id: 0,
|
next_send_id: 0,
|
||||||
next_recv_id: 0,
|
next_recv_id: 0,
|
||||||
queue: MessageQueue::new(Service::Coordinator, rpc, Zeroizing::new(coord_key)),
|
queue: MessageQueue::new(Service::Coordinator, rpc, Zeroizing::new(keys.coordinator)),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Sleep for up to a minute in case the external network's RPC has yet to start
|
// Sleep for up to a minute in case the external network's RPC has yet to start
|
||||||
@@ -302,6 +346,11 @@ impl Coordinator {
|
|||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the eVRF keys for the associated processor.
|
||||||
|
pub fn evrf_keys(&self) -> EvrfPublicKeys {
|
||||||
|
self.evrf_keys.clone()
|
||||||
|
}
|
||||||
|
|
||||||
/// Send a message to a processor as its coordinator.
|
/// Send a message to a processor as its coordinator.
|
||||||
pub async fn send_message(&mut self, msg: impl Into<CoordinatorMessage>) {
|
pub async fn send_message(&mut self, msg: impl Into<CoordinatorMessage>) {
|
||||||
let msg: CoordinatorMessage = msg.into();
|
let msg: CoordinatorMessage = msg.into();
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ use std::{
|
|||||||
time::{SystemTime, Duration},
|
time::{SystemTime, Duration},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use rand_core::RngCore;
|
||||||
|
|
||||||
use dkg::{Participant, tests::clone_without};
|
use dkg::{Participant, tests::clone_without};
|
||||||
|
|
||||||
use messages::{coordinator::*, SubstrateContext};
|
use messages::{coordinator::*, SubstrateContext};
|
||||||
|
|||||||
@@ -1,30 +1,24 @@
|
|||||||
use std::{collections::HashMap, time::SystemTime};
|
use std::time::SystemTime;
|
||||||
|
|
||||||
use dkg::{Participant, ThresholdParams, tests::clone_without};
|
use dkg::Participant;
|
||||||
|
|
||||||
use serai_client::{
|
use serai_client::{
|
||||||
primitives::{NetworkId, BlockHash, PublicKey},
|
primitives::{NetworkId, BlockHash, PublicKey},
|
||||||
validator_sets::primitives::{Session, KeyPair},
|
validator_sets::primitives::{Session, KeyPair},
|
||||||
};
|
};
|
||||||
|
|
||||||
use messages::{SubstrateContext, key_gen::KeyGenId, CoordinatorMessage, ProcessorMessage};
|
use messages::{SubstrateContext, CoordinatorMessage, ProcessorMessage};
|
||||||
|
|
||||||
use crate::{*, tests::*};
|
use crate::{*, tests::*};
|
||||||
|
|
||||||
pub(crate) async fn key_gen(coordinators: &mut [Coordinator]) -> KeyPair {
|
pub(crate) async fn key_gen(coordinators: &mut [Coordinator]) -> KeyPair {
|
||||||
// Perform an interaction with all processors via their coordinators
|
// Perform an interaction with all processors via their coordinators
|
||||||
async fn interact_with_all<
|
async fn interact_with_all<FR: FnMut(Participant, messages::key_gen::ProcessorMessage)>(
|
||||||
FS: Fn(Participant) -> messages::key_gen::CoordinatorMessage,
|
|
||||||
FR: FnMut(Participant, messages::key_gen::ProcessorMessage),
|
|
||||||
>(
|
|
||||||
coordinators: &mut [Coordinator],
|
coordinators: &mut [Coordinator],
|
||||||
message: FS,
|
|
||||||
mut recv: FR,
|
mut recv: FR,
|
||||||
) {
|
) {
|
||||||
for (i, coordinator) in coordinators.iter_mut().enumerate() {
|
for (i, coordinator) in coordinators.iter_mut().enumerate() {
|
||||||
let participant = Participant::new(u16::try_from(i + 1).unwrap()).unwrap();
|
let participant = Participant::new(u16::try_from(i + 1).unwrap()).unwrap();
|
||||||
coordinator.send_message(CoordinatorMessage::KeyGen(message(participant))).await;
|
|
||||||
|
|
||||||
match coordinator.recv_message().await {
|
match coordinator.recv_message().await {
|
||||||
ProcessorMessage::KeyGen(msg) => recv(participant, msg),
|
ProcessorMessage::KeyGen(msg) => recv(participant, msg),
|
||||||
_ => panic!("processor didn't return KeyGen message"),
|
_ => panic!("processor didn't return KeyGen message"),
|
||||||
@@ -33,85 +27,67 @@ pub(crate) async fn key_gen(coordinators: &mut [Coordinator]) -> KeyPair {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Order a key gen
|
// Order a key gen
|
||||||
let id = KeyGenId { session: Session(0), attempt: 0 };
|
let session = Session(0);
|
||||||
|
|
||||||
let mut commitments = HashMap::new();
|
let mut evrf_public_keys = vec![];
|
||||||
interact_with_all(
|
for coordinator in &*coordinators {
|
||||||
coordinators,
|
let keys = coordinator.evrf_keys();
|
||||||
|participant| messages::key_gen::CoordinatorMessage::GenerateKey {
|
evrf_public_keys.push((keys.substrate, keys.network));
|
||||||
id,
|
}
|
||||||
params: ThresholdParams::new(
|
|
||||||
u16::try_from(THRESHOLD).unwrap(),
|
let mut participations = vec![];
|
||||||
u16::try_from(COORDINATORS).unwrap(),
|
for coordinator in &mut *coordinators {
|
||||||
|
coordinator
|
||||||
|
.send_message(CoordinatorMessage::KeyGen(
|
||||||
|
messages::key_gen::CoordinatorMessage::GenerateKey {
|
||||||
|
session,
|
||||||
|
threshold: u16::try_from(THRESHOLD).unwrap(),
|
||||||
|
evrf_public_keys: evrf_public_keys.clone(),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
// This takes forever on debug, as we use in these tests
|
||||||
|
tokio::time::sleep(core::time::Duration::from_secs(600)).await;
|
||||||
|
interact_with_all(coordinators, |participant, msg| match msg {
|
||||||
|
messages::key_gen::ProcessorMessage::Participation { session: this_session, participation } => {
|
||||||
|
assert_eq!(this_session, session);
|
||||||
|
participations.push(messages::key_gen::CoordinatorMessage::Participation {
|
||||||
|
session,
|
||||||
participant,
|
participant,
|
||||||
)
|
participation,
|
||||||
.unwrap(),
|
});
|
||||||
shares: 1,
|
}
|
||||||
},
|
_ => panic!("processor didn't return Participation in response to GenerateKey"),
|
||||||
|participant, msg| match msg {
|
})
|
||||||
messages::key_gen::ProcessorMessage::Commitments {
|
|
||||||
id: this_id,
|
|
||||||
commitments: mut these_commitments,
|
|
||||||
} => {
|
|
||||||
assert_eq!(this_id, id);
|
|
||||||
assert_eq!(these_commitments.len(), 1);
|
|
||||||
commitments.insert(participant, these_commitments.swap_remove(0));
|
|
||||||
}
|
|
||||||
_ => panic!("processor didn't return Commitments in response to GenerateKey"),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
// Send the commitments to all parties
|
// Send the participations
|
||||||
let mut shares = HashMap::new();
|
|
||||||
interact_with_all(
|
|
||||||
coordinators,
|
|
||||||
|participant| messages::key_gen::CoordinatorMessage::Commitments {
|
|
||||||
id,
|
|
||||||
commitments: clone_without(&commitments, &participant),
|
|
||||||
},
|
|
||||||
|participant, msg| match msg {
|
|
||||||
messages::key_gen::ProcessorMessage::Shares { id: this_id, shares: mut these_shares } => {
|
|
||||||
assert_eq!(this_id, id);
|
|
||||||
assert_eq!(these_shares.len(), 1);
|
|
||||||
shares.insert(participant, these_shares.swap_remove(0));
|
|
||||||
}
|
|
||||||
_ => panic!("processor didn't return Shares in response to GenerateKey"),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
// Send the shares
|
|
||||||
let mut substrate_key = None;
|
let mut substrate_key = None;
|
||||||
let mut network_key = None;
|
let mut network_key = None;
|
||||||
interact_with_all(
|
for participation in participations {
|
||||||
coordinators,
|
for coordinator in &mut *coordinators {
|
||||||
|participant| messages::key_gen::CoordinatorMessage::Shares {
|
coordinator.send_message(participation.clone()).await;
|
||||||
id,
|
}
|
||||||
shares: vec![shares
|
}
|
||||||
.iter()
|
// This also takes a while on debug
|
||||||
.filter_map(|(this_participant, shares)| {
|
tokio::time::sleep(core::time::Duration::from_secs(240)).await;
|
||||||
shares.get(&participant).cloned().map(|share| (*this_participant, share))
|
interact_with_all(coordinators, |_, msg| match msg {
|
||||||
})
|
messages::key_gen::ProcessorMessage::GeneratedKeyPair {
|
||||||
.collect()],
|
session: this_session,
|
||||||
},
|
substrate_key: this_substrate_key,
|
||||||
|_, msg| match msg {
|
network_key: this_network_key,
|
||||||
messages::key_gen::ProcessorMessage::GeneratedKeyPair {
|
} => {
|
||||||
id: this_id,
|
assert_eq!(this_session, session);
|
||||||
substrate_key: this_substrate_key,
|
if substrate_key.is_none() {
|
||||||
network_key: this_network_key,
|
substrate_key = Some(this_substrate_key);
|
||||||
} => {
|
network_key = Some(this_network_key.clone());
|
||||||
assert_eq!(this_id, id);
|
|
||||||
if substrate_key.is_none() {
|
|
||||||
substrate_key = Some(this_substrate_key);
|
|
||||||
network_key = Some(this_network_key.clone());
|
|
||||||
}
|
|
||||||
assert_eq!(substrate_key.unwrap(), this_substrate_key);
|
|
||||||
assert_eq!(network_key.as_ref().unwrap(), &this_network_key);
|
|
||||||
}
|
}
|
||||||
_ => panic!("processor didn't return GeneratedKeyPair in response to GenerateKey"),
|
assert_eq!(substrate_key.unwrap(), this_substrate_key);
|
||||||
},
|
assert_eq!(network_key.as_ref().unwrap(), &this_network_key);
|
||||||
)
|
}
|
||||||
|
_ => panic!("processor didn't return GeneratedKeyPair in response to all Participations"),
|
||||||
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
// Confirm the key pair
|
// Confirm the key pair
|
||||||
@@ -132,7 +108,7 @@ pub(crate) async fn key_gen(coordinators: &mut [Coordinator]) -> KeyPair {
|
|||||||
.send_message(CoordinatorMessage::Substrate(
|
.send_message(CoordinatorMessage::Substrate(
|
||||||
messages::substrate::CoordinatorMessage::ConfirmKeyPair {
|
messages::substrate::CoordinatorMessage::ConfirmKeyPair {
|
||||||
context,
|
context,
|
||||||
session: id.session,
|
session,
|
||||||
key_pair: key_pair.clone(),
|
key_pair: key_pair.clone(),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
use ciphersuite::{Ciphersuite, Ristretto};
|
|
||||||
|
|
||||||
use serai_client::primitives::NetworkId;
|
use serai_client::primitives::NetworkId;
|
||||||
|
|
||||||
use dockertest::DockerTest;
|
use dockertest::DockerTest;
|
||||||
@@ -17,18 +15,18 @@ mod send;
|
|||||||
pub(crate) const COORDINATORS: usize = 4;
|
pub(crate) const COORDINATORS: usize = 4;
|
||||||
pub(crate) const THRESHOLD: usize = ((COORDINATORS * 2) / 3) + 1;
|
pub(crate) const THRESHOLD: usize = ((COORDINATORS * 2) / 3) + 1;
|
||||||
|
|
||||||
fn new_test(network: NetworkId) -> (Vec<(Handles, <Ristretto as Ciphersuite>::F)>, DockerTest) {
|
fn new_test(network: NetworkId) -> (Vec<(Handles, ProcessorKeys)>, DockerTest) {
|
||||||
let mut coordinators = vec![];
|
let mut coordinators = vec![];
|
||||||
let mut test = DockerTest::new().with_network(dockertest::Network::Isolated);
|
let mut test = DockerTest::new().with_network(dockertest::Network::Isolated);
|
||||||
let mut eth_handle = None;
|
let mut eth_handle = None;
|
||||||
for _ in 0 .. COORDINATORS {
|
for _ in 0 .. COORDINATORS {
|
||||||
let (handles, coord_key, compositions) = processor_stack(network, eth_handle.clone());
|
let (handles, keys, compositions) = processor_stack(network, eth_handle.clone());
|
||||||
// TODO: Remove this once https://github.com/foundry-rs/foundry/issues/7955
|
// TODO: Remove this once https://github.com/foundry-rs/foundry/issues/7955
|
||||||
// This has all processors share an Ethereum node until we can sync controlled nodes
|
// This has all processors share an Ethereum node until we can sync controlled nodes
|
||||||
if network == NetworkId::Ethereum {
|
if network == NetworkId::Ethereum {
|
||||||
eth_handle = eth_handle.or_else(|| Some(handles.0.clone()));
|
eth_handle = eth_handle.or_else(|| Some(handles.0.clone()));
|
||||||
}
|
}
|
||||||
coordinators.push((handles, coord_key));
|
coordinators.push((handles, keys));
|
||||||
for composition in compositions {
|
for composition in compositions {
|
||||||
test.provide_container(composition);
|
test.provide_container(composition);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ use std::{
|
|||||||
time::{SystemTime, Duration},
|
time::{SystemTime, Duration},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use rand_core::RngCore;
|
||||||
|
|
||||||
use dkg::{Participant, tests::clone_without};
|
use dkg::{Participant, tests::clone_without};
|
||||||
|
|
||||||
use messages::{sign::SignId, SubstrateContext};
|
use messages::{sign::SignId, SubstrateContext};
|
||||||
|
|||||||
Reference in New Issue
Block a user