Properly route attempt around DkgConfirmer

This commit is contained in:
Luke Parker 2023-09-01 00:16:43 -04:00
parent fa8ff62b09
commit 3f3f6b2d0c
No known key found for this signature in database
3 changed files with 28 additions and 23 deletions

View file

@ -480,11 +480,10 @@ pub async fn handle_processors<D: Db, Pro: Processors, P: P2p>(
loop { loop {
// TODO: Dispatch this message to a task dedicated to handling this processor, preventing one // TODO: Dispatch this message to a task dedicated to handling this processor, preventing one
// processor from holding up all the others // processor from holding up all the others. This would require a peek method be added to the
// In order to do so, we need to locally track if a message was handled, even if its ID is // message-queue (to view multiple future messages at once)
// greater than what the message-queue tracks as ack'd // TODO: Do we handle having handled a message, by DB, yet having rebooted before `ack`ing it?
// TODO: Shouldn't the processor also perform such checks? That its ack doesn't exceed the // Does the processor?
// queue's?
let msg = processors.recv().await; let msg = processors.recv().await;
// TODO2: This is slow, and only works as long as a network only has a single Tributary // TODO2: This is slow, and only works as long as a network only has a single Tributary
@ -511,12 +510,7 @@ pub async fn handle_processors<D: Db, Pro: Processors, P: P2p>(
} }
key_gen::ProcessorMessage::Shares { id, mut shares } => { key_gen::ProcessorMessage::Shares { id, mut shares } => {
// Create a MuSig-based machine to inform Substrate of this key generation // Create a MuSig-based machine to inform Substrate of this key generation
// DkgConfirmer has a TODO noting it's only secure for a single usage, yet this ensures let nonces = crate::tributary::dkg_confirmation_nonces(&key, &spec, id.attempt);
// the TODO is resolved before unsafe usage
if id.attempt != 0 {
panic!("attempt wasn't 0");
}
let nonces = crate::tributary::dkg_confirmation_nonces(&key, &spec);
let mut tx_shares = Vec::with_capacity(shares.len()); let mut tx_shares = Vec::with_capacity(shares.len());
for i in 1 ..= spec.n() { for i in 1 ..= spec.n() {
@ -549,6 +543,7 @@ pub async fn handle_processors<D: Db, Pro: Processors, P: P2p>(
&key, &key,
&spec, &spec,
&(Public(substrate_key), network_key.try_into().unwrap()), &(Public(substrate_key), network_key.try_into().unwrap()),
id.attempt,
); );
txn.commit(); txn.commit();

View file

@ -172,7 +172,7 @@ async fn dkg_test() {
let mut tx = Transaction::DkgShares { let mut tx = Transaction::DkgShares {
attempt, attempt,
shares, shares,
confirmation_nonces: crate::tributary::dkg_confirmation_nonces(key, &spec), confirmation_nonces: crate::tributary::dkg_confirmation_nonces(key, &spec, 0),
signed: Transaction::empty_signed(), signed: Transaction::empty_signed(),
}; };
tx.sign(&mut OsRng, spec.genesis(), key, 1); tx.sign(&mut OsRng, spec.genesis(), key, 1);
@ -287,7 +287,7 @@ async fn dkg_test() {
// albeit poor // albeit poor
let mut txn = scanner_db.0.txn(); let mut txn = scanner_db.0.txn();
let share = let share =
crate::tributary::generated_key_pair::<MemDb>(&mut txn, key, &spec, &key_pair).unwrap(); crate::tributary::generated_key_pair::<MemDb>(&mut txn, key, &spec, &key_pair, 0).unwrap();
txn.commit(); txn.commit();
let mut tx = Transaction::DkgConfirmed(attempt, share, Transaction::empty_signed()); let mut tx = Transaction::DkgConfirmed(attempt, share, Transaction::empty_signed());

View file

@ -48,6 +48,7 @@ impl DkgConfirmer {
fn preprocess_internal( fn preprocess_internal(
spec: &TributarySpec, spec: &TributarySpec,
key: &Zeroizing<<Ristretto as Ciphersuite>::F>, key: &Zeroizing<<Ristretto as Ciphersuite>::F>,
attempt: u32,
) -> (AlgorithmSignMachine<Ristretto, Schnorrkel>, [u8; 64]) { ) -> (AlgorithmSignMachine<Ristretto, Schnorrkel>, [u8; 64]) {
// TODO: Does Substrate already have a validator-uniqueness check? // TODO: Does Substrate already have a validator-uniqueness check?
let validators = spec.validators().iter().map(|val| val.0).collect::<Vec<_>>(); let validators = spec.validators().iter().map(|val| val.0).collect::<Vec<_>>();
@ -57,7 +58,7 @@ impl DkgConfirmer {
let mut entropy_transcript = RecommendedTranscript::new(b"DkgConfirmer Entropy"); let mut entropy_transcript = RecommendedTranscript::new(b"DkgConfirmer Entropy");
entropy_transcript.append_message(b"spec", spec.serialize()); entropy_transcript.append_message(b"spec", spec.serialize());
entropy_transcript.append_message(b"key", Zeroizing::new(key.to_bytes())); entropy_transcript.append_message(b"key", Zeroizing::new(key.to_bytes()));
// TODO: This is incredibly insecure unless message-bound (or bound via the attempt) entropy_transcript.append_message(b"attempt", attempt.to_le_bytes());
Zeroizing::new(entropy_transcript).rng_seed(b"preprocess") Zeroizing::new(entropy_transcript).rng_seed(b"preprocess")
}); });
let (machine, preprocess) = AlgorithmMachine::new( let (machine, preprocess) = AlgorithmMachine::new(
@ -71,17 +72,22 @@ impl DkgConfirmer {
(machine, preprocess.serialize().try_into().unwrap()) (machine, preprocess.serialize().try_into().unwrap())
} }
// Get the preprocess for this confirmation. // Get the preprocess for this confirmation.
fn preprocess(spec: &TributarySpec, key: &Zeroizing<<Ristretto as Ciphersuite>::F>) -> [u8; 64] { fn preprocess(
Self::preprocess_internal(spec, key).1 spec: &TributarySpec,
key: &Zeroizing<<Ristretto as Ciphersuite>::F>,
attempt: u32,
) -> [u8; 64] {
Self::preprocess_internal(spec, key, attempt).1
} }
fn share_internal( fn share_internal(
spec: &TributarySpec, spec: &TributarySpec,
key: &Zeroizing<<Ristretto as Ciphersuite>::F>, key: &Zeroizing<<Ristretto as Ciphersuite>::F>,
attempt: u32,
preprocesses: HashMap<Participant, Vec<u8>>, preprocesses: HashMap<Participant, Vec<u8>>,
key_pair: &KeyPair, key_pair: &KeyPair,
) -> Result<(AlgorithmSignatureMachine<Ristretto, Schnorrkel>, [u8; 32]), Participant> { ) -> Result<(AlgorithmSignatureMachine<Ristretto, Schnorrkel>, [u8; 32]), Participant> {
let machine = Self::preprocess_internal(spec, key).0; let machine = Self::preprocess_internal(spec, key, attempt).0;
let preprocesses = preprocesses let preprocesses = preprocesses
.into_iter() .into_iter()
.map(|(p, preprocess)| { .map(|(p, preprocess)| {
@ -109,20 +115,22 @@ impl DkgConfirmer {
fn share( fn share(
spec: &TributarySpec, spec: &TributarySpec,
key: &Zeroizing<<Ristretto as Ciphersuite>::F>, key: &Zeroizing<<Ristretto as Ciphersuite>::F>,
attempt: u32,
preprocesses: HashMap<Participant, Vec<u8>>, preprocesses: HashMap<Participant, Vec<u8>>,
key_pair: &KeyPair, key_pair: &KeyPair,
) -> Result<[u8; 32], Participant> { ) -> Result<[u8; 32], Participant> {
Self::share_internal(spec, key, preprocesses, key_pair).map(|(_, share)| share) Self::share_internal(spec, key, attempt, preprocesses, key_pair).map(|(_, share)| share)
} }
fn complete( fn complete(
spec: &TributarySpec, spec: &TributarySpec,
key: &Zeroizing<<Ristretto as Ciphersuite>::F>, key: &Zeroizing<<Ristretto as Ciphersuite>::F>,
attempt: u32,
preprocesses: HashMap<Participant, Vec<u8>>, preprocesses: HashMap<Participant, Vec<u8>>,
key_pair: &KeyPair, key_pair: &KeyPair,
shares: HashMap<Participant, Vec<u8>>, shares: HashMap<Participant, Vec<u8>>,
) -> Result<[u8; 64], Participant> { ) -> Result<[u8; 64], Participant> {
let machine = Self::share_internal(spec, key, preprocesses, key_pair) let machine = Self::share_internal(spec, key, attempt, preprocesses, key_pair)
.expect("trying to complete a machine which failed to preprocess") .expect("trying to complete a machine which failed to preprocess")
.0; .0;
@ -193,8 +201,9 @@ fn read_known_to_exist_data<D: Db, G: Get>(
pub fn dkg_confirmation_nonces( pub fn dkg_confirmation_nonces(
key: &Zeroizing<<Ristretto as Ciphersuite>::F>, key: &Zeroizing<<Ristretto as Ciphersuite>::F>,
spec: &TributarySpec, spec: &TributarySpec,
attempt: u32,
) -> [u8; 64] { ) -> [u8; 64] {
DkgConfirmer::preprocess(spec, key) DkgConfirmer::preprocess(spec, key, attempt)
} }
#[allow(clippy::needless_pass_by_ref_mut)] #[allow(clippy::needless_pass_by_ref_mut)]
@ -203,10 +212,10 @@ pub fn generated_key_pair<D: Db>(
key: &Zeroizing<<Ristretto as Ciphersuite>::F>, key: &Zeroizing<<Ristretto as Ciphersuite>::F>,
spec: &TributarySpec, spec: &TributarySpec,
key_pair: &KeyPair, key_pair: &KeyPair,
attempt: u32,
) -> Result<[u8; 32], Participant> { ) -> Result<[u8; 32], Participant> {
TributaryDb::<D>::save_currently_completing_key_pair(txn, spec.genesis(), key_pair); TributaryDb::<D>::save_currently_completing_key_pair(txn, spec.genesis(), key_pair);
let attempt = 0; // TODO
let Some(preprocesses) = read_known_to_exist_data::<D, _>( let Some(preprocesses) = read_known_to_exist_data::<D, _>(
txn, txn,
spec, spec,
@ -220,7 +229,7 @@ pub fn generated_key_pair<D: Db>(
) else { ) else {
panic!("wasn't a participant in confirming a key pair"); panic!("wasn't a participant in confirming a key pair");
}; };
DkgConfirmer::share(spec, key, preprocesses, key_pair) DkgConfirmer::share(spec, key, attempt, preprocesses, key_pair)
} }
#[allow(clippy::too_many_arguments)] // TODO #[allow(clippy::too_many_arguments)] // TODO
@ -430,7 +439,8 @@ pub async fn handle_application_tx<
"(including us) fires DkgConfirmed, yet no confirming key pair" "(including us) fires DkgConfirmed, yet no confirming key pair"
) )
}); });
let Ok(sig) = DkgConfirmer::complete(spec, key, preprocesses, &key_pair, shares) else { let Ok(sig) = DkgConfirmer::complete(spec, key, attempt, preprocesses, &key_pair, shares)
else {
// TODO: Full slash // TODO: Full slash
todo!(); todo!();
}; };