mirror of
https://github.com/serai-dex/serai.git
synced 2024-12-23 03:59:22 +00:00
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.
This commit is contained in:
parent
2f29c91d30
commit
1e8a9ec5bd
12 changed files with 442 additions and 39 deletions
1
.github/workflows/tests.yml
vendored
1
.github/workflows/tests.yml
vendored
|
@ -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 \
|
||||
|
|
13
Cargo.lock
generated
13
Cargo.lock
generated
|
@ -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"
|
||||
|
|
|
@ -71,6 +71,7 @@ members = [
|
|||
|
||||
"processor/messages",
|
||||
"processor/key-gen",
|
||||
"processor/frost-attempt-manager",
|
||||
"processor",
|
||||
|
||||
"coordinator/tributary/tendermint",
|
||||
|
|
|
@ -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" },
|
||||
|
|
|
@ -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
|
||||
|
|
29
processor/frost-attempt-manager/Cargo.toml
Normal file
29
processor/frost-attempt-manager/Cargo.toml
Normal file
|
@ -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 <lukeparker5132@gmail.com>"]
|
||||
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" }
|
15
processor/frost-attempt-manager/LICENSE
Normal file
15
processor/frost-attempt-manager/LICENSE
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
6
processor/frost-attempt-manager/README.md
Normal file
6
processor/frost-attempt-manager/README.md
Normal file
|
@ -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.
|
251
processor/frost-attempt-manager/src/individual.rs
Normal file
251
processor/frost-attempt-manager/src/individual.rs
Normal file
|
@ -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<M: Clone + PreprocessMachine> {
|
||||
// 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<M>,
|
||||
preprocessed: HashMap<u32, (Vec<M::SignMachine>, HashMap<Participant, Vec<u8>>)>,
|
||||
// Here, we drop to a single machine as we only need one to complete the signature.
|
||||
shared: HashMap<
|
||||
u32,
|
||||
(
|
||||
<M::SignMachine as SignMachine<M::Signature>>::SignatureMachine,
|
||||
HashMap<Participant, Vec<u8>>,
|
||||
),
|
||||
>,
|
||||
}
|
||||
|
||||
impl<M: Clone + PreprocessMachine> SigningProtocol<M> {
|
||||
/// Create a new signing protocol.
|
||||
pub(crate) fn new(session: Session, start_i: Participant, id: [u8; 32], root: Vec<M>) -> 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<ProcessorMessage> {
|
||||
/*
|
||||
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<Participant, Vec<u8>>,
|
||||
) -> Vec<ProcessorMessage> {
|
||||
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<Participant, Vec<u8>>,
|
||||
) -> Result<M::Signature, Vec<ProcessorMessage>> {
|
||||
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)
|
||||
}
|
||||
}
|
92
processor/frost-attempt-manager/src/lib.rs
Normal file
92
processor/frost-attempt-manager/src/lib.rs
Normal file
|
@ -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<M: PreprocessMachine> {
|
||||
/// Messages to send to the coordinator.
|
||||
Messages(Vec<ProcessorMessage>),
|
||||
/// A produced signature.
|
||||
Signature(M::Signature),
|
||||
}
|
||||
|
||||
/// A manager of attempts for a variety of signing protocols.
|
||||
pub struct AttemptManager<M: Clone + PreprocessMachine> {
|
||||
session: Session,
|
||||
start_i: Participant,
|
||||
active: HashMap<[u8; 32], SigningProtocol<M>>,
|
||||
}
|
||||
|
||||
impl<M: Clone + PreprocessMachine> AttemptManager<M> {
|
||||
/// 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<M>) {
|
||||
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<M> {
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<P: KeyGenParams, D: Db> KeyGen<P, D> {
|
|||
|
||||
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<P: KeyGenParams, D: Db> KeyGen<P, D> {
|
|||
}
|
||||
|
||||
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::<P>::params(txn, session).unwrap();
|
||||
|
@ -295,7 +293,7 @@ impl<P: KeyGenParams, D: Db> KeyGen<P, D> {
|
|||
// 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::<Ristretto>::verify(
|
||||
&mut OsRng,
|
||||
|
|
|
@ -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<u8>)> },
|
||||
// Received participations for the specified key generation protocol.
|
||||
Participation { session: Session, participant: Participant, participation: Vec<u8> },
|
||||
|
@ -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<Participant, Vec<u8>> },
|
||||
// Re-attempt a signing protocol.
|
||||
Reattempt { id: SignId },
|
||||
/* TODO
|
||||
// Completed a signing protocol already.
|
||||
Completed { session: Session, id: [u8; 32], tx: Vec<u8> },
|
||||
*/
|
||||
}
|
||||
|
||||
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<Vec<u8>> },
|
||||
// Signed share for the specified signing protocol.
|
||||
Share { id: SignId, shares: Vec<Vec<u8>> },
|
||||
InvalidParticipant { session: Session, participant: Participant },
|
||||
// Created preprocesses for the specified signing protocol.
|
||||
Preprocesses { id: SignId, preprocesses: Vec<Vec<u8>> },
|
||||
// Signed shares for the specified signing protocol.
|
||||
Shares { id: SignId, shares: Vec<Vec<u8>> },
|
||||
// Completed a signing protocol already.
|
||||
Completed { session: Session, id: [u8; 32], tx: Vec<u8> },
|
||||
// TODO Completed { session: Session, id: [u8; 32], tx: Vec<u8> },
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<Participant, [u8; 64]> },
|
||||
SubstrateShares { id: SubstrateSignId, shares: HashMap<Participant, [u8; 32]> },
|
||||
// Re-attempt a batch signing protocol.
|
||||
BatchReattempt { id: SubstrateSignId },
|
||||
}
|
||||
|
||||
impl CoordinatorMessage {
|
||||
|
@ -192,9 +191,9 @@ pub mod coordinator {
|
|||
SubstrateBlockAck { block: u64, plans: Vec<PlanMeta> },
|
||||
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<u8> },
|
||||
SignedSlashReport { session: Session, signature: Vec<u8> },
|
||||
|
@ -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];
|
||||
|
|
Loading…
Reference in a new issue