diff --git a/processor/key-gen/tests/key_gen.rs b/processor/key-gen/tests/key_gen.rs new file mode 100644 index 00000000..3c828e61 --- /dev/null +++ b/processor/key-gen/tests/key_gen.rs @@ -0,0 +1,248 @@ +use zeroize::Zeroizing; + +use rand_core::OsRng; + +use ciphersuite::{ + group::{ff::Field, GroupEncoding}, + Ciphersuite, Ristretto, +}; +use dkg::{Participant, evrf::*}; + +use serai_db::{DbTxn, Db, MemDb}; + +use messages::key_gen::*; +use serai_processor_key_gen::{KeyGen, KeyGenParams}; +use serai_validator_sets_primitives::Session; + +const SESSION: Session = Session(1); + +pub(crate) struct RistrettoKeyGenParams; +impl KeyGenParams for RistrettoKeyGenParams { + const ID: &'static str = "Ristretto"; + + type ExternalNetworkCiphersuite = Ristretto; +} + +#[test] +fn test_valid_participants() { + test_valid_participants_inner::<RistrettoKeyGenParams>(); +} + +#[test] +fn test_some_bad_participants() { + test_some_bad_participants_inner::<RistrettoKeyGenParams>(); +} + +fn test_valid_participants_inner<K: KeyGenParams>() { + let mut dbs = Vec::new(); + let mut substrate_evrf_keys = Vec::new(); + let mut network_evrf_keys = Vec::new(); + let mut evrf_public_keys = vec![]; + let mut key_gens = Vec::new(); + + for _ in 0 .. 5 { + let db = MemDb::new(); + dbs.push(db.clone()); + + let substrate_evrf_key = Zeroizing::new( + <<Ristretto as EvrfCurve>::EmbeddedCurve as Ciphersuite>::F::random(&mut OsRng), + ); + substrate_evrf_keys.push(substrate_evrf_key.clone()); + let network_evrf_key = Zeroizing::new( + <<K::ExternalNetworkCiphersuite as EvrfCurve>::EmbeddedCurve as Ciphersuite>::F::random( + &mut OsRng, + ), + ); + network_evrf_keys.push(network_evrf_key.clone()); + + evrf_public_keys.push(( + (<<Ristretto as EvrfCurve>::EmbeddedCurve as Ciphersuite>::generator() * *substrate_evrf_key) + .to_bytes(), + (<<K::ExternalNetworkCiphersuite as EvrfCurve>::EmbeddedCurve as Ciphersuite>::generator() * + *network_evrf_key) + .to_bytes() + .as_ref() + .to_vec(), + )); + key_gens.push(KeyGen::<K>::new(substrate_evrf_key.clone(), network_evrf_key.clone())); + } + + let mut participations = Vec::with_capacity(key_gens.len()); + + for i in 0 .. 5 { + let mut tx = dbs[i].txn(); + + let mut messages = key_gens[i].handle( + &mut tx, + CoordinatorMessage::GenerateKey { + session: SESSION, + threshold: 3, + evrf_public_keys: evrf_public_keys.clone(), + }, + ); + + assert_eq!(messages.len(), 1); + + let Some(ProcessorMessage::Participation { session, participation }) = messages.pop() else { + panic!("KeyGen returned unexpected message.") + }; + + assert_eq!(session, SESSION); + + participations.push(participation); + + tx.commit(); + } + + let mut res = None; + + for i in 0 .. 5 { + let mut tx = dbs[i].txn(); + let key_gen = &mut key_gens[i]; + + for (i, participation) in participations.iter().cloned().enumerate() { + let mut messages = key_gen.handle( + &mut tx, + CoordinatorMessage::Participation { + session: SESSION, + participant: Participant::new(i as u16 + 1).unwrap(), + participation, + }, + ); + + if i != 2 { + assert!(messages.is_empty()); + } else { + let Some(ProcessorMessage::GeneratedKeyPair { session, substrate_key, network_key }) = + messages.pop() + else { + panic!("KeyGen returned unexpected message.") + }; + + assert_eq!(session, SESSION); + + if res.is_none() { + res = Some((substrate_key, network_key.clone())); + } + assert_eq!(res.as_ref().unwrap(), &(substrate_key, network_key)); + } + } + + tx.commit(); + } +} + +fn test_some_bad_participants_inner<K: KeyGenParams>() { + let mut dbs = Vec::new(); + let mut substrate_evrf_keys = Vec::new(); + let mut network_evrf_keys = Vec::new(); + let mut evrf_public_keys = vec![]; + let mut key_gens = Vec::new(); + + for i in 0 .. 5 { + let db = MemDb::new(); + dbs.push(db.clone()); + + let substrate_evrf_key = Zeroizing::new( + <<Ristretto as EvrfCurve>::EmbeddedCurve as Ciphersuite>::F::random(&mut OsRng), + ); + substrate_evrf_keys.push(substrate_evrf_key.clone()); + let network_evrf_key = Zeroizing::new( + <<K::ExternalNetworkCiphersuite as EvrfCurve>::EmbeddedCurve as Ciphersuite>::F::random( + &mut OsRng, + ), + ); + network_evrf_keys.push(network_evrf_key.clone()); + + if i == 0 { + evrf_public_keys.push(([0; 32], [0; 32].to_vec())) + } else { + evrf_public_keys.push( + ( + (<<Ristretto as EvrfCurve>::EmbeddedCurve as Ciphersuite>::generator() * + *substrate_evrf_key) + .to_bytes(), + (<<K::ExternalNetworkCiphersuite as EvrfCurve>::EmbeddedCurve as Ciphersuite>::generator( + ) * *network_evrf_key) + .to_bytes() + .as_ref() + .to_vec(), + ), + ); + } + key_gens.push(KeyGen::<K>::new(substrate_evrf_key.clone(), network_evrf_key.clone())); + } + + let mut participations = Vec::with_capacity(key_gens.len()); + + for i in 0 .. 5 { + if i == 0 { + continue; + } + + let mut tx = dbs[i].txn(); + + let mut messages = key_gens[i].handle( + &mut tx, + CoordinatorMessage::GenerateKey { + session: SESSION, + threshold: 3, + evrf_public_keys: evrf_public_keys.clone(), + }, + ); + + assert_eq!(messages.len(), 3); + + let ProcessorMessage::Blame { session, participant } = &messages[0] else { + panic!("KeyGen returned unexpected message.") + }; + assert_eq!((session, participant), (&SESSION, &Participant::new(1).unwrap())); + + let Some(ProcessorMessage::Participation { session, participation }) = messages.pop() else { + panic!("KeyGen returned unexpected message.") + }; + + assert_eq!(session, SESSION); + + participations.push(participation); + + tx.commit(); + } + + let mut res = None; + + for i in 0 .. 5 { + let mut tx = dbs[i].txn(); + let key_gen = &mut key_gens[i]; + + for (i, participation) in participations.iter().cloned().enumerate() { + let mut messages = key_gen.handle( + &mut tx, + CoordinatorMessage::Participation { + session: SESSION, + participant: Participant::new(i as u16 + 1).unwrap(), + participation, + }, + ); + + if i != 2 { + assert!(messages.is_empty()); + } else { + let Some(ProcessorMessage::GeneratedKeyPair { session, substrate_key, network_key }) = + messages.pop() + else { + panic!("KeyGen returned unexpected message.") + }; + + assert_eq!(session, SESSION); + + if res.is_none() { + res = Some((substrate_key, network_key.clone())); + } + assert_eq!(res.as_ref().unwrap(), &(substrate_key, network_key)); + } + } + + tx.commit(); + } +}