Files
serai/coordinator/substrate/src/lib.rs

238 lines
7.5 KiB
Rust
Raw Normal View History

2025-11-04 10:20:17 -05:00
#![cfg_attr(docsrs, feature(doc_cfg))]
2024-12-31 10:37:19 -05:00
#![doc = include_str!("../README.md")]
#![deny(missing_docs)]
use std::collections::HashMap;
use borsh::{BorshSerialize, BorshDeserialize};
use dkg::Participant;
2024-12-31 10:37:19 -05:00
use serai_client_serai::abi::{
primitives::{
network_id::ExternalNetworkId,
validator_sets::{Session, ExternalValidatorSet, SlashReport},
crypto::{Signature, KeyPair},
address::SeraiAddress,
instructions::SignedBatch,
},
2025-01-14 01:58:26 -05:00
Transaction,
2024-12-31 10:37:19 -05:00
};
use serai_db::*;
mod canonical;
pub use canonical::CanonicalEventStream;
use canonical::last_indexed_batch_id;
2024-12-31 10:37:19 -05:00
mod ephemeral;
pub use ephemeral::EphemeralEventStream;
2024-12-31 10:37:19 -05:00
2025-01-14 01:58:26 -05:00
mod set_keys;
pub use set_keys::SetKeysTask;
mod publish_batch;
pub use publish_batch::PublishBatchTask;
mod publish_slash_report;
pub use publish_slash_report::PublishSlashReportTask;
2024-12-31 10:37:19 -05:00
/// The information for a new set.
#[derive(Clone, Debug, BorshSerialize, BorshDeserialize)]
#[borsh(init = init_participant_indexes)]
2024-12-31 10:37:19 -05:00
pub struct NewSetInformation {
/// The set.
2025-01-30 03:14:24 -05:00
pub set: ExternalValidatorSet,
/// The Serai block which declared it.
pub serai_block: [u8; 32],
/// The time of the block which declared it, in seconds since the epoch.
pub declaration_time: u64,
/// The threshold to use.
pub threshold: u16,
/// The validators, with the amount of key shares they have.
pub validators: Vec<(SeraiAddress, u16)>,
/// The eVRF public keys.
///
/// This will have the necessary copies of the keys proper for each validator's weight,
/// accordingly syncing up with `participant_indexes`.
pub evrf_public_keys: Vec<([u8; 32], Vec<u8>)>,
/// The participant indexes, indexed by their validator.
#[borsh(skip)]
pub participant_indexes: HashMap<SeraiAddress, Vec<Participant>>,
/// The validators, indexed by their participant indexes.
#[borsh(skip)]
pub participant_indexes_reverse_lookup: HashMap<Participant, SeraiAddress>,
}
impl NewSetInformation {
fn init_participant_indexes(&mut self) {
let mut next_i = 1;
self.participant_indexes = HashMap::with_capacity(self.validators.len());
self.participant_indexes_reverse_lookup = HashMap::with_capacity(self.validators.len());
for (validator, weight) in &self.validators {
let mut these_is = Vec::with_capacity((*weight).into());
for _ in 0 .. *weight {
let this_i = Participant::new(next_i).unwrap();
next_i += 1;
these_is.push(this_i);
self.participant_indexes_reverse_lookup.insert(this_i, *validator);
}
self.participant_indexes.insert(*validator, these_is);
}
}
2024-12-31 10:37:19 -05:00
}
mod _public_db {
2025-01-14 01:58:26 -05:00
use super::*;
2024-12-31 10:37:19 -05:00
db_channel!(
CoordinatorSubstrate {
// Canonical messages to send to the processor
2025-01-30 03:14:24 -05:00
Canonical: (network: ExternalNetworkId) -> messages::substrate::CoordinatorMessage,
2024-12-31 10:37:19 -05:00
// Relevant new set, from an ephemeral event stream
NewSet: () -> NewSetInformation,
2025-01-11 06:51:55 -05:00
// Potentially relevant sign slash report, from an ephemeral event stream
2025-01-30 03:14:24 -05:00
SignSlashReport: (set: ExternalValidatorSet) -> (),
2025-01-14 01:58:26 -05:00
// Signed batches to publish onto the Serai network
2025-01-30 03:14:24 -05:00
SignedBatches: (network: ExternalNetworkId) -> SignedBatch,
2025-01-14 01:58:26 -05:00
}
);
create_db!(
CoordinatorSubstrate {
// Keys to set on the Serai network
Keys: (network: ExternalNetworkId) -> (Session, Transaction),
2025-01-14 01:58:26 -05:00
// Slash reports to publish onto the Serai network
SlashReports: (network: ExternalNetworkId) -> (Session, Transaction),
2024-12-31 10:37:19 -05:00
}
);
}
/// The canonical event stream.
pub struct Canonical;
impl Canonical {
pub(crate) fn send(
txn: &mut impl DbTxn,
2025-01-30 03:14:24 -05:00
network: ExternalNetworkId,
2024-12-31 10:37:19 -05:00
msg: &messages::substrate::CoordinatorMessage,
) {
_public_db::Canonical::send(txn, network, msg);
}
/// Try to receive a canonical event, returning `None` if there is none to receive.
pub fn try_recv(
txn: &mut impl DbTxn,
2025-01-30 03:14:24 -05:00
network: ExternalNetworkId,
2024-12-31 10:37:19 -05:00
) -> Option<messages::substrate::CoordinatorMessage> {
_public_db::Canonical::try_recv(txn, network)
}
}
/// The channel for new set events emitted by an ephemeral event stream.
pub struct NewSet;
impl NewSet {
pub(crate) fn send(txn: &mut impl DbTxn, msg: &NewSetInformation) {
_public_db::NewSet::send(txn, msg);
}
/// Try to receive a new set's information, returning `None` if there is none to receive.
pub fn try_recv(txn: &mut impl DbTxn) -> Option<NewSetInformation> {
_public_db::NewSet::try_recv(txn)
}
}
/// The channel for notifications to sign a slash report, as emitted by an ephemeral event stream.
///
/// These notifications MAY be for irrelevant validator sets. The only guarantee is the
/// notifications for all relevant validator sets will be included.
2024-12-31 10:37:19 -05:00
pub struct SignSlashReport;
impl SignSlashReport {
2025-01-30 03:14:24 -05:00
pub(crate) fn send(txn: &mut impl DbTxn, set: ExternalValidatorSet) {
2025-01-11 06:51:55 -05:00
_public_db::SignSlashReport::send(txn, set, &());
2024-12-31 10:37:19 -05:00
}
/// Try to receive a notification to sign a slash report, returning `None` if there is none to
/// receive.
2025-01-30 03:14:24 -05:00
pub fn try_recv(txn: &mut impl DbTxn, set: ExternalValidatorSet) -> Option<()> {
2025-01-11 06:51:55 -05:00
_public_db::SignSlashReport::try_recv(txn, set)
2024-12-31 10:37:19 -05:00
}
}
2025-01-14 01:58:26 -05:00
/// The keys to set on Serai.
pub struct Keys;
impl Keys {
/// Set the keys to report for a validator set.
///
/// This only saves the most recent keys as only a single session is eligible to have its keys
/// reported at once.
pub fn set(
txn: &mut impl DbTxn,
2025-01-30 03:14:24 -05:00
set: ExternalValidatorSet,
2025-01-14 01:58:26 -05:00
key_pair: KeyPair,
signature_participants: bitvec::vec::BitVec<u8, bitvec::order::Lsb0>,
signature: Signature,
) {
// If we have a more recent pair of keys, don't write this historic one
if let Some((existing_session, _)) = _public_db::Keys::get(txn, set.network) {
if existing_session.0 >= set.session.0 {
return;
}
}
let tx = serai_client_serai::ValidatorSets::set_keys(
2025-01-14 01:58:26 -05:00
set.network,
key_pair,
signature_participants,
signature,
);
_public_db::Keys::set(txn, set.network, &(set.session, tx));
2025-01-14 01:58:26 -05:00
}
2025-01-30 03:14:24 -05:00
pub(crate) fn take(
txn: &mut impl DbTxn,
network: ExternalNetworkId,
) -> Option<(Session, Transaction)> {
_public_db::Keys::take(txn, network)
2025-01-14 01:58:26 -05:00
}
}
/// The signed batches to publish onto Serai.
pub struct SignedBatches;
impl SignedBatches {
/// Send a `SignedBatch` to publish onto Serai.
pub fn send(txn: &mut impl DbTxn, batch: &SignedBatch) {
_public_db::SignedBatches::send(txn, batch.batch.network(), batch);
2025-01-14 01:58:26 -05:00
}
2025-01-30 03:14:24 -05:00
pub(crate) fn try_recv(txn: &mut impl DbTxn, network: ExternalNetworkId) -> Option<SignedBatch> {
2025-01-14 01:58:26 -05:00
_public_db::SignedBatches::try_recv(txn, network)
}
}
/// The slash reports to publish onto Serai.
pub struct SlashReports;
impl SlashReports {
/// Set the slashes to report for a validator set.
///
/// This only saves the most recent slashes as only a single session is eligible to have its
/// slashes reported at once.
pub fn set(
txn: &mut impl DbTxn,
2025-01-30 03:14:24 -05:00
set: ExternalValidatorSet,
slash_report: SlashReport,
2025-01-14 01:58:26 -05:00
signature: Signature,
) {
2025-01-14 01:58:26 -05:00
// If we have a more recent slash report, don't write this historic one
if let Some((existing_session, _)) = _public_db::SlashReports::get(txn, set.network) {
if existing_session.0 >= set.session.0 {
return;
2025-01-14 01:58:26 -05:00
}
}
let tx =
serai_client_serai::ValidatorSets::report_slashes(set.network, slash_report, signature);
_public_db::SlashReports::set(txn, set.network, &(set.session, tx));
2025-01-14 01:58:26 -05:00
}
2025-01-30 03:14:24 -05:00
pub(crate) fn take(
txn: &mut impl DbTxn,
network: ExternalNetworkId,
) -> Option<(Session, Transaction)> {
_public_db::SlashReports::take(txn, network)
2025-01-14 01:58:26 -05:00
}
}