mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-09 04:39:24 +00:00
Perform MuSig signing of generated keys
This commit is contained in:
@@ -4,12 +4,16 @@ use std::collections::HashMap;
|
||||
use zeroize::Zeroizing;
|
||||
use rand_core::{RngCore, OsRng};
|
||||
|
||||
use ciphersuite::{Ciphersuite, Ristretto};
|
||||
use scale::Decode;
|
||||
|
||||
use ciphersuite::{group::GroupEncoding, Ciphersuite, Ristretto};
|
||||
use frost::Participant;
|
||||
|
||||
use sp_runtime::traits::Verify;
|
||||
|
||||
use tokio::{time::sleep, sync::mpsc};
|
||||
|
||||
use serai_db::MemDb;
|
||||
use serai_db::{DbTxn, Db, MemDb};
|
||||
|
||||
use processor_messages::{
|
||||
key_gen::{self, KeyGenId},
|
||||
@@ -85,6 +89,7 @@ async fn dkg_test() {
|
||||
key,
|
||||
&mpsc::unbounded_channel().0,
|
||||
&processors,
|
||||
|_, _| async { panic!("test tried to publish a new Serai TX in new_processors") },
|
||||
spec,
|
||||
&tributary.reader(),
|
||||
)
|
||||
@@ -108,6 +113,7 @@ async fn dkg_test() {
|
||||
&keys[0],
|
||||
&mpsc::unbounded_channel().0,
|
||||
&processors,
|
||||
|_, _| async { panic!("test tried to publish a new Serai TX after Commitments") },
|
||||
&spec,
|
||||
&tributaries[0].1.reader(),
|
||||
)
|
||||
@@ -160,12 +166,13 @@ async fn dkg_test() {
|
||||
}
|
||||
}
|
||||
|
||||
let mut tx = Transaction::DkgShares(
|
||||
let mut tx = Transaction::DkgShares {
|
||||
attempt,
|
||||
Participant::new((k + 1).try_into().unwrap()).unwrap(),
|
||||
sender_i: Participant::new((k + 1).try_into().unwrap()).unwrap(),
|
||||
shares,
|
||||
Transaction::empty_signed(),
|
||||
);
|
||||
confirmation_nonces: crate::tributary::scanner::dkg_confirmation_nonces(key, &spec),
|
||||
signed: Transaction::empty_signed(),
|
||||
};
|
||||
tx.sign(&mut OsRng, spec.genesis(), key, 1);
|
||||
txs.push(tx);
|
||||
}
|
||||
@@ -184,6 +191,7 @@ async fn dkg_test() {
|
||||
&keys[0],
|
||||
&mpsc::unbounded_channel().0,
|
||||
&processors,
|
||||
|_, _| async { panic!("test tried to publish a new Serai TX after some shares") },
|
||||
&spec,
|
||||
&tributaries[0].1.reader(),
|
||||
)
|
||||
@@ -205,7 +213,7 @@ async fn dkg_test() {
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(l, tx)| {
|
||||
if let Transaction::DkgShares(_, _, shares, _) = tx {
|
||||
if let Transaction::DkgShares { shares, .. } = tx {
|
||||
shares
|
||||
.get(&Participant::new((i + 1).try_into().unwrap()).unwrap())
|
||||
.cloned()
|
||||
@@ -224,6 +232,7 @@ async fn dkg_test() {
|
||||
&keys[0],
|
||||
&mpsc::unbounded_channel().0,
|
||||
&processors,
|
||||
|_, _| async { panic!("test tried to publish a new Serai TX") },
|
||||
&spec,
|
||||
&tributaries[0].1.reader(),
|
||||
)
|
||||
@@ -254,4 +263,99 @@ async fn dkg_test() {
|
||||
assert_eq!(msgs.pop_front().unwrap(), shares_for(i));
|
||||
assert!(msgs.is_empty());
|
||||
}
|
||||
|
||||
// Send DkgConfirmed
|
||||
let mut substrate_key = [0; 32];
|
||||
OsRng.fill_bytes(&mut substrate_key);
|
||||
let mut network_key = vec![0; usize::try_from((OsRng.next_u64() % 32) + 32).unwrap()];
|
||||
OsRng.fill_bytes(&mut network_key);
|
||||
let key_pair = (serai_client::Public(substrate_key), network_key.try_into().unwrap());
|
||||
|
||||
let mut txs = vec![];
|
||||
for (k, key) in keys.iter().enumerate() {
|
||||
let attempt = 0;
|
||||
// This is fine to re-use the one DB as such, due to exactly how this specific call is coded,
|
||||
// albeit poor
|
||||
let mut txn = scanner_db.0.txn();
|
||||
let share =
|
||||
crate::tributary::scanner::generated_key_pair::<MemDb>(&mut txn, key, &spec, &key_pair)
|
||||
.unwrap();
|
||||
txn.commit();
|
||||
|
||||
let mut tx = Transaction::DkgConfirmed(attempt, share, Transaction::empty_signed());
|
||||
tx.sign(&mut OsRng, spec.genesis(), key, 2);
|
||||
txs.push(tx);
|
||||
}
|
||||
let block_before_tx = tributaries[0].1.tip().await;
|
||||
for (i, tx) in txs.iter().enumerate() {
|
||||
assert!(tributaries[i].1.add_transaction(tx.clone()).await);
|
||||
}
|
||||
for tx in txs.iter() {
|
||||
wait_for_tx_inclusion(&tributaries[0].1, block_before_tx, tx.hash()).await;
|
||||
}
|
||||
|
||||
// The scanner should successfully try to publish a transaction with a validly signed signature
|
||||
handle_new_blocks(
|
||||
&mut scanner_db,
|
||||
&keys[0],
|
||||
&mpsc::unbounded_channel().0,
|
||||
&processors,
|
||||
|set, tx| {
|
||||
let spec = spec.clone();
|
||||
let key_pair = key_pair.clone();
|
||||
async move {
|
||||
let tx = tx.0;
|
||||
// Version, Pallet, Call, Network, Key Pair, Signature
|
||||
let expected_len = 1 + 1 + 1 + 1 + 32 + 1 + key_pair.1.len() + 64;
|
||||
// It's length prefixed
|
||||
assert_eq!(tx.len(), 2 + expected_len);
|
||||
let expected_len = u16::try_from(expected_len).unwrap();
|
||||
|
||||
// Check the encoded length
|
||||
let bottom_six = expected_len & 0b111111;
|
||||
let upper_eight = expected_len >> 6;
|
||||
assert_eq!(u8::try_from((bottom_six << 2) | 1).unwrap(), tx[0]);
|
||||
assert_eq!(u8::try_from(upper_eight).unwrap(), tx[1]);
|
||||
|
||||
// Version
|
||||
assert_eq!(tx[2], 4);
|
||||
|
||||
// Pallet
|
||||
// TODO
|
||||
|
||||
// Call
|
||||
type Call = serai_client::runtime::validator_sets::Call<serai_client::runtime::Runtime>;
|
||||
let tx = Call::decode(&mut &tx[4 ..]).unwrap();
|
||||
match tx {
|
||||
Call::set_keys { network, key_pair: set_key_pair, signature } => {
|
||||
assert_eq!(set, spec.set());
|
||||
assert_eq!(set.network, network);
|
||||
assert_eq!(key_pair, set_key_pair);
|
||||
assert!(signature.verify(
|
||||
&*serai_client::validator_sets::primitives::set_keys_message(&set, &key_pair),
|
||||
&serai_client::Public(
|
||||
frost::dkg::musig::musig_key::<Ristretto>(
|
||||
&serai_client::validator_sets::primitives::musig_context(set),
|
||||
&spec
|
||||
.validators()
|
||||
.into_iter()
|
||||
.map(|(validator, _)| validator)
|
||||
.collect::<Vec<_>>()
|
||||
)
|
||||
.unwrap()
|
||||
.to_bytes()
|
||||
),
|
||||
));
|
||||
}
|
||||
_ => panic!("Serai TX wasn't to set_keys"),
|
||||
}
|
||||
}
|
||||
},
|
||||
&spec,
|
||||
&tributaries[0].1.reader(),
|
||||
)
|
||||
.await;
|
||||
{
|
||||
assert!(processors.0.read().await.get(&spec.set().network).unwrap().is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,14 +79,29 @@ fn serialize_transaction() {
|
||||
shares.insert(Participant::new(u16::try_from(i + 1).unwrap()).unwrap(), share);
|
||||
}
|
||||
|
||||
test_read_write(Transaction::DkgShares(
|
||||
random_u32(&mut OsRng),
|
||||
Participant::new(random_u16(&mut OsRng).saturating_add(1)).unwrap(),
|
||||
test_read_write(Transaction::DkgShares {
|
||||
attempt: random_u32(&mut OsRng),
|
||||
sender_i: Participant::new(random_u16(&mut OsRng).saturating_add(1)).unwrap(),
|
||||
shares,
|
||||
random_signed(&mut OsRng),
|
||||
));
|
||||
confirmation_nonces: {
|
||||
let mut nonces = [0; 64];
|
||||
OsRng.fill_bytes(&mut nonces);
|
||||
nonces
|
||||
},
|
||||
signed: random_signed(&mut OsRng),
|
||||
});
|
||||
}
|
||||
|
||||
test_read_write(Transaction::DkgConfirmed(
|
||||
random_u32(&mut OsRng),
|
||||
{
|
||||
let mut share = [0; 32];
|
||||
OsRng.fill_bytes(&mut share);
|
||||
share
|
||||
},
|
||||
random_signed(&mut OsRng),
|
||||
));
|
||||
|
||||
{
|
||||
let mut ext_block = [0; 32];
|
||||
OsRng.fill_bytes(&mut ext_block);
|
||||
|
||||
Reference in New Issue
Block a user