From 1e8a9ec5bd944b965c8106c6ec43b469649d2a4f Mon Sep 17 00:00:00 2001 From: Luke Parker Date: Sun, 18 Aug 2024 22:43:13 -0400 Subject: [PATCH] Smash out the signer Abstract, to be done for the transactions, the batches, the cosigns, the slash reports, everything. It has a minimal API itself, intending to be as clear as possible. --- .github/workflows/tests.yml | 1 + Cargo.lock | 13 + Cargo.toml | 1 + deny.toml | 1 + processor/LICENSE | 2 +- processor/frost-attempt-manager/Cargo.toml | 29 ++ processor/frost-attempt-manager/LICENSE | 15 ++ processor/frost-attempt-manager/README.md | 6 + .../frost-attempt-manager/src/individual.rs | 251 ++++++++++++++++++ processor/frost-attempt-manager/src/lib.rs | 92 +++++++ processor/key-gen/src/lib.rs | 8 +- processor/messages/src/lib.rs | 62 ++--- 12 files changed, 442 insertions(+), 39 deletions(-) create mode 100644 processor/frost-attempt-manager/Cargo.toml create mode 100644 processor/frost-attempt-manager/LICENSE create mode 100644 processor/frost-attempt-manager/README.md create mode 100644 processor/frost-attempt-manager/src/individual.rs create mode 100644 processor/frost-attempt-manager/src/lib.rs diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index fabfaba9..5aa3d234 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -40,6 +40,7 @@ jobs: -p serai-message-queue \ -p serai-processor-messages \ -p serai-processor-key-gen \ + -p serai-processor-frost-attempt-manager \ -p serai-processor \ -p tendermint-machine \ -p tributary-chain \ diff --git a/Cargo.lock b/Cargo.lock index 62952da0..3de56915 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8599,6 +8599,19 @@ dependencies = [ "zeroize", ] +[[package]] +name = "serai-processor-frost-attempt-manager" +version = "0.1.0" +dependencies = [ + "hex", + "log", + "modular-frost", + "rand_core", + "serai-db", + "serai-processor-messages", + "serai-validator-sets-primitives", +] + [[package]] name = "serai-processor-key-gen" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index f0bdd6a8..ddfaf1f2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,6 +71,7 @@ members = [ "processor/messages", "processor/key-gen", + "processor/frost-attempt-manager", "processor", "coordinator/tributary/tendermint", diff --git a/deny.toml b/deny.toml index 0d82cb8a..ea61fcc1 100644 --- a/deny.toml +++ b/deny.toml @@ -47,6 +47,7 @@ exceptions = [ { allow = ["AGPL-3.0"], name = "serai-processor-messages" }, { allow = ["AGPL-3.0"], name = "serai-processor-key-gen" }, + { allow = ["AGPL-3.0"], name = "serai-processor-frost-attempt-manager" }, { allow = ["AGPL-3.0"], name = "serai-processor" }, { allow = ["AGPL-3.0"], name = "tributary-chain" }, diff --git a/processor/LICENSE b/processor/LICENSE index c425427c..41d5a261 100644 --- a/processor/LICENSE +++ b/processor/LICENSE @@ -1,6 +1,6 @@ AGPL-3.0-only license -Copyright (c) 2022-2023 Luke Parker +Copyright (c) 2022-2024 Luke Parker This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License Version 3 as diff --git a/processor/frost-attempt-manager/Cargo.toml b/processor/frost-attempt-manager/Cargo.toml new file mode 100644 index 00000000..7a9abe01 --- /dev/null +++ b/processor/frost-attempt-manager/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "serai-processor-frost-attempt-manager" +version = "0.1.0" +description = "A manager of multiple attempts of FROST signing protocols" +license = "AGPL-3.0-only" +repository = "https://github.com/serai-dex/serai/tree/develop/processor/frost-attempt-manager" +authors = ["Luke Parker "] +keywords = ["frost", "multisig", "threshold"] +edition = "2021" +rust-version = "1.79" + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[lints] +workspace = true + +[dependencies] +rand_core = { version = "0.6", default-features = false, features = ["std", "getrandom"] } + +frost = { package = "modular-frost", path = "../../crypto/frost", version = "^0.8.1", default-features = false } + +serai-validator-sets-primitives = { path = "../../substrate/validator-sets/primitives", default-features = false, features = ["std"] } + +hex = { version = "0.4", default-features = false, features = ["std"] } +log = { version = "0.4", default-features = false, features = ["std"] } +serai-db = { path = "../../common/db" } +messages = { package = "serai-processor-messages", path = "../messages" } diff --git a/processor/frost-attempt-manager/LICENSE b/processor/frost-attempt-manager/LICENSE new file mode 100644 index 00000000..41d5a261 --- /dev/null +++ b/processor/frost-attempt-manager/LICENSE @@ -0,0 +1,15 @@ +AGPL-3.0-only license + +Copyright (c) 2022-2024 Luke Parker + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License Version 3 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . diff --git a/processor/frost-attempt-manager/README.md b/processor/frost-attempt-manager/README.md new file mode 100644 index 00000000..c7b0be25 --- /dev/null +++ b/processor/frost-attempt-manager/README.md @@ -0,0 +1,6 @@ +# FROST Attempt Manager + +A library for helper structures to manage various attempts of a FROST signing +protocol. + +This library is interacted with via the `serai-processor-messages::sign` API. diff --git a/processor/frost-attempt-manager/src/individual.rs b/processor/frost-attempt-manager/src/individual.rs new file mode 100644 index 00000000..f64ad453 --- /dev/null +++ b/processor/frost-attempt-manager/src/individual.rs @@ -0,0 +1,251 @@ +use std::collections::HashMap; + +use rand_core::OsRng; + +use frost::{ + Participant, FrostError, + sign::{Writable, PreprocessMachine, SignMachine, SignatureMachine}, +}; + +use serai_validator_sets_primitives::Session; + +use messages::sign::{SignId, ProcessorMessage}; + +/// An instance of a signing protocol with re-attempts handled internally. +#[allow(clippy::type_complexity)] +pub(crate) struct SigningProtocol { + // The session this signing protocol is being conducted by. + session: Session, + // The `i` of our first, or starting, set of key shares we will be signing with. + // The key shares we sign with are expected to be continguous from this position. + start_i: Participant, + // The ID of this signing protocol. + id: [u8; 32], + // This accepts a vector of `root` machines in order to support signing with multiple key shares. + root: Vec, + preprocessed: HashMap, HashMap>)>, + // Here, we drop to a single machine as we only need one to complete the signature. + shared: HashMap< + u32, + ( + >::SignatureMachine, + HashMap>, + ), + >, +} + +impl SigningProtocol { + /// Create a new signing protocol. + pub(crate) fn new(session: Session, start_i: Participant, id: [u8; 32], root: Vec) -> Self { + log::info!("starting signing protocol {}", hex::encode(id)); + + Self { + session, + start_i, + id, + root, + preprocessed: HashMap::with_capacity(1), + shared: HashMap::with_capacity(1), + } + } + + /// Start a new attempt of the signing protocol. + /// + /// Returns the (serialized) preprocesses for the attempt. + pub(crate) fn attempt(&mut self, attempt: u32) -> Vec { + /* + We'd get slashed as malicious if we: + 1) Preprocessed + 2) Rebooted + 3) On reboot, preprocessed again, sending new preprocesses which would be deduplicated by + the message-queue + 4) Got sent preprocesses + 5) Sent a share based on our new preprocesses, yet with everyone else expecting it to be + based on our old preprocesses + + We avoid this by saving to the DB we preprocessed before sending our preprocessed, and only + keeping our preprocesses for this instance of the processor. Accordingly, on reboot, we will + flag the prior preprocess and not send new preprocesses. + + We also won't send the share we were supposed to, unfortunately, yet caching/reloading the + preprocess has enough safety issues it isn't worth the headache. + */ + // TODO + + log::debug!("attemting a new instance of signing protocol {}", hex::encode(self.id)); + + let mut our_preprocesses = HashMap::with_capacity(self.root.len()); + let mut preprocessed = Vec::with_capacity(self.root.len()); + let mut preprocesses = Vec::with_capacity(self.root.len()); + for (i, machine) in self.root.iter().enumerate() { + let (machine, preprocess) = machine.clone().preprocess(&mut OsRng); + preprocessed.push(machine); + + let mut this_preprocess = Vec::with_capacity(64); + preprocess.write(&mut this_preprocess).unwrap(); + + our_preprocesses.insert( + Participant::new( + u16::from(self.start_i) + u16::try_from(i).expect("signing with 2**16 machines"), + ) + .expect("start_i + i exceeded the valid indexes for a Participant"), + this_preprocess.clone(), + ); + preprocesses.push(this_preprocess); + } + assert!(self.preprocessed.insert(attempt, (preprocessed, our_preprocesses)).is_none()); + + vec![ProcessorMessage::Preprocesses { + id: SignId { session: self.session, id: self.id, attempt }, + preprocesses, + }] + } + + /// Handle preprocesses for the signing protocol. + /// + /// Returns the (serialized) shares for the attempt. + pub(crate) fn preprocesses( + &mut self, + attempt: u32, + serialized_preprocesses: HashMap>, + ) -> Vec { + log::debug!("handling preprocesses for signing protocol {}", hex::encode(self.id)); + + let Some((machines, our_serialized_preprocesses)) = self.preprocessed.remove(&attempt) else { + return vec![]; + }; + + let mut msgs = Vec::with_capacity(1); + + let mut preprocesses = + HashMap::with_capacity(serialized_preprocesses.len() + our_serialized_preprocesses.len()); + for (i, serialized_preprocess) in + serialized_preprocesses.into_iter().chain(our_serialized_preprocesses) + { + let mut serialized_preprocess = serialized_preprocess.as_slice(); + let Ok(preprocess) = machines[0].read_preprocess(&mut serialized_preprocess) else { + msgs.push(ProcessorMessage::InvalidParticipant { session: self.session, participant: i }); + continue; + }; + if !serialized_preprocess.is_empty() { + msgs.push(ProcessorMessage::InvalidParticipant { session: self.session, participant: i }); + continue; + } + preprocesses.insert(i, preprocess); + } + // We throw out our preprocessed machines here, despite the fact they haven't been invalidated + // We could reuse them with a new set of valid preprocesses + // https://github.com/serai-dex/serai/issues/588 + if !msgs.is_empty() { + return msgs; + } + + let mut our_shares = HashMap::with_capacity(self.root.len()); + let mut shared = Vec::with_capacity(machines.len()); + let mut shares = Vec::with_capacity(machines.len()); + for (i, machine) in machines.into_iter().enumerate() { + let i = Participant::new( + u16::from(self.start_i) + u16::try_from(i).expect("signing with 2**16 machines"), + ) + .expect("start_i + i exceeded the valid indexes for a Participant"); + + let mut preprocesses = preprocesses.clone(); + assert!(preprocesses.remove(&i).is_some()); + + // TODO: Replace this with `()`, which requires making the message type part of the trait + let (machine, share) = match machine.sign(preprocesses, &[]) { + Ok((machine, share)) => (machine, share), + Err(e) => match e { + FrostError::InternalError(_) | + FrostError::InvalidParticipant(_, _) | + FrostError::InvalidSigningSet(_) | + FrostError::InvalidParticipantQuantity(_, _) | + FrostError::DuplicatedParticipant(_) | + FrostError::MissingParticipant(_) | + FrostError::InvalidShare(_) => { + panic!("FROST had an error which shouldn't be reachable: {e:?}"); + } + FrostError::InvalidPreprocess(i) => { + msgs + .push(ProcessorMessage::InvalidParticipant { session: self.session, participant: i }); + return msgs; + } + }, + }; + shared.push(machine); + + let mut this_share = Vec::with_capacity(32); + share.write(&mut this_share).unwrap(); + + our_shares.insert(i, this_share.clone()); + shares.push(this_share); + } + + assert!(self.shared.insert(attempt, (shared.swap_remove(0), our_shares)).is_none()); + log::debug!( + "successfully handled preprocesses for signing protocol {}, sending shares", + hex::encode(self.id) + ); + msgs.push(ProcessorMessage::Shares { + id: SignId { session: self.session, id: self.id, attempt }, + shares, + }); + msgs + } + + /// Process shares for the signing protocol. + /// + /// Returns the signature produced by the protocol. + pub(crate) fn shares( + &mut self, + attempt: u32, + serialized_shares: HashMap>, + ) -> Result> { + log::debug!("handling shares for signing protocol {}", hex::encode(self.id)); + + let Some((machine, our_serialized_shares)) = self.shared.remove(&attempt) else { Err(vec![])? }; + + let mut msgs = Vec::with_capacity(1); + + let mut shares = HashMap::with_capacity(serialized_shares.len() + our_serialized_shares.len()); + for (i, serialized_share) in our_serialized_shares.into_iter().chain(serialized_shares) { + let mut serialized_share = serialized_share.as_slice(); + let Ok(share) = machine.read_share(&mut serialized_share) else { + msgs.push(ProcessorMessage::InvalidParticipant { session: self.session, participant: i }); + continue; + }; + if !serialized_share.is_empty() { + msgs.push(ProcessorMessage::InvalidParticipant { session: self.session, participant: i }); + continue; + } + shares.insert(i, share); + } + if !msgs.is_empty() { + Err(msgs)?; + } + + assert!(shares.remove(&self.start_i).is_some()); + + let signature = match machine.complete(shares) { + Ok(signature) => signature, + Err(e) => match e { + FrostError::InternalError(_) | + FrostError::InvalidParticipant(_, _) | + FrostError::InvalidSigningSet(_) | + FrostError::InvalidParticipantQuantity(_, _) | + FrostError::DuplicatedParticipant(_) | + FrostError::MissingParticipant(_) | + FrostError::InvalidPreprocess(_) => { + panic!("FROST had an error which shouldn't be reachable: {e:?}"); + } + FrostError::InvalidShare(i) => { + Err(vec![ProcessorMessage::InvalidParticipant { session: self.session, participant: i }])? + } + }, + }; + + log::info!("finished signing for protocol {}", hex::encode(self.id)); + + Ok(signature) + } +} diff --git a/processor/frost-attempt-manager/src/lib.rs b/processor/frost-attempt-manager/src/lib.rs new file mode 100644 index 00000000..e7e51d30 --- /dev/null +++ b/processor/frost-attempt-manager/src/lib.rs @@ -0,0 +1,92 @@ +#![cfg_attr(docsrs, feature(doc_auto_cfg))] +#![doc = include_str!("../README.md")] +#![deny(missing_docs)] + +use std::collections::HashMap; + +use frost::{Participant, sign::PreprocessMachine}; + +use serai_validator_sets_primitives::Session; + +use messages::sign::{ProcessorMessage, CoordinatorMessage}; + +mod individual; +use individual::SigningProtocol; + +/// A response to handling a message from the coordinator. +pub enum Response { + /// Messages to send to the coordinator. + Messages(Vec), + /// A produced signature. + Signature(M::Signature), +} + +/// A manager of attempts for a variety of signing protocols. +pub struct AttemptManager { + session: Session, + start_i: Participant, + active: HashMap<[u8; 32], SigningProtocol>, +} + +impl AttemptManager { + /// Create a new attempt manager. + pub fn new(session: Session, start_i: Participant) -> Self { + AttemptManager { session, start_i, active: HashMap::new() } + } + + /// Register a signing protocol to attempt. + pub fn register(&mut self, id: [u8; 32], machines: Vec) { + self.active.insert(id, SigningProtocol::new(self.session, self.start_i, id, machines)); + } + + /// Retire a signing protocol. + /// + /// This frees all memory used for it and means no further messages will be handled for it. + /// This does not stop the protocol from being re-registered and further worked on (with + /// undefined behavior) then. The higher-level context must never call `register` again with this + /// ID. + // TODO: Also have the DB for this SigningProtocol cleaned up here. + pub fn retire(&mut self, id: [u8; 32]) { + log::info!("retiring signing protocol {}", hex::encode(id)); + self.active.remove(&id); + } + + /// Handle a message for a signing protocol. + pub fn handle(&mut self, msg: CoordinatorMessage) -> Response { + match msg { + CoordinatorMessage::Preprocesses { id, preprocesses } => { + let Some(protocol) = self.active.get_mut(&id.id) else { + log::trace!( + "handling preprocesses for signing protocol {}, which we're not actively running", + hex::encode(id.id) + ); + return Response::Messages(vec![]); + }; + Response::Messages(protocol.preprocesses(id.attempt, preprocesses)) + } + CoordinatorMessage::Shares { id, shares } => { + let Some(protocol) = self.active.get_mut(&id.id) else { + log::trace!( + "handling shares for signing protocol {}, which we're not actively running", + hex::encode(id.id) + ); + return Response::Messages(vec![]); + }; + match protocol.shares(id.attempt, shares) { + Ok(signature) => Response::Signature(signature), + Err(messages) => Response::Messages(messages), + } + } + CoordinatorMessage::Reattempt { id } => { + let Some(protocol) = self.active.get_mut(&id.id) else { + log::trace!( + "reattempting signing protocol {}, which we're not actively running", + hex::encode(id.id) + ); + return Response::Messages(vec![]); + }; + Response::Messages(protocol.attempt(id.attempt)) + } + } + } +} diff --git a/processor/key-gen/src/lib.rs b/processor/key-gen/src/lib.rs index 8d4e911f..3d8c3552 100644 --- a/processor/key-gen/src/lib.rs +++ b/processor/key-gen/src/lib.rs @@ -17,8 +17,6 @@ use ciphersuite::{ }; use dkg::{Participant, ThresholdKeys, evrf::*}; -use log::info; - use serai_validator_sets_primitives::Session; use messages::key_gen::*; @@ -184,7 +182,7 @@ impl KeyGen { match msg { CoordinatorMessage::GenerateKey { session, threshold, evrf_public_keys } => { - info!("Generating new key. Session: {session:?}"); + log::info!("Generating new key. Session: {session:?}"); // Unzip the vector of eVRF keys let substrate_evrf_public_keys = @@ -260,7 +258,7 @@ impl KeyGen { } CoordinatorMessage::Participation { session, participant, participation } => { - info!("received participation from {:?} for {:?}", participant, session); + log::info!("received participation from {:?} for {:?}", participant, session); let Params { t: threshold, n, substrate_evrf_public_keys, network_evrf_public_keys } = KeyGenDb::

::params(txn, session).unwrap(); @@ -295,7 +293,7 @@ impl KeyGen { // participations and continue. We solely have to verify them, as to identify malicious // participants and prevent DoSs, before returning if self.key_shares(session).is_some() { - info!("already finished generating a key for {:?}", session); + log::info!("already finished generating a key for {:?}", session); match EvrfDkg::::verify( &mut OsRng, diff --git a/processor/messages/src/lib.rs b/processor/messages/src/lib.rs index 98af97ce..096fddb9 100644 --- a/processor/messages/src/lib.rs +++ b/processor/messages/src/lib.rs @@ -22,7 +22,6 @@ pub mod key_gen { #[derive(Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] pub enum CoordinatorMessage { // Instructs the Processor to begin the key generation process. - // TODO: Should this be moved under Substrate? GenerateKey { session: Session, threshold: u16, evrf_public_keys: Vec<([u8; 32], Vec)> }, // Received participations for the specified key generation protocol. Participation { session: Session, participant: Participant, participation: Vec }, @@ -93,6 +92,8 @@ pub mod sign { pub attempt: u32, } + // TODO: Make this generic to the ID once we introduce topics into the message-queue and remove + // the global ProcessorMessage/CoordinatorMessage #[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)] pub enum CoordinatorMessage { // Received preprocesses for the specified signing protocol. @@ -101,8 +102,10 @@ pub mod sign { Shares { id: SignId, shares: HashMap> }, // Re-attempt a signing protocol. Reattempt { id: SignId }, + /* TODO // Completed a signing protocol already. Completed { session: Session, id: [u8; 32], tx: Vec }, + */ } impl CoordinatorMessage { @@ -114,8 +117,8 @@ pub mod sign { match self { CoordinatorMessage::Preprocesses { id, .. } | CoordinatorMessage::Shares { id, .. } | - CoordinatorMessage::Reattempt { id } => id.session, - CoordinatorMessage::Completed { session, .. } => *session, + CoordinatorMessage::Reattempt { id, .. } => id.session, + // TODO CoordinatorMessage::Completed { session, .. } => *session, } } } @@ -123,13 +126,13 @@ pub mod sign { #[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)] pub enum ProcessorMessage { // Participant sent an invalid message during the sign protocol. - InvalidParticipant { id: SignId, participant: Participant }, - // Created preprocess for the specified signing protocol. - Preprocess { id: SignId, preprocesses: Vec> }, - // Signed share for the specified signing protocol. - Share { id: SignId, shares: Vec> }, + InvalidParticipant { session: Session, participant: Participant }, + // Created preprocesses for the specified signing protocol. + Preprocesses { id: SignId, preprocesses: Vec> }, + // Signed shares for the specified signing protocol. + Shares { id: SignId, shares: Vec> }, // Completed a signing protocol already. - Completed { session: Session, id: [u8; 32], tx: Vec }, + // TODO Completed { session: Session, id: [u8; 32], tx: Vec }, } } @@ -165,10 +168,6 @@ pub mod coordinator { pub enum CoordinatorMessage { CosignSubstrateBlock { id: SubstrateSignId, block_number: u64 }, SignSlashReport { id: SubstrateSignId, report: Vec<([u8; 32], u32)> }, - SubstratePreprocesses { id: SubstrateSignId, preprocesses: HashMap }, - SubstrateShares { id: SubstrateSignId, shares: HashMap }, - // Re-attempt a batch signing protocol. - BatchReattempt { id: SubstrateSignId }, } impl CoordinatorMessage { @@ -192,9 +191,9 @@ pub mod coordinator { SubstrateBlockAck { block: u64, plans: Vec }, InvalidParticipant { id: SubstrateSignId, participant: Participant }, CosignPreprocess { id: SubstrateSignId, preprocesses: Vec<[u8; 64]> }, + // TODO: Remove BatchPreprocess? Why does this take a BlockHash here and not in its + // SubstrateSignId? BatchPreprocess { id: SubstrateSignId, block: BlockHash, preprocesses: Vec<[u8; 64]> }, - SlashReportPreprocess { id: SubstrateSignId, preprocesses: Vec<[u8; 64]> }, - SubstrateShare { id: SubstrateSignId, shares: Vec<[u8; 32]> }, // TODO: Make these signatures [u8; 64]? CosignedBlock { block_number: u64, block: [u8; 32], signature: Vec }, SignedSlashReport { session: Session, signature: Vec }, @@ -327,19 +326,19 @@ impl CoordinatorMessage { } CoordinatorMessage::Sign(msg) => { let (sub, id) = match msg { - // Unique since SignId includes a hash of the network, and specific transaction info - sign::CoordinatorMessage::Preprocesses { id, .. } => (0, id.encode()), - sign::CoordinatorMessage::Shares { id, .. } => (1, id.encode()), - sign::CoordinatorMessage::Reattempt { id } => (2, id.encode()), + // Unique since SignId + sign::CoordinatorMessage::Preprocesses { id, .. } => (0, id), + sign::CoordinatorMessage::Shares { id, .. } => (1, id), + sign::CoordinatorMessage::Reattempt { id, .. } => (2, id), // The coordinator should report all reported completions to the processor // Accordingly, the intent is a combination of plan ID and actual TX // While transaction alone may suffice, that doesn't cover cross-chain TX ID conflicts, // which are possible - sign::CoordinatorMessage::Completed { id, tx, .. } => (3, (id, tx).encode()), + // TODO sign::CoordinatorMessage::Completed { id, tx, .. } => (3, (id, tx).encode()), }; let mut res = vec![COORDINATOR_UID, TYPE_SIGN_UID, sub]; - res.extend(&id); + res.extend(id.encode()); res } CoordinatorMessage::Coordinator(msg) => { @@ -349,10 +348,6 @@ impl CoordinatorMessage { // Unique since there's only one of these per session/attempt, and ID is inclusive to // both coordinator::CoordinatorMessage::SignSlashReport { id, .. } => (1, id.encode()), - // Unique since this embeds the batch ID (including its network) and attempt - coordinator::CoordinatorMessage::SubstratePreprocesses { id, .. } => (2, id.encode()), - coordinator::CoordinatorMessage::SubstrateShares { id, .. } => (3, id.encode()), - coordinator::CoordinatorMessage::BatchReattempt { id, .. } => (4, id.encode()), }; let mut res = vec![COORDINATOR_UID, TYPE_COORDINATOR_UID, sub]; @@ -404,12 +399,15 @@ impl ProcessorMessage { } ProcessorMessage::Sign(msg) => { let (sub, id) = match msg { + // Unique since we'll only fatally slash a a participant once + sign::ProcessorMessage::InvalidParticipant { session, participant } => { + (0, (session, u16::from(*participant)).encode()) + } // Unique since SignId - sign::ProcessorMessage::InvalidParticipant { id, .. } => (0, id.encode()), - sign::ProcessorMessage::Preprocess { id, .. } => (1, id.encode()), - sign::ProcessorMessage::Share { id, .. } => (2, id.encode()), + sign::ProcessorMessage::Preprocesses { id, .. } => (1, id.encode()), + sign::ProcessorMessage::Shares { id, .. } => (2, id.encode()), // Unique since a processor will only sign a TX once - sign::ProcessorMessage::Completed { id, .. } => (3, id.to_vec()), + // TODO sign::ProcessorMessage::Completed { id, .. } => (3, id.to_vec()), }; let mut res = vec![PROCESSOR_UID, TYPE_SIGN_UID, sub]; @@ -423,11 +421,9 @@ impl ProcessorMessage { coordinator::ProcessorMessage::InvalidParticipant { id, .. } => (1, id.encode()), coordinator::ProcessorMessage::CosignPreprocess { id, .. } => (2, id.encode()), coordinator::ProcessorMessage::BatchPreprocess { id, .. } => (3, id.encode()), - coordinator::ProcessorMessage::SlashReportPreprocess { id, .. } => (4, id.encode()), - coordinator::ProcessorMessage::SubstrateShare { id, .. } => (5, id.encode()), // Unique since only one instance of a signature matters - coordinator::ProcessorMessage::CosignedBlock { block, .. } => (6, block.encode()), - coordinator::ProcessorMessage::SignedSlashReport { .. } => (7, vec![]), + coordinator::ProcessorMessage::CosignedBlock { block, .. } => (4, block.encode()), + coordinator::ProcessorMessage::SignedSlashReport { .. } => (5, vec![]), }; let mut res = vec![PROCESSOR_UID, TYPE_COORDINATOR_UID, sub];