mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-13 06:29:25 +00:00
Compare commits
6 Commits
2ae2883106
...
8c50a31633
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c50a31633 | ||
|
|
d943e037e5 | ||
|
|
3042697243 | ||
|
|
8de696f169 | ||
|
|
b8912e4b7b | ||
|
|
89fc88b283 |
6
Cargo.lock
generated
6
Cargo.lock
generated
@@ -8140,10 +8140,12 @@ dependencies = [
|
|||||||
"ciphersuite",
|
"ciphersuite",
|
||||||
"dkg",
|
"dkg",
|
||||||
"dockertest",
|
"dockertest",
|
||||||
|
"embedwards25519",
|
||||||
"hex",
|
"hex",
|
||||||
"parity-scale-codec",
|
"parity-scale-codec",
|
||||||
"rand_core",
|
"rand_core",
|
||||||
"schnorrkel",
|
"schnorrkel",
|
||||||
|
"secq256k1",
|
||||||
"serai-client",
|
"serai-client",
|
||||||
"serai-docker-tests",
|
"serai-docker-tests",
|
||||||
"serai-message-queue",
|
"serai-message-queue",
|
||||||
@@ -8404,11 +8406,13 @@ name = "serai-orchestrator"
|
|||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ciphersuite",
|
"ciphersuite",
|
||||||
|
"embedwards25519",
|
||||||
"flexible-transcript",
|
"flexible-transcript",
|
||||||
"hex",
|
"hex",
|
||||||
"home",
|
"home",
|
||||||
"rand_chacha",
|
"rand_chacha",
|
||||||
"rand_core",
|
"rand_core",
|
||||||
|
"secq256k1",
|
||||||
"zalloc",
|
"zalloc",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
@@ -8418,6 +8422,7 @@ name = "serai-primitives"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"borsh",
|
"borsh",
|
||||||
|
"ciphersuite",
|
||||||
"frame-support",
|
"frame-support",
|
||||||
"parity-scale-codec",
|
"parity-scale-codec",
|
||||||
"rand_core",
|
"rand_core",
|
||||||
@@ -8602,7 +8607,6 @@ dependencies = [
|
|||||||
"bitvec",
|
"bitvec",
|
||||||
"frame-support",
|
"frame-support",
|
||||||
"frame-system",
|
"frame-system",
|
||||||
"hashbrown 0.14.5",
|
|
||||||
"pallet-babe",
|
"pallet-babe",
|
||||||
"pallet-grandpa",
|
"pallet-grandpa",
|
||||||
"parity-scale-codec",
|
"parity-scale-codec",
|
||||||
|
|||||||
@@ -359,10 +359,7 @@ impl<
|
|||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
Accumulation::Ready(DataSet::NotParticipating) => {
|
Accumulation::Ready(DataSet::NotParticipating) | Accumulation::NotReady => {}
|
||||||
panic!("wasn't a participant in DKG confirmination shares")
|
|
||||||
}
|
|
||||||
Accumulation::NotReady => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -161,6 +161,7 @@ fn polynomial<F: PrimeField + Zeroize>(
|
|||||||
share
|
share
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
fn share_verification_statements<C: Ciphersuite>(
|
fn share_verification_statements<C: Ciphersuite>(
|
||||||
rng: &mut (impl RngCore + CryptoRng),
|
rng: &mut (impl RngCore + CryptoRng),
|
||||||
commitments: &[C::G],
|
commitments: &[C::G],
|
||||||
@@ -234,6 +235,7 @@ pub struct EvrfDkg<C: EvrfCurve> {
|
|||||||
evrf_public_keys: Vec<<C::EmbeddedCurve as Ciphersuite>::G>,
|
evrf_public_keys: Vec<<C::EmbeddedCurve as Ciphersuite>::G>,
|
||||||
group_key: C::G,
|
group_key: C::G,
|
||||||
verification_shares: HashMap<Participant, C::G>,
|
verification_shares: HashMap<Participant, C::G>,
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
encrypted_secret_shares:
|
encrypted_secret_shares:
|
||||||
HashMap<Participant, HashMap<Participant, ([<C::EmbeddedCurve as Ciphersuite>::G; 2], C::F)>>,
|
HashMap<Participant, HashMap<Participant, ([<C::EmbeddedCurve as Ciphersuite>::G; 2], C::F)>>,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ pub fn musig_key<C: Ciphersuite>(context: &[u8], keys: &[C::G]) -> Result<C::G,
|
|||||||
let transcript = binding_factor_transcript::<C>(context, keys)?;
|
let transcript = binding_factor_transcript::<C>(context, keys)?;
|
||||||
let mut res = C::G::identity();
|
let mut res = C::G::identity();
|
||||||
for i in 1 ..= keys_len {
|
for i in 1 ..= keys_len {
|
||||||
|
// TODO: Calculate this with a multiexp
|
||||||
res += keys[usize::from(i - 1)] * binding_factor::<C>(transcript.clone(), i);
|
res += keys[usize::from(i - 1)] * binding_factor::<C>(transcript.clone(), i);
|
||||||
}
|
}
|
||||||
Ok(res)
|
Ok(res)
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ rand_chacha = { version = "0.3", default-features = false, features = ["std"] }
|
|||||||
|
|
||||||
transcript = { package = "flexible-transcript", path = "../crypto/transcript", default-features = false, features = ["std", "recommended"] }
|
transcript = { package = "flexible-transcript", path = "../crypto/transcript", default-features = false, features = ["std", "recommended"] }
|
||||||
ciphersuite = { path = "../crypto/ciphersuite", default-features = false, features = ["std", "ristretto"] }
|
ciphersuite = { path = "../crypto/ciphersuite", default-features = false, features = ["std", "ristretto"] }
|
||||||
|
embedwards25519 = { path = "../crypto/evrf/embedwards25519" }
|
||||||
|
secq256k1 = { path = "../crypto/evrf/secq256k1" }
|
||||||
|
|
||||||
zalloc = { path = "../common/zalloc" }
|
zalloc = { path = "../common/zalloc" }
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ use ciphersuite::{
|
|||||||
},
|
},
|
||||||
Ciphersuite, Ristretto,
|
Ciphersuite, Ristretto,
|
||||||
};
|
};
|
||||||
|
use embedwards25519::Embedwards25519;
|
||||||
|
use secq256k1::Secq256k1;
|
||||||
|
|
||||||
mod mimalloc;
|
mod mimalloc;
|
||||||
use mimalloc::mimalloc;
|
use mimalloc::mimalloc;
|
||||||
@@ -267,6 +269,55 @@ fn infrastructure_keys(network: Network) -> InfrastructureKeys {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct EmbeddedCurveKeys {
|
||||||
|
embedwards25519: (Zeroizing<Vec<u8>>, Vec<u8>),
|
||||||
|
secq256k1: (Zeroizing<Vec<u8>>, Vec<u8>),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn embedded_curve_keys(network: Network) -> EmbeddedCurveKeys {
|
||||||
|
// Generate entropy for the embedded curve keys
|
||||||
|
|
||||||
|
let entropy = {
|
||||||
|
let path = home::home_dir()
|
||||||
|
.unwrap()
|
||||||
|
.join(".serai")
|
||||||
|
.join(network.label())
|
||||||
|
.join("embedded_curve_keys_entropy");
|
||||||
|
// Check if there's existing entropy
|
||||||
|
if let Ok(entropy) = fs::read(&path).map(Zeroizing::new) {
|
||||||
|
assert_eq!(entropy.len(), 32, "entropy saved to disk wasn't 32 bytes");
|
||||||
|
let mut res = Zeroizing::new([0; 32]);
|
||||||
|
res.copy_from_slice(entropy.as_ref());
|
||||||
|
res
|
||||||
|
} else {
|
||||||
|
// If there isn't, generate fresh entropy
|
||||||
|
let mut res = Zeroizing::new([0; 32]);
|
||||||
|
OsRng.fill_bytes(res.as_mut());
|
||||||
|
fs::write(&path, &res).unwrap();
|
||||||
|
res
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut transcript =
|
||||||
|
RecommendedTranscript::new(b"Serai Orchestrator Embedded Curve Keys Transcript");
|
||||||
|
transcript.append_message(b"network", network.label().as_bytes());
|
||||||
|
transcript.append_message(b"entropy", entropy);
|
||||||
|
let mut rng = ChaCha20Rng::from_seed(transcript.rng_seed(b"embedded_curve_keys"));
|
||||||
|
|
||||||
|
EmbeddedCurveKeys {
|
||||||
|
embedwards25519: {
|
||||||
|
let key = Zeroizing::new(<Embedwards25519 as Ciphersuite>::F::random(&mut rng));
|
||||||
|
let pub_key = Embedwards25519::generator() * key.deref();
|
||||||
|
(Zeroizing::new(key.to_repr().as_slice().to_vec()), pub_key.to_bytes().to_vec())
|
||||||
|
},
|
||||||
|
secq256k1: {
|
||||||
|
let key = Zeroizing::new(<Secq256k1 as Ciphersuite>::F::random(&mut rng));
|
||||||
|
let pub_key = Secq256k1::generator() * key.deref();
|
||||||
|
(Zeroizing::new(key.to_repr().as_slice().to_vec()), pub_key.to_bytes().to_vec())
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn dockerfiles(network: Network) {
|
fn dockerfiles(network: Network) {
|
||||||
let orchestration_path = orchestration_path(network);
|
let orchestration_path = orchestration_path(network);
|
||||||
|
|
||||||
@@ -294,18 +345,15 @@ fn dockerfiles(network: Network) {
|
|||||||
monero_key.1,
|
monero_key.1,
|
||||||
);
|
);
|
||||||
|
|
||||||
let new_entropy = || {
|
let embedded_curve_keys = embedded_curve_keys(network);
|
||||||
let mut res = Zeroizing::new([0; 32]);
|
|
||||||
OsRng.fill_bytes(res.as_mut());
|
|
||||||
res
|
|
||||||
};
|
|
||||||
processor(
|
processor(
|
||||||
&orchestration_path,
|
&orchestration_path,
|
||||||
network,
|
network,
|
||||||
"bitcoin",
|
"bitcoin",
|
||||||
coordinator_key.1,
|
coordinator_key.1,
|
||||||
bitcoin_key.0,
|
bitcoin_key.0,
|
||||||
new_entropy(),
|
embedded_curve_keys.embedwards25519.0.clone(),
|
||||||
|
embedded_curve_keys.secq256k1.0.clone(),
|
||||||
);
|
);
|
||||||
processor(
|
processor(
|
||||||
&orchestration_path,
|
&orchestration_path,
|
||||||
@@ -313,9 +361,18 @@ fn dockerfiles(network: Network) {
|
|||||||
"ethereum",
|
"ethereum",
|
||||||
coordinator_key.1,
|
coordinator_key.1,
|
||||||
ethereum_key.0,
|
ethereum_key.0,
|
||||||
new_entropy(),
|
embedded_curve_keys.embedwards25519.0.clone(),
|
||||||
|
embedded_curve_keys.secq256k1.0.clone(),
|
||||||
|
);
|
||||||
|
processor(
|
||||||
|
&orchestration_path,
|
||||||
|
network,
|
||||||
|
"monero",
|
||||||
|
coordinator_key.1,
|
||||||
|
monero_key.0,
|
||||||
|
embedded_curve_keys.embedwards25519.0.clone(),
|
||||||
|
embedded_curve_keys.embedwards25519.0.clone(),
|
||||||
);
|
);
|
||||||
processor(&orchestration_path, network, "monero", coordinator_key.1, monero_key.0, new_entropy());
|
|
||||||
|
|
||||||
let serai_key = {
|
let serai_key = {
|
||||||
let serai_key = Zeroizing::new(
|
let serai_key = Zeroizing::new(
|
||||||
@@ -346,6 +403,7 @@ fn key_gen(network: Network) {
|
|||||||
let _ = fs::create_dir_all(&serai_dir);
|
let _ = fs::create_dir_all(&serai_dir);
|
||||||
fs::write(key_file, key.to_repr()).expect("couldn't write key");
|
fs::write(key_file, key.to_repr()).expect("couldn't write key");
|
||||||
|
|
||||||
|
// TODO: Move embedded curve key gen here, and print them
|
||||||
println!(
|
println!(
|
||||||
"Public Key: {}",
|
"Public Key: {}",
|
||||||
hex::encode((<Ristretto as Ciphersuite>::generator() * key).to_bytes())
|
hex::encode((<Ristretto as Ciphersuite>::generator() * key).to_bytes())
|
||||||
|
|||||||
@@ -12,8 +12,9 @@ pub fn processor(
|
|||||||
network: Network,
|
network: Network,
|
||||||
coin: &'static str,
|
coin: &'static str,
|
||||||
_coordinator_key: <Ristretto as Ciphersuite>::G,
|
_coordinator_key: <Ristretto as Ciphersuite>::G,
|
||||||
coin_key: Zeroizing<<Ristretto as Ciphersuite>::F>,
|
processor_key: Zeroizing<<Ristretto as Ciphersuite>::F>,
|
||||||
entropy: Zeroizing<[u8; 32]>,
|
substrate_evrf_key: Zeroizing<Vec<u8>>,
|
||||||
|
network_evrf_key: Zeroizing<Vec<u8>>,
|
||||||
) {
|
) {
|
||||||
let setup = mimalloc(Os::Debian).to_string() +
|
let setup = mimalloc(Os::Debian).to_string() +
|
||||||
&build_serai_service(
|
&build_serai_service(
|
||||||
@@ -53,8 +54,9 @@ RUN apt install -y ca-certificates
|
|||||||
|
|
||||||
let mut env_vars = vec![
|
let mut env_vars = vec![
|
||||||
("MESSAGE_QUEUE_RPC", format!("serai-{}-message-queue", network.label())),
|
("MESSAGE_QUEUE_RPC", format!("serai-{}-message-queue", network.label())),
|
||||||
("MESSAGE_QUEUE_KEY", hex::encode(coin_key.to_repr())),
|
("MESSAGE_QUEUE_KEY", hex::encode(processor_key.to_repr())),
|
||||||
("ENTROPY", hex::encode(entropy.as_ref())),
|
("SUBSTRATE_EVRF_KEY", hex::encode(substrate_evrf_key)),
|
||||||
|
("NETWORK_EVRF_KEY", hex::encode(network_evrf_key)),
|
||||||
("NETWORK", coin.to_string()),
|
("NETWORK", coin.to_string()),
|
||||||
("NETWORK_RPC_LOGIN", format!("{RPC_USER}:{RPC_PASS}")),
|
("NETWORK_RPC_LOGIN", format!("{RPC_USER}:{RPC_PASS}")),
|
||||||
("NETWORK_RPC_HOSTNAME", hostname),
|
("NETWORK_RPC_HOSTNAME", hostname),
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ use zeroize::Zeroizing;
|
|||||||
use rand_core::OsRng;
|
use rand_core::OsRng;
|
||||||
|
|
||||||
use sp_core::{
|
use sp_core::{
|
||||||
|
ConstU32,
|
||||||
|
bounded_vec::BoundedVec,
|
||||||
sr25519::{Pair, Signature},
|
sr25519::{Pair, Signature},
|
||||||
Pair as PairTrait,
|
Pair as PairTrait,
|
||||||
};
|
};
|
||||||
@@ -14,8 +16,9 @@ use frost::dkg::musig::musig;
|
|||||||
use schnorrkel::Schnorrkel;
|
use schnorrkel::Schnorrkel;
|
||||||
|
|
||||||
use serai_client::{
|
use serai_client::{
|
||||||
|
primitives::EmbeddedEllipticCurve,
|
||||||
validator_sets::{
|
validator_sets::{
|
||||||
primitives::{ValidatorSet, KeyPair, musig_context, set_keys_message},
|
primitives::{MAX_KEY_LEN, ValidatorSet, KeyPair, musig_context, set_keys_message},
|
||||||
ValidatorSetsEvent,
|
ValidatorSetsEvent,
|
||||||
},
|
},
|
||||||
Amount, Serai, SeraiValidatorSets,
|
Amount, Serai, SeraiValidatorSets,
|
||||||
@@ -58,7 +61,7 @@ pub async fn set_keys(
|
|||||||
let sig = frost::tests::sign_without_caching(
|
let sig = frost::tests::sign_without_caching(
|
||||||
&mut OsRng,
|
&mut OsRng,
|
||||||
frost::tests::algorithm_machines(&mut OsRng, &Schnorrkel::new(b"substrate"), &musig_keys),
|
frost::tests::algorithm_machines(&mut OsRng, &Schnorrkel::new(b"substrate"), &musig_keys),
|
||||||
&set_keys_message(&set, &[], &key_pair),
|
&set_keys_message(&set, &key_pair),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Set the key pair
|
// Set the key pair
|
||||||
@@ -66,8 +69,8 @@ pub async fn set_keys(
|
|||||||
serai,
|
serai,
|
||||||
&SeraiValidatorSets::set_keys(
|
&SeraiValidatorSets::set_keys(
|
||||||
set.network,
|
set.network,
|
||||||
vec![].try_into().unwrap(),
|
|
||||||
key_pair.clone(),
|
key_pair.clone(),
|
||||||
|
vec![1; musig_keys.len()].try_into().unwrap(),
|
||||||
Signature(sig.to_bytes()),
|
Signature(sig.to_bytes()),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use sp_core::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use serai_client::{
|
use serai_client::{
|
||||||
primitives::{NETWORKS, NetworkId, BlockHash, insecure_pair_from_name},
|
primitives::{NETWORKS, EmbeddedEllipticCurve, NetworkId, BlockHash, insecure_pair_from_name},
|
||||||
validator_sets::{
|
validator_sets::{
|
||||||
primitives::{Session, ValidatorSet, KeyPair},
|
primitives::{Session, ValidatorSet, KeyPair},
|
||||||
ValidatorSetsEvent,
|
ValidatorSetsEvent,
|
||||||
@@ -21,7 +21,7 @@ use serai_client::{
|
|||||||
mod common;
|
mod common;
|
||||||
use common::{
|
use common::{
|
||||||
tx::publish_tx,
|
tx::publish_tx,
|
||||||
validator_sets::{allocate_stake, deallocate_stake, set_keys},
|
validator_sets::{set_embedded_elliptic_curve_key, allocate_stake, deallocate_stake, set_keys},
|
||||||
};
|
};
|
||||||
|
|
||||||
fn get_random_key_pair() -> KeyPair {
|
fn get_random_key_pair() -> KeyPair {
|
||||||
@@ -231,9 +231,9 @@ async fn validator_set_rotation() {
|
|||||||
{
|
{
|
||||||
set_embedded_elliptic_curve_key(
|
set_embedded_elliptic_curve_key(
|
||||||
&serai,
|
&serai,
|
||||||
|
&last_participant,
|
||||||
embedded_elliptic_curve,
|
embedded_elliptic_curve,
|
||||||
vec![0; 32].try_into().unwrap(),
|
vec![0; 32].try_into().unwrap(),
|
||||||
&last_participant,
|
|
||||||
i.try_into().unwrap(),
|
i.try_into().unwrap(),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|||||||
@@ -4,10 +4,7 @@ use sp_core::Pair as PairTrait;
|
|||||||
|
|
||||||
use sc_service::ChainType;
|
use sc_service::ChainType;
|
||||||
|
|
||||||
use ciphersuite::{
|
use ciphersuite::{group::GroupEncoding, Ciphersuite};
|
||||||
group::{ff::PrimeField, GroupEncoding},
|
|
||||||
Ciphersuite,
|
|
||||||
};
|
|
||||||
use embedwards25519::Embedwards25519;
|
use embedwards25519::Embedwards25519;
|
||||||
use secq256k1::Secq256k1;
|
use secq256k1::Secq256k1;
|
||||||
|
|
||||||
@@ -23,13 +20,9 @@ fn account_from_name(name: &'static str) -> PublicKey {
|
|||||||
insecure_pair_from_name(name).public()
|
insecure_pair_from_name(name).public()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Panics on names which are too long, or ciphersuites with weirdly encoded scalars
|
fn insecure_arbitrary_public_key_from_name<C: Ciphersuite>(name: &'static str) -> Vec<u8> {
|
||||||
fn insecure_ciphersuite_key_from_name<C: Ciphersuite>(name: &'static str) -> Vec<u8> {
|
let key = insecure_arbitrary_key_from_name::<C>(name);
|
||||||
let mut repr = <C::F as PrimeField>::Repr::default();
|
(C::generator() * key).to_bytes().as_ref().to_vec()
|
||||||
let repr_len = repr.as_ref().len();
|
|
||||||
let start = (repr_len / 2) - (name.len() / 2);
|
|
||||||
repr.as_mut()[start .. (start + name.len())].copy_from_slice(name.as_bytes());
|
|
||||||
(C::generator() * C::F::from_repr(repr).unwrap()).to_bytes().as_ref().to_vec()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wasm_binary() -> Vec<u8> {
|
fn wasm_binary() -> Vec<u8> {
|
||||||
@@ -54,10 +47,10 @@ fn devnet_genesis(
|
|||||||
(
|
(
|
||||||
account_from_name(name),
|
account_from_name(name),
|
||||||
AllEmbeddedEllipticCurveKeysAtGenesis {
|
AllEmbeddedEllipticCurveKeysAtGenesis {
|
||||||
embedwards25519: insecure_ciphersuite_key_from_name::<Embedwards25519>(name)
|
embedwards25519: insecure_arbitrary_public_key_from_name::<Embedwards25519>(name)
|
||||||
.try_into()
|
.try_into()
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
secq256k1: insecure_ciphersuite_key_from_name::<Secq256k1>(name).try_into().unwrap(),
|
secq256k1: insecure_arbitrary_public_key_from_name::<Secq256k1>(name).try_into().unwrap(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ workspace = true
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
zeroize = { version = "^1.5", features = ["derive"], optional = true }
|
zeroize = { version = "^1.5", features = ["derive"], optional = true }
|
||||||
|
|
||||||
|
ciphersuite = { path = "../../crypto/ciphersuite", default-features = false, optional = true }
|
||||||
|
|
||||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
||||||
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
||||||
|
|
||||||
@@ -35,7 +37,7 @@ frame-support = { git = "https://github.com/serai-dex/substrate", default-featur
|
|||||||
rand_core = { version = "0.6", default-features = false, features = ["getrandom"] }
|
rand_core = { version = "0.6", default-features = false, features = ["getrandom"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
std = ["zeroize", "scale/std", "borsh?/std", "serde?/std", "scale-info/std", "sp-core/std", "sp-runtime/std", "frame-support/std"]
|
std = ["zeroize", "ciphersuite/std", "scale/std", "borsh?/std", "serde?/std", "scale-info/std", "sp-core/std", "sp-runtime/std", "frame-support/std"]
|
||||||
borsh = ["dep:borsh"]
|
borsh = ["dep:borsh"]
|
||||||
serde = ["dep:serde"]
|
serde = ["dep:serde"]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
|
|||||||
@@ -90,11 +90,22 @@ impl std::fmt::Display for SeraiAddress {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a Substraate key pair by a name.
|
||||||
|
///
|
||||||
|
/// This should never be considered to have a secure private key. It has effectively no entropy.
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub fn insecure_pair_from_name(name: &str) -> Pair {
|
pub fn insecure_pair_from_name(name: &str) -> Pair {
|
||||||
Pair::from_string(&format!("//{name}"), None).unwrap()
|
Pair::from_string(&format!("//{name}"), None).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a private key for an arbitrary ciphersuite by a name.
|
||||||
|
///
|
||||||
|
/// This key should never be considered a secure private key. It has effectively no entropy.
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
pub fn insecure_arbitrary_key_from_name<C: ciphersuite::Ciphersuite>(name: &str) -> C::F {
|
||||||
|
C::hash_to_F(b"insecure arbitrary key", name.as_bytes())
|
||||||
|
}
|
||||||
|
|
||||||
pub struct AccountLookup;
|
pub struct AccountLookup;
|
||||||
impl Lookup for AccountLookup {
|
impl Lookup for AccountLookup {
|
||||||
type Source = SeraiAddress;
|
type Source = SeraiAddress;
|
||||||
|
|||||||
@@ -12,15 +12,11 @@ rust-version = "1.74"
|
|||||||
all-features = true
|
all-features = true
|
||||||
rustdoc-args = ["--cfg", "docsrs"]
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
||||||
[package.metadata.cargo-machete]
|
|
||||||
ignored = ["scale", "scale-info"]
|
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bitvec = { version = "1", default-features = false, features = ["alloc", "serde"] }
|
bitvec = { version = "1", default-features = false, features = ["alloc", "serde"] }
|
||||||
hashbrown = { version = "0.14", default-features = false, features = ["ahash", "inline-more"] }
|
|
||||||
|
|
||||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive", "bit-vec"] }
|
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive", "bit-vec"] }
|
||||||
scale-info = { version = "2", default-features = false, features = ["derive", "bit-vec"] }
|
scale-info = { version = "2", default-features = false, features = ["derive", "bit-vec"] }
|
||||||
|
|||||||
@@ -1002,7 +1002,7 @@ pub mod pallet {
|
|||||||
EmbeddedEllipticCurve::Secq256k1 => 33,
|
EmbeddedEllipticCurve::Secq256k1 => 33,
|
||||||
};
|
};
|
||||||
if key.len() != expected_len {
|
if key.len() != expected_len {
|
||||||
Err(Error::InvalidEmbeddedEllipticCurveKey)?;
|
Err(Error::<T>::InvalidEmbeddedEllipticCurveKey)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This does allow overwriting an existing key which... is unlikely to be done?
|
// This does allow overwriting an existing key which... is unlikely to be done?
|
||||||
|
|||||||
@@ -24,7 +24,11 @@ zeroize = { version = "1", default-features = false }
|
|||||||
rand_core = { version = "0.6", default-features = false }
|
rand_core = { version = "0.6", default-features = false }
|
||||||
|
|
||||||
blake2 = "0.10"
|
blake2 = "0.10"
|
||||||
|
|
||||||
ciphersuite = { path = "../../crypto/ciphersuite", default-features = false, features = ["ristretto", "secp256k1"] }
|
ciphersuite = { path = "../../crypto/ciphersuite", default-features = false, features = ["ristretto", "secp256k1"] }
|
||||||
|
embedwards25519 = { path = "../../crypto/evrf/embedwards25519" }
|
||||||
|
secq256k1 = { path = "../../crypto/evrf/secq256k1" }
|
||||||
|
|
||||||
schnorrkel = "0.11"
|
schnorrkel = "0.11"
|
||||||
dkg = { path = "../../crypto/dkg", default-features = false, features = ["tests"] }
|
dkg = { path = "../../crypto/dkg", default-features = false, features = ["tests"] }
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ use ciphersuite::{
|
|||||||
group::{ff::PrimeField, GroupEncoding},
|
group::{ff::PrimeField, GroupEncoding},
|
||||||
Ciphersuite, Ristretto,
|
Ciphersuite, Ristretto,
|
||||||
};
|
};
|
||||||
|
use embedwards25519::Embedwards25519;
|
||||||
|
use secq256k1::Secq256k1;
|
||||||
|
|
||||||
use serai_client::primitives::NetworkId;
|
use serai_client::primitives::NetworkId;
|
||||||
|
|
||||||
@@ -118,6 +120,8 @@ pub struct Processor {
|
|||||||
queue_for_sending: MessageQueue,
|
queue_for_sending: MessageQueue,
|
||||||
abort_handle: Option<Arc<AbortHandle>>,
|
abort_handle: Option<Arc<AbortHandle>>,
|
||||||
|
|
||||||
|
evrf_public_keys: ([u8; 32], Vec<u8>),
|
||||||
|
|
||||||
substrate_key: Arc<AsyncMutex<Option<Zeroizing<<Ristretto as Ciphersuite>::F>>>>,
|
substrate_key: Arc<AsyncMutex<Option<Zeroizing<<Ristretto as Ciphersuite>::F>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,7 +135,7 @@ impl Drop for Processor {
|
|||||||
|
|
||||||
impl Processor {
|
impl Processor {
|
||||||
pub async fn new(
|
pub async fn new(
|
||||||
raw_i: u8,
|
name: &'static str,
|
||||||
network: NetworkId,
|
network: NetworkId,
|
||||||
ops: &DockerOperations,
|
ops: &DockerOperations,
|
||||||
handles: Handles,
|
handles: Handles,
|
||||||
@@ -168,6 +172,7 @@ impl Processor {
|
|||||||
|
|
||||||
let (msg_send, msg_recv) = mpsc::unbounded_channel();
|
let (msg_send, msg_recv) = mpsc::unbounded_channel();
|
||||||
|
|
||||||
|
use serai_client::primitives::insecure_arbitrary_key_from_name;
|
||||||
let substrate_key = Arc::new(AsyncMutex::new(None));
|
let substrate_key = Arc::new(AsyncMutex::new(None));
|
||||||
let mut res = Processor {
|
let mut res = Processor {
|
||||||
network,
|
network,
|
||||||
@@ -183,6 +188,28 @@ impl Processor {
|
|||||||
msgs: msg_recv,
|
msgs: msg_recv,
|
||||||
abort_handle: None,
|
abort_handle: None,
|
||||||
|
|
||||||
|
evrf_public_keys: (
|
||||||
|
(Embedwards25519::generator() * insecure_arbitrary_key_from_name::<Embedwards25519>(name))
|
||||||
|
.to_bytes(),
|
||||||
|
match network {
|
||||||
|
NetworkId::Serai => panic!("mock processor for the serai network"),
|
||||||
|
NetworkId::Bitcoin | NetworkId::Ethereum => {
|
||||||
|
let key = (Secq256k1::generator() *
|
||||||
|
insecure_arbitrary_key_from_name::<Secq256k1>(name))
|
||||||
|
.to_bytes();
|
||||||
|
let key: &[u8] = key.as_ref();
|
||||||
|
key.to_vec()
|
||||||
|
}
|
||||||
|
NetworkId::Monero => {
|
||||||
|
let key = (Embedwards25519::generator() *
|
||||||
|
insecure_arbitrary_key_from_name::<Embedwards25519>(name))
|
||||||
|
.to_bytes();
|
||||||
|
let key: &[u8] = key.as_ref();
|
||||||
|
key.to_vec()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
substrate_key: substrate_key.clone(),
|
substrate_key: substrate_key.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -256,10 +283,12 @@ impl Processor {
|
|||||||
if current_cosign.is_none() || (current_cosign.as_ref().unwrap().block != block) {
|
if current_cosign.is_none() || (current_cosign.as_ref().unwrap().block != block) {
|
||||||
*current_cosign = Some(new_cosign);
|
*current_cosign = Some(new_cosign);
|
||||||
}
|
}
|
||||||
|
let mut preprocess = [0; 64];
|
||||||
|
preprocess[.. name.len()].copy_from_slice(name.as_ref());
|
||||||
send_message(
|
send_message(
|
||||||
messages::coordinator::ProcessorMessage::CosignPreprocess {
|
messages::coordinator::ProcessorMessage::CosignPreprocess {
|
||||||
id: id.clone(),
|
id: id.clone(),
|
||||||
preprocesses: vec![[raw_i; 64]],
|
preprocesses: vec![preprocess],
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
)
|
)
|
||||||
@@ -270,11 +299,10 @@ impl Processor {
|
|||||||
) => {
|
) => {
|
||||||
// TODO: Assert the ID matches CURRENT_COSIGN
|
// TODO: Assert the ID matches CURRENT_COSIGN
|
||||||
// TODO: Verify the received preprocesses
|
// TODO: Verify the received preprocesses
|
||||||
|
let mut share = [0; 32];
|
||||||
|
share[.. name.len()].copy_from_slice(name.as_bytes());
|
||||||
send_message(
|
send_message(
|
||||||
messages::coordinator::ProcessorMessage::SubstrateShare {
|
messages::coordinator::ProcessorMessage::SubstrateShare { id, shares: vec![share] }
|
||||||
id,
|
|
||||||
shares: vec![[raw_i; 32]],
|
|
||||||
}
|
|
||||||
.into(),
|
.into(),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
@@ -327,6 +355,10 @@ impl Processor {
|
|||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn evrf_public_keys(&self) -> ([u8; 32], Vec<u8>) {
|
||||||
|
self.evrf_public_keys.clone()
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn serai(&self) -> Serai {
|
pub async fn serai(&self) -> Serai {
|
||||||
Serai::new(self.serai_rpc.clone()).await.unwrap()
|
Serai::new(self.serai_rpc.clone()).await.unwrap()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
use std::{
|
use std::time::{Duration, SystemTime};
|
||||||
time::{Duration, SystemTime},
|
|
||||||
collections::HashMap,
|
|
||||||
};
|
|
||||||
|
|
||||||
use zeroize::Zeroizing;
|
use zeroize::Zeroizing;
|
||||||
use rand_core::OsRng;
|
use rand_core::OsRng;
|
||||||
@@ -10,14 +7,14 @@ use ciphersuite::{
|
|||||||
group::{ff::Field, GroupEncoding},
|
group::{ff::Field, GroupEncoding},
|
||||||
Ciphersuite, Ristretto, Secp256k1,
|
Ciphersuite, Ristretto, Secp256k1,
|
||||||
};
|
};
|
||||||
use dkg::ThresholdParams;
|
use dkg::Participant;
|
||||||
|
|
||||||
use serai_client::{
|
use serai_client::{
|
||||||
primitives::NetworkId,
|
primitives::NetworkId,
|
||||||
Public,
|
Public,
|
||||||
validator_sets::primitives::{Session, ValidatorSet, KeyPair},
|
validator_sets::primitives::{Session, ValidatorSet, KeyPair},
|
||||||
};
|
};
|
||||||
use messages::{key_gen::KeyGenId, CoordinatorMessage};
|
use messages::CoordinatorMessage;
|
||||||
|
|
||||||
use crate::tests::*;
|
use crate::tests::*;
|
||||||
|
|
||||||
@@ -29,16 +26,27 @@ pub async fn key_gen<C: Ciphersuite>(
|
|||||||
let mut participant_is = vec![];
|
let mut participant_is = vec![];
|
||||||
|
|
||||||
let set = ValidatorSet { session, network: NetworkId::Bitcoin };
|
let set = ValidatorSet { session, network: NetworkId::Bitcoin };
|
||||||
let id = KeyGenId { session: set.session, attempt: 0 };
|
|
||||||
|
|
||||||
|
// This is distinct from the result of evrf_public_keys for each processor, as there'll have some
|
||||||
|
// ordering algorithm on-chain which won't match our ordering
|
||||||
|
let mut evrf_public_keys_as_on_chain = None;
|
||||||
for (i, processor) in processors.iter_mut().enumerate() {
|
for (i, processor) in processors.iter_mut().enumerate() {
|
||||||
let msg = processor.recv_message().await;
|
let msg = processor.recv_message().await;
|
||||||
match &msg {
|
match &msg {
|
||||||
CoordinatorMessage::KeyGen(messages::key_gen::CoordinatorMessage::GenerateKey {
|
CoordinatorMessage::KeyGen(messages::key_gen::CoordinatorMessage::GenerateKey {
|
||||||
params,
|
evrf_public_keys,
|
||||||
..
|
..
|
||||||
}) => {
|
}) => {
|
||||||
participant_is.push(params.i());
|
if evrf_public_keys_as_on_chain.is_none() {
|
||||||
|
evrf_public_keys_as_on_chain = Some(evrf_public_keys.clone());
|
||||||
|
}
|
||||||
|
assert_eq!(evrf_public_keys_as_on_chain.as_ref().unwrap(), evrf_public_keys);
|
||||||
|
let i = evrf_public_keys
|
||||||
|
.iter()
|
||||||
|
.position(|public_keys| *public_keys == processor.evrf_public_keys())
|
||||||
|
.unwrap();
|
||||||
|
let i = Participant::new(1 + u16::try_from(i).unwrap()).unwrap();
|
||||||
|
participant_is.push(i);
|
||||||
}
|
}
|
||||||
_ => panic!("unexpected message: {msg:?}"),
|
_ => panic!("unexpected message: {msg:?}"),
|
||||||
}
|
}
|
||||||
@@ -46,63 +54,40 @@ pub async fn key_gen<C: Ciphersuite>(
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
msg,
|
msg,
|
||||||
CoordinatorMessage::KeyGen(messages::key_gen::CoordinatorMessage::GenerateKey {
|
CoordinatorMessage::KeyGen(messages::key_gen::CoordinatorMessage::GenerateKey {
|
||||||
id,
|
session,
|
||||||
params: ThresholdParams::new(
|
threshold: u16::try_from(((coordinators * 2) / 3) + 1).unwrap(),
|
||||||
u16::try_from(((coordinators * 2) / 3) + 1).unwrap(),
|
evrf_public_keys: evrf_public_keys_as_on_chain.clone().unwrap(),
|
||||||
u16::try_from(coordinators).unwrap(),
|
|
||||||
participant_is[i],
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
shares: 1,
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
processor
|
processor
|
||||||
.send_message(messages::key_gen::ProcessorMessage::Commitments {
|
.send_message(messages::key_gen::ProcessorMessage::Participation {
|
||||||
id,
|
session,
|
||||||
commitments: vec![vec![u8::try_from(u16::from(participant_is[i])).unwrap()]],
|
participation: vec![u8::try_from(u16::from(participant_is[i])).unwrap()],
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
// Sleep so this participation gets included, before moving to the next participation
|
||||||
|
wait_for_tributary().await;
|
||||||
|
wait_for_tributary().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
wait_for_tributary().await;
|
wait_for_tributary().await;
|
||||||
for (i, processor) in processors.iter_mut().enumerate() {
|
for processor in processors.iter_mut() {
|
||||||
let mut commitments = (0 .. u8::try_from(coordinators).unwrap())
|
#[allow(clippy::needless_range_loop)] // This wouldn't improve readability/clarity
|
||||||
.map(|l| {
|
for i in 0 .. coordinators {
|
||||||
(
|
|
||||||
participant_is[usize::from(l)],
|
|
||||||
vec![u8::try_from(u16::from(participant_is[usize::from(l)])).unwrap()],
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect::<HashMap<_, _>>();
|
|
||||||
commitments.remove(&participant_is[i]);
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
processor.recv_message().await,
|
processor.recv_message().await,
|
||||||
CoordinatorMessage::KeyGen(messages::key_gen::CoordinatorMessage::Commitments {
|
CoordinatorMessage::KeyGen(messages::key_gen::CoordinatorMessage::Participation {
|
||||||
id,
|
session,
|
||||||
commitments,
|
participant: participant_is[i],
|
||||||
|
participation: vec![u8::try_from(u16::from(participant_is[i])).unwrap()],
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
}
|
||||||
// Recipient it's for -> (Sender i, Recipient i)
|
|
||||||
let mut shares = (0 .. u8::try_from(coordinators).unwrap())
|
|
||||||
.map(|l| {
|
|
||||||
(
|
|
||||||
participant_is[usize::from(l)],
|
|
||||||
vec![
|
|
||||||
u8::try_from(u16::from(participant_is[i])).unwrap(),
|
|
||||||
u8::try_from(u16::from(participant_is[usize::from(l)])).unwrap(),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect::<HashMap<_, _>>();
|
|
||||||
|
|
||||||
shares.remove(&participant_is[i]);
|
|
||||||
processor
|
|
||||||
.send_message(messages::key_gen::ProcessorMessage::Shares { id, shares: vec![shares] })
|
|
||||||
.await;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Now that we've received all participations, publish the key pair
|
||||||
let substrate_priv_key = Zeroizing::new(<Ristretto as Ciphersuite>::F::random(&mut OsRng));
|
let substrate_priv_key = Zeroizing::new(<Ristretto as Ciphersuite>::F::random(&mut OsRng));
|
||||||
let substrate_key = (<Ristretto as Ciphersuite>::generator() * *substrate_priv_key).to_bytes();
|
let substrate_key = (<Ristretto as Ciphersuite>::generator() * *substrate_priv_key).to_bytes();
|
||||||
|
|
||||||
@@ -112,40 +97,24 @@ pub async fn key_gen<C: Ciphersuite>(
|
|||||||
let serai = processors[0].serai().await;
|
let serai = processors[0].serai().await;
|
||||||
let mut last_serai_block = serai.latest_finalized_block().await.unwrap().number();
|
let mut last_serai_block = serai.latest_finalized_block().await.unwrap().number();
|
||||||
|
|
||||||
wait_for_tributary().await;
|
for processor in processors.iter_mut() {
|
||||||
for (i, processor) in processors.iter_mut().enumerate() {
|
|
||||||
let i = participant_is[i];
|
|
||||||
assert_eq!(
|
|
||||||
processor.recv_message().await,
|
|
||||||
CoordinatorMessage::KeyGen(messages::key_gen::CoordinatorMessage::Shares {
|
|
||||||
id,
|
|
||||||
shares: {
|
|
||||||
let mut shares = (0 .. u8::try_from(coordinators).unwrap())
|
|
||||||
.map(|l| {
|
|
||||||
(
|
|
||||||
participant_is[usize::from(l)],
|
|
||||||
vec![
|
|
||||||
u8::try_from(u16::from(participant_is[usize::from(l)])).unwrap(),
|
|
||||||
u8::try_from(u16::from(i)).unwrap(),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect::<HashMap<_, _>>();
|
|
||||||
shares.remove(&i);
|
|
||||||
vec![shares]
|
|
||||||
},
|
|
||||||
})
|
|
||||||
);
|
|
||||||
processor
|
processor
|
||||||
.send_message(messages::key_gen::ProcessorMessage::GeneratedKeyPair {
|
.send_message(messages::key_gen::ProcessorMessage::GeneratedKeyPair {
|
||||||
id,
|
session,
|
||||||
substrate_key,
|
substrate_key,
|
||||||
network_key: network_key.clone(),
|
network_key: network_key.clone(),
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sleeps for longer since we need to wait for a Substrate block as well
|
// Wait for the Nonces TXs to go around
|
||||||
|
wait_for_tributary().await;
|
||||||
|
// Wait for the Share TXs to go around
|
||||||
|
wait_for_tributary().await;
|
||||||
|
|
||||||
|
// And now we're waiting ro the TX to be published onto Serai
|
||||||
|
|
||||||
|
// We need to wait for a finalized Substrate block as well, so this waites for up to 20 blocks
|
||||||
'outer: for _ in 0 .. 20 {
|
'outer: for _ in 0 .. 20 {
|
||||||
tokio::time::sleep(Duration::from_secs(6)).await;
|
tokio::time::sleep(Duration::from_secs(6)).await;
|
||||||
if std::env::var("GITHUB_CI") == Ok("true".to_string()) {
|
if std::env::var("GITHUB_CI") == Ok("true".to_string()) {
|
||||||
|
|||||||
@@ -41,6 +41,18 @@ impl<F: Send + Future, TB: 'static + Send + Sync + Fn(Vec<Processor>) -> F> Test
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn name(i: usize) -> &'static str {
|
||||||
|
match i {
|
||||||
|
0 => "Alice",
|
||||||
|
1 => "Bob",
|
||||||
|
2 => "Charlie",
|
||||||
|
3 => "Dave",
|
||||||
|
4 => "Eve",
|
||||||
|
5 => "Ferdie",
|
||||||
|
_ => panic!("needed a 7th name for a serai node"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) async fn new_test(test_body: impl TestBody, fast_epoch: bool) {
|
pub(crate) async fn new_test(test_body: impl TestBody, fast_epoch: bool) {
|
||||||
let mut unique_id_lock = UNIQUE_ID.get_or_init(|| Mutex::new(0)).lock().await;
|
let mut unique_id_lock = UNIQUE_ID.get_or_init(|| Mutex::new(0)).lock().await;
|
||||||
|
|
||||||
@@ -50,15 +62,7 @@ pub(crate) async fn new_test(test_body: impl TestBody, fast_epoch: bool) {
|
|||||||
// Spawn one extra coordinator which isn't in-set
|
// Spawn one extra coordinator which isn't in-set
|
||||||
#[allow(clippy::range_plus_one)]
|
#[allow(clippy::range_plus_one)]
|
||||||
for i in 0 .. (COORDINATORS + 1) {
|
for i in 0 .. (COORDINATORS + 1) {
|
||||||
let name = match i {
|
let name = name(i);
|
||||||
0 => "Alice",
|
|
||||||
1 => "Bob",
|
|
||||||
2 => "Charlie",
|
|
||||||
3 => "Dave",
|
|
||||||
4 => "Eve",
|
|
||||||
5 => "Ferdie",
|
|
||||||
_ => panic!("needed a 7th name for a serai node"),
|
|
||||||
};
|
|
||||||
let serai_composition = serai_composition(name, fast_epoch);
|
let serai_composition = serai_composition(name, fast_epoch);
|
||||||
|
|
||||||
let (processor_key, message_queue_keys, message_queue_composition) =
|
let (processor_key, message_queue_keys, message_queue_composition) =
|
||||||
@@ -196,14 +200,7 @@ pub(crate) async fn new_test(test_body: impl TestBody, fast_epoch: bool) {
|
|||||||
let mut processors: Vec<Processor> = vec![];
|
let mut processors: Vec<Processor> = vec![];
|
||||||
for (i, (handles, key)) in coordinators.iter().enumerate() {
|
for (i, (handles, key)) in coordinators.iter().enumerate() {
|
||||||
processors.push(
|
processors.push(
|
||||||
Processor::new(
|
Processor::new(name(i), NetworkId::Bitcoin, &outer_ops, handles.clone(), *key).await,
|
||||||
i.try_into().unwrap(),
|
|
||||||
NetworkId::Bitcoin,
|
|
||||||
&outer_ops,
|
|
||||||
handles.clone(),
|
|
||||||
*key,
|
|
||||||
)
|
|
||||||
.await,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,14 +57,24 @@ pub(crate) async fn new_test(test_body: impl TestBody) {
|
|||||||
let (coord_key, message_queue_keys, message_queue_composition) = message_queue_instance();
|
let (coord_key, message_queue_keys, message_queue_composition) = message_queue_instance();
|
||||||
|
|
||||||
let (bitcoin_composition, bitcoin_port) = network_instance(NetworkId::Bitcoin);
|
let (bitcoin_composition, bitcoin_port) = network_instance(NetworkId::Bitcoin);
|
||||||
let mut bitcoin_processor_composition =
|
let mut bitcoin_processor_composition = processor_instance(
|
||||||
processor_instance(NetworkId::Bitcoin, bitcoin_port, message_queue_keys[&NetworkId::Bitcoin]);
|
name,
|
||||||
|
NetworkId::Bitcoin,
|
||||||
|
bitcoin_port,
|
||||||
|
message_queue_keys[&NetworkId::Bitcoin],
|
||||||
|
)
|
||||||
|
.0;
|
||||||
assert_eq!(bitcoin_processor_composition.len(), 1);
|
assert_eq!(bitcoin_processor_composition.len(), 1);
|
||||||
let bitcoin_processor_composition = bitcoin_processor_composition.swap_remove(0);
|
let bitcoin_processor_composition = bitcoin_processor_composition.swap_remove(0);
|
||||||
|
|
||||||
let (monero_composition, monero_port) = network_instance(NetworkId::Monero);
|
let (monero_composition, monero_port) = network_instance(NetworkId::Monero);
|
||||||
let mut monero_processor_composition =
|
let mut monero_processor_composition = processor_instance(
|
||||||
processor_instance(NetworkId::Monero, monero_port, message_queue_keys[&NetworkId::Monero]);
|
name,
|
||||||
|
NetworkId::Monero,
|
||||||
|
monero_port,
|
||||||
|
message_queue_keys[&NetworkId::Monero],
|
||||||
|
)
|
||||||
|
.0;
|
||||||
assert_eq!(monero_processor_composition.len(), 1);
|
assert_eq!(monero_processor_composition.len(), 1);
|
||||||
let monero_processor_composition = monero_processor_composition.swap_remove(0);
|
let monero_processor_composition = monero_processor_composition.swap_remove(0);
|
||||||
|
|
||||||
|
|||||||
@@ -3,18 +3,14 @@
|
|||||||
use std::sync::{OnceLock, Mutex};
|
use std::sync::{OnceLock, Mutex};
|
||||||
|
|
||||||
use zeroize::Zeroizing;
|
use zeroize::Zeroizing;
|
||||||
use rand_core::OsRng;
|
|
||||||
|
|
||||||
use ciphersuite::{
|
use ciphersuite::{
|
||||||
group::{
|
group::{ff::PrimeField, GroupEncoding},
|
||||||
ff::{Field, PrimeField},
|
|
||||||
GroupEncoding,
|
|
||||||
},
|
|
||||||
Ciphersuite, Secp256k1, Ed25519, Ristretto,
|
Ciphersuite, Secp256k1, Ed25519, Ristretto,
|
||||||
};
|
};
|
||||||
use dkg::evrf::*;
|
use dkg::evrf::*;
|
||||||
|
|
||||||
use serai_client::primitives::NetworkId;
|
use serai_client::primitives::{NetworkId, insecure_arbitrary_key_from_name};
|
||||||
use messages::{ProcessorMessage, CoordinatorMessage};
|
use messages::{ProcessorMessage, CoordinatorMessage};
|
||||||
use serai_message_queue::{Service, Metadata, client::MessageQueue};
|
use serai_message_queue::{Service, Metadata, client::MessageQueue};
|
||||||
|
|
||||||
@@ -39,12 +35,13 @@ pub struct EvrfPublicKeys {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn processor_instance(
|
pub fn processor_instance(
|
||||||
|
name: &str,
|
||||||
network: NetworkId,
|
network: NetworkId,
|
||||||
port: u32,
|
port: u32,
|
||||||
message_queue_key: <Ristretto as Ciphersuite>::F,
|
message_queue_key: <Ristretto as Ciphersuite>::F,
|
||||||
) -> (Vec<TestBodySpecification>, EvrfPublicKeys) {
|
) -> (Vec<TestBodySpecification>, EvrfPublicKeys) {
|
||||||
let substrate_evrf_key =
|
let substrate_evrf_key =
|
||||||
<<Ristretto as EvrfCurve>::EmbeddedCurve as Ciphersuite>::F::random(&mut OsRng);
|
insecure_arbitrary_key_from_name::<<Ristretto as EvrfCurve>::EmbeddedCurve>(name);
|
||||||
let substrate_evrf_pub_key =
|
let substrate_evrf_pub_key =
|
||||||
(<Ristretto as EvrfCurve>::EmbeddedCurve::generator() * substrate_evrf_key).to_bytes();
|
(<Ristretto as EvrfCurve>::EmbeddedCurve::generator() * substrate_evrf_key).to_bytes();
|
||||||
let substrate_evrf_key = substrate_evrf_key.to_repr();
|
let substrate_evrf_key = substrate_evrf_key.to_repr();
|
||||||
@@ -53,13 +50,14 @@ pub fn processor_instance(
|
|||||||
NetworkId::Serai => panic!("starting a processor for Serai"),
|
NetworkId::Serai => panic!("starting a processor for Serai"),
|
||||||
NetworkId::Bitcoin | NetworkId::Ethereum => {
|
NetworkId::Bitcoin | NetworkId::Ethereum => {
|
||||||
let evrf_key =
|
let evrf_key =
|
||||||
<<Secp256k1 as EvrfCurve>::EmbeddedCurve as Ciphersuite>::F::random(&mut OsRng);
|
insecure_arbitrary_key_from_name::<<Secp256k1 as EvrfCurve>::EmbeddedCurve>(name);
|
||||||
let pub_key =
|
let pub_key =
|
||||||
(<Secp256k1 as EvrfCurve>::EmbeddedCurve::generator() * evrf_key).to_bytes().to_vec();
|
(<Secp256k1 as EvrfCurve>::EmbeddedCurve::generator() * evrf_key).to_bytes().to_vec();
|
||||||
(evrf_key.to_repr(), pub_key)
|
(evrf_key.to_repr(), pub_key)
|
||||||
}
|
}
|
||||||
NetworkId::Monero => {
|
NetworkId::Monero => {
|
||||||
let evrf_key = <<Ed25519 as EvrfCurve>::EmbeddedCurve as Ciphersuite>::F::random(&mut OsRng);
|
let evrf_key =
|
||||||
|
insecure_arbitrary_key_from_name::<<Ed25519 as EvrfCurve>::EmbeddedCurve>(name);
|
||||||
let pub_key =
|
let pub_key =
|
||||||
(<Ed25519 as EvrfCurve>::EmbeddedCurve::generator() * evrf_key).to_bytes().to_vec();
|
(<Ed25519 as EvrfCurve>::EmbeddedCurve::generator() * evrf_key).to_bytes().to_vec();
|
||||||
(evrf_key.to_repr(), pub_key)
|
(evrf_key.to_repr(), pub_key)
|
||||||
@@ -120,6 +118,7 @@ pub struct ProcessorKeys {
|
|||||||
|
|
||||||
pub type Handles = (String, String, String, String);
|
pub type Handles = (String, String, String, String);
|
||||||
pub fn processor_stack(
|
pub fn processor_stack(
|
||||||
|
name: &str,
|
||||||
network: NetworkId,
|
network: NetworkId,
|
||||||
network_hostname_override: Option<String>,
|
network_hostname_override: Option<String>,
|
||||||
) -> (Handles, ProcessorKeys, Vec<TestBodySpecification>) {
|
) -> (Handles, ProcessorKeys, Vec<TestBodySpecification>) {
|
||||||
@@ -129,7 +128,7 @@ pub fn processor_stack(
|
|||||||
serai_message_queue_tests::instance();
|
serai_message_queue_tests::instance();
|
||||||
|
|
||||||
let (mut processor_compositions, evrf_keys) =
|
let (mut processor_compositions, evrf_keys) =
|
||||||
processor_instance(network, network_rpc_port, message_queue_keys[&network]);
|
processor_instance(name, 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
|
||||||
// Uses a Mutex as we can't generate a 8-byte random ID without hitting hostname length limits
|
// Uses a Mutex as we can't generate a 8-byte random ID without hitting hostname length limits
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use std::{
|
|||||||
time::{SystemTime, Duration},
|
time::{SystemTime, Duration},
|
||||||
};
|
};
|
||||||
|
|
||||||
use rand_core::RngCore;
|
use rand_core::{RngCore, OsRng};
|
||||||
|
|
||||||
use dkg::{Participant, tests::clone_without};
|
use dkg::{Participant, tests::clone_without};
|
||||||
|
|
||||||
|
|||||||
@@ -19,8 +19,11 @@ 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 i in 0 .. COORDINATORS {
|
||||||
let (handles, keys, compositions) = processor_stack(network, eth_handle.clone());
|
// Uses the counter `i` as this has no relation to any other system, and while Substrate has
|
||||||
|
// hard-coded names for itself, these tests down't spawn any Substrate node
|
||||||
|
let (handles, keys, compositions) =
|
||||||
|
processor_stack(&i.to_string(), 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 {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use std::{
|
|||||||
time::{SystemTime, Duration},
|
time::{SystemTime, Duration},
|
||||||
};
|
};
|
||||||
|
|
||||||
use rand_core::RngCore;
|
use rand_core::{RngCore, OsRng};
|
||||||
|
|
||||||
use dkg::{Participant, tests::clone_without};
|
use dkg::{Participant, tests::clone_without};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user