Cleanup DB handling a bit in key-gen/attempt-manager

This commit is contained in:
Luke Parker 2024-08-19 00:41:18 -04:00
parent 1e8a9ec5bd
commit 2f3bd7a02a
6 changed files with 77 additions and 27 deletions

2
Cargo.lock generated
View file

@ -8603,9 +8603,11 @@ dependencies = [
name = "serai-processor-frost-attempt-manager" name = "serai-processor-frost-attempt-manager"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"borsh",
"hex", "hex",
"log", "log",
"modular-frost", "modular-frost",
"parity-scale-codec",
"rand_core", "rand_core",
"serai-db", "serai-db",
"serai-processor-messages", "serai-processor-messages",

View file

@ -25,5 +25,9 @@ serai-validator-sets-primitives = { path = "../../substrate/validator-sets/primi
hex = { version = "0.4", default-features = false, features = ["std"] } hex = { version = "0.4", default-features = false, features = ["std"] }
log = { version = "0.4", default-features = false, features = ["std"] } log = { version = "0.4", default-features = false, features = ["std"] }
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["std"] }
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
serai-db = { path = "../../common/db" } serai-db = { path = "../../common/db" }
messages = { package = "serai-processor-messages", path = "../messages" } messages = { package = "serai-processor-messages", path = "../messages" }

View file

@ -9,11 +9,19 @@ use frost::{
use serai_validator_sets_primitives::Session; use serai_validator_sets_primitives::Session;
use serai_db::{Get, DbTxn, Db, create_db};
use messages::sign::{SignId, ProcessorMessage}; use messages::sign::{SignId, ProcessorMessage};
create_db!(
FrostAttemptManager {
Attempted: (id: [u8; 32]) -> u32,
}
);
/// An instance of a signing protocol with re-attempts handled internally. /// An instance of a signing protocol with re-attempts handled internally.
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
pub(crate) struct SigningProtocol<M: Clone + PreprocessMachine> { pub(crate) struct SigningProtocol<D: Db, M: Clone + PreprocessMachine> {
db: D,
// The session this signing protocol is being conducted by. // The session this signing protocol is being conducted by.
session: Session, session: Session,
// The `i` of our first, or starting, set of key shares we will be signing with. // The `i` of our first, or starting, set of key shares we will be signing with.
@ -34,12 +42,19 @@ pub(crate) struct SigningProtocol<M: Clone + PreprocessMachine> {
>, >,
} }
impl<M: Clone + PreprocessMachine> SigningProtocol<M> { impl<D: Db, M: Clone + PreprocessMachine> SigningProtocol<D, M> {
/// Create a new signing protocol. /// Create a new signing protocol.
pub(crate) fn new(session: Session, start_i: Participant, id: [u8; 32], root: Vec<M>) -> Self { pub(crate) fn new(
db: D,
session: Session,
start_i: Participant,
id: [u8; 32],
root: Vec<M>,
) -> Self {
log::info!("starting signing protocol {}", hex::encode(id)); log::info!("starting signing protocol {}", hex::encode(id));
Self { Self {
db,
session, session,
start_i, start_i,
id, id,
@ -70,7 +85,15 @@ impl<M: Clone + PreprocessMachine> SigningProtocol<M> {
We also won't send the share we were supposed to, unfortunately, yet caching/reloading the 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. preprocess has enough safety issues it isn't worth the headache.
*/ */
// TODO {
let mut txn = self.db.txn();
let prior_attempted = Attempted::get(&txn, self.id);
if Some(attempt) <= prior_attempted {
return vec![];
}
Attempted::set(&mut txn, self.id, &attempt);
txn.commit();
}
log::debug!("attemting a new instance of signing protocol {}", hex::encode(self.id)); log::debug!("attemting a new instance of signing protocol {}", hex::encode(self.id));
@ -248,4 +271,11 @@ impl<M: Clone + PreprocessMachine> SigningProtocol<M> {
Ok(signature) Ok(signature)
} }
/// Cleanup the database entries for a specified signing protocol.
pub(crate) fn cleanup(db: &mut D, id: [u8; 32]) {
let mut txn = db.txn();
Attempted::del(&mut txn, id);
txn.commit();
}
} }

View file

@ -8,6 +8,7 @@ use frost::{Participant, sign::PreprocessMachine};
use serai_validator_sets_primitives::Session; use serai_validator_sets_primitives::Session;
use serai_db::Db;
use messages::sign::{ProcessorMessage, CoordinatorMessage}; use messages::sign::{ProcessorMessage, CoordinatorMessage};
mod individual; mod individual;
@ -22,21 +23,28 @@ pub enum Response<M: PreprocessMachine> {
} }
/// A manager of attempts for a variety of signing protocols. /// A manager of attempts for a variety of signing protocols.
pub struct AttemptManager<M: Clone + PreprocessMachine> { pub struct AttemptManager<D: Db, M: Clone + PreprocessMachine> {
db: D,
session: Session, session: Session,
start_i: Participant, start_i: Participant,
active: HashMap<[u8; 32], SigningProtocol<M>>, active: HashMap<[u8; 32], SigningProtocol<D, M>>,
} }
impl<M: Clone + PreprocessMachine> AttemptManager<M> { impl<D: Db, M: Clone + PreprocessMachine> AttemptManager<D, M> {
/// Create a new attempt manager. /// Create a new attempt manager.
pub fn new(session: Session, start_i: Participant) -> Self { pub fn new(db: D, session: Session, start_i: Participant) -> Self {
AttemptManager { session, start_i, active: HashMap::new() } AttemptManager { db, session, start_i, active: HashMap::new() }
} }
/// Register a signing protocol to attempt. /// 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)); /// This ID must be unique across all sessions, attempt managers, protocols, etc.
pub fn register(&mut self, id: [u8; 32], machines: Vec<M>) -> Vec<ProcessorMessage> {
let mut protocol =
SigningProtocol::new(self.db.clone(), self.session, self.start_i, id, machines);
let messages = protocol.attempt(0);
self.active.insert(id, protocol);
messages
} }
/// Retire a signing protocol. /// Retire a signing protocol.
@ -45,10 +53,13 @@ impl<M: Clone + PreprocessMachine> AttemptManager<M> {
/// This does not stop the protocol from being re-registered and further worked on (with /// 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 /// undefined behavior) then. The higher-level context must never call `register` again with this
/// ID. /// ID.
// TODO: Also have the DB for this SigningProtocol cleaned up here.
pub fn retire(&mut self, id: [u8; 32]) { pub fn retire(&mut self, id: [u8; 32]) {
log::info!("retiring signing protocol {}", hex::encode(id)); if self.active.remove(&id).is_none() {
self.active.remove(&id); log::info!("retiring protocol {}, which we didn't register/already retired", hex::encode(id));
} else {
log::info!("retired signing protocol {}", hex::encode(id));
}
SigningProtocol::<D, M>::cleanup(&mut self.db, id);
} }
/// Handle a message for a signing protocol. /// Handle a message for a signing protocol.

View file

@ -36,10 +36,10 @@ pub(crate) struct Participations {
} }
create_db!( create_db!(
KeyGenDb { KeyGen {
ParamsDb: (session: &Session) -> RawParams, Params: (session: &Session) -> RawParams,
ParticipationsDb: (session: &Session) -> Participations, Participations: (session: &Session) -> Participations,
KeySharesDb: (session: &Session) -> Vec<u8>, KeyShares: (session: &Session) -> Vec<u8>,
} }
); );
@ -48,7 +48,7 @@ impl<P: KeyGenParams> KeyGenDb<P> {
pub(crate) fn set_params(txn: &mut impl DbTxn, session: Session, params: Params<P>) { pub(crate) fn set_params(txn: &mut impl DbTxn, session: Session, params: Params<P>) {
assert_eq!(params.substrate_evrf_public_keys.len(), params.network_evrf_public_keys.len()); assert_eq!(params.substrate_evrf_public_keys.len(), params.network_evrf_public_keys.len());
ParamsDb::set( Params::set(
txn, txn,
&session, &session,
&RawParams { &RawParams {
@ -68,7 +68,7 @@ impl<P: KeyGenParams> KeyGenDb<P> {
} }
pub(crate) fn params(getter: &impl Get, session: Session) -> Option<Params<P>> { pub(crate) fn params(getter: &impl Get, session: Session) -> Option<Params<P>> {
ParamsDb::get(getter, &session).map(|params| Params { Params::get(getter, &session).map(|params| Params {
t: params.t, t: params.t,
n: params n: params
.network_evrf_public_keys .network_evrf_public_keys
@ -101,12 +101,13 @@ impl<P: KeyGenParams> KeyGenDb<P> {
session: Session, session: Session,
participations: &Participations, participations: &Participations,
) { ) {
ParticipationsDb::set(txn, &session, participations) Participations::set(txn, &session, participations)
} }
pub(crate) fn participations(getter: &impl Get, session: Session) -> Option<Participations> { pub(crate) fn participations(getter: &impl Get, session: Session) -> Option<Participations> {
ParticipationsDb::get(getter, &session) Participations::get(getter, &session)
} }
// Set the key shares for a session.
pub(crate) fn set_key_shares( pub(crate) fn set_key_shares(
txn: &mut impl DbTxn, txn: &mut impl DbTxn,
session: Session, session: Session,
@ -120,7 +121,7 @@ impl<P: KeyGenParams> KeyGenDb<P> {
keys.extend(substrate_keys.serialize().as_slice()); keys.extend(substrate_keys.serialize().as_slice());
keys.extend(network_keys.serialize().as_slice()); keys.extend(network_keys.serialize().as_slice());
} }
KeySharesDb::set(txn, &session, &keys); KeyShares::set(txn, &session, &keys);
} }
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
@ -128,7 +129,7 @@ impl<P: KeyGenParams> KeyGenDb<P> {
getter: &impl Get, getter: &impl Get,
session: Session, session: Session,
) -> Option<(Vec<ThresholdKeys<Ristretto>>, Vec<ThresholdKeys<P::ExternalNetworkCurve>>)> { ) -> Option<(Vec<ThresholdKeys<Ristretto>>, Vec<ThresholdKeys<P::ExternalNetworkCurve>>)> {
let keys = KeySharesDb::get(getter, &session)?; let keys = KeyShares::get(getter, &session)?;
let mut keys: &[u8] = keys.as_ref(); let mut keys: &[u8] = keys.as_ref();
let mut substrate_keys = vec![]; let mut substrate_keys = vec![];

View file

@ -182,7 +182,7 @@ impl<P: KeyGenParams, D: Db> KeyGen<P, D> {
match msg { match msg {
CoordinatorMessage::GenerateKey { session, threshold, evrf_public_keys } => { CoordinatorMessage::GenerateKey { session, threshold, evrf_public_keys } => {
log::info!("Generating new key. Session: {session:?}"); log::info!("generating new key, session: {session:?}");
// Unzip the vector of eVRF keys // Unzip the vector of eVRF keys
let substrate_evrf_public_keys = let substrate_evrf_public_keys =
@ -258,7 +258,7 @@ impl<P: KeyGenParams, D: Db> KeyGen<P, D> {
} }
CoordinatorMessage::Participation { session, participant, participation } => { CoordinatorMessage::Participation { session, participant, participation } => {
log::info!("received participation from {:?} for {:?}", participant, session); log::debug!("received participation from {:?} for {:?}", participant, session);
let Params { t: threshold, n, substrate_evrf_public_keys, network_evrf_public_keys } = let Params { t: threshold, n, substrate_evrf_public_keys, network_evrf_public_keys } =
KeyGenDb::<P>::params(txn, session).unwrap(); KeyGenDb::<P>::params(txn, session).unwrap();
@ -293,7 +293,7 @@ impl<P: KeyGenParams, D: Db> KeyGen<P, D> {
// participations and continue. We solely have to verify them, as to identify malicious // participations and continue. We solely have to verify them, as to identify malicious
// participants and prevent DoSs, before returning // participants and prevent DoSs, before returning
if self.key_shares(session).is_some() { if self.key_shares(session).is_some() {
log::info!("already finished generating a key for {:?}", session); log::debug!("already finished generating a key for {:?}", session);
match EvrfDkg::<Ristretto>::verify( match EvrfDkg::<Ristretto>::verify(
&mut OsRng, &mut OsRng,
@ -511,6 +511,8 @@ impl<P: KeyGenParams, D: Db> KeyGen<P, D> {
} }
KeyGenDb::<P>::set_key_shares(txn, session, &substrate_keys, &network_keys); KeyGenDb::<P>::set_key_shares(txn, session, &substrate_keys, &network_keys);
log::info!("generated key, session: {session:?}");
// Since no one we verified was invalid, and we had the threshold, yield the new keys // Since no one we verified was invalid, and we had the threshold, yield the new keys
vec![ProcessorMessage::GeneratedKeyPair { vec![ProcessorMessage::GeneratedKeyPair {
session, session,