mirror of
https://github.com/serai-dex/serai.git
synced 2024-12-23 20:19:24 +00:00
SlashReport signing and signature publication
This commit is contained in:
parent
3cc7b49492
commit
46c12c0e66
8 changed files with 200 additions and 34 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -8731,6 +8731,7 @@ dependencies = [
|
||||||
"rand_core",
|
"rand_core",
|
||||||
"serai-db",
|
"serai-db",
|
||||||
"serai-in-instructions-primitives",
|
"serai-in-instructions-primitives",
|
||||||
|
"serai-primitives",
|
||||||
"serai-processor-frost-attempt-manager",
|
"serai-processor-frost-attempt-manager",
|
||||||
"serai-processor-messages",
|
"serai-processor-messages",
|
||||||
"serai-processor-primitives",
|
"serai-processor-primitives",
|
||||||
|
|
|
@ -7,9 +7,9 @@ use borsh::{BorshSerialize, BorshDeserialize};
|
||||||
use dkg::Participant;
|
use dkg::Participant;
|
||||||
|
|
||||||
use serai_primitives::BlockHash;
|
use serai_primitives::BlockHash;
|
||||||
use in_instructions_primitives::{Batch, SignedBatch};
|
|
||||||
use coins_primitives::OutInstructionWithBalance;
|
|
||||||
use validator_sets_primitives::{Session, KeyPair, Slash};
|
use validator_sets_primitives::{Session, KeyPair, Slash};
|
||||||
|
use coins_primitives::OutInstructionWithBalance;
|
||||||
|
use in_instructions_primitives::{Batch, SignedBatch};
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||||
pub struct SubstrateContext {
|
pub struct SubstrateContext {
|
||||||
|
@ -84,7 +84,7 @@ pub mod sign {
|
||||||
pub enum VariantSignId {
|
pub enum VariantSignId {
|
||||||
Cosign([u8; 32]),
|
Cosign([u8; 32]),
|
||||||
Batch(u32),
|
Batch(u32),
|
||||||
SlashReport([u8; 32]),
|
SlashReport(Session),
|
||||||
Transaction([u8; 32]),
|
Transaction([u8; 32]),
|
||||||
}
|
}
|
||||||
impl fmt::Debug for VariantSignId {
|
impl fmt::Debug for VariantSignId {
|
||||||
|
@ -94,10 +94,9 @@ pub mod sign {
|
||||||
f.debug_struct("VariantSignId::Cosign").field("0", &hex::encode(cosign)).finish()
|
f.debug_struct("VariantSignId::Cosign").field("0", &hex::encode(cosign)).finish()
|
||||||
}
|
}
|
||||||
Self::Batch(batch) => f.debug_struct("VariantSignId::Batch").field("0", &batch).finish(),
|
Self::Batch(batch) => f.debug_struct("VariantSignId::Batch").field("0", &batch).finish(),
|
||||||
Self::SlashReport(slash_report) => f
|
Self::SlashReport(session) => {
|
||||||
.debug_struct("VariantSignId::SlashReport")
|
f.debug_struct("VariantSignId::SlashReport").field("0", &session).finish()
|
||||||
.field("0", &hex::encode(slash_report))
|
}
|
||||||
.finish(),
|
|
||||||
Self::Transaction(tx) => {
|
Self::Transaction(tx) => {
|
||||||
f.debug_struct("VariantSignId::Transaction").field("0", &hex::encode(tx)).finish()
|
f.debug_struct("VariantSignId::Transaction").field("0", &hex::encode(tx)).finish()
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ pub use lifetime::LifetimeStage;
|
||||||
// Database schema definition and associated functions.
|
// Database schema definition and associated functions.
|
||||||
mod db;
|
mod db;
|
||||||
use db::ScannerGlobalDb;
|
use db::ScannerGlobalDb;
|
||||||
pub use db::{BatchesToSign, AcknowledgedBatches, CompletedEventualities};
|
pub use db::{Batches, BatchesToSign, AcknowledgedBatches, CompletedEventualities};
|
||||||
// Task to index the blockchain, ensuring we don't reorganize finalized blocks.
|
// Task to index the blockchain, ensuring we don't reorganize finalized blocks.
|
||||||
mod index;
|
mod index;
|
||||||
// Scans blocks for received coins.
|
// Scans blocks for received coins.
|
||||||
|
|
|
@ -31,6 +31,7 @@ frost-schnorrkel = { path = "../../crypto/schnorrkel", default-features = false
|
||||||
scale = { package = "parity-scale-codec", version = "3", 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"] }
|
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
|
||||||
|
|
||||||
|
serai-primitives = { path = "../../substrate/primitives", default-features = false, features = ["std"] }
|
||||||
serai-validator-sets-primitives = { path = "../../substrate/validator-sets/primitives", default-features = false, features = ["std"] }
|
serai-validator-sets-primitives = { path = "../../substrate/validator-sets/primitives", default-features = false, features = ["std"] }
|
||||||
serai-in-instructions-primitives = { path = "../../substrate/in-instructions/primitives", default-features = false, features = ["std"] }
|
serai-in-instructions-primitives = { path = "../../substrate/in-instructions/primitives", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,9 @@
|
||||||
|
use scale::Decode;
|
||||||
use serai_db::{DbTxn, Db};
|
use serai_db::{DbTxn, Db};
|
||||||
|
|
||||||
use primitives::task::ContinuallyRan;
|
use primitives::task::ContinuallyRan;
|
||||||
|
|
||||||
use crate::{
|
use crate::{db::*, Coordinator};
|
||||||
db::{
|
|
||||||
RegisteredKeys, CosignerToCoordinatorMessages, BatchSignerToCoordinatorMessages,
|
|
||||||
SlashReportSignerToCoordinatorMessages, TransactionSignerToCoordinatorMessages,
|
|
||||||
},
|
|
||||||
Coordinator,
|
|
||||||
};
|
|
||||||
|
|
||||||
mod db;
|
mod db;
|
||||||
|
|
||||||
|
@ -30,6 +25,7 @@ impl<D: Db, C: Coordinator> ContinuallyRan for CoordinatorTask<D, C> {
|
||||||
let mut iterated = false;
|
let mut iterated = false;
|
||||||
|
|
||||||
for session in RegisteredKeys::get(&self.db).unwrap_or(vec![]) {
|
for session in RegisteredKeys::get(&self.db).unwrap_or(vec![]) {
|
||||||
|
// Publish the messages generated by this key's signers
|
||||||
loop {
|
loop {
|
||||||
let mut txn = self.db.txn();
|
let mut txn = self.db.txn();
|
||||||
let Some(msg) = CosignerToCoordinatorMessages::try_recv(&mut txn, session) else {
|
let Some(msg) = CosignerToCoordinatorMessages::try_recv(&mut txn, session) else {
|
||||||
|
@ -93,6 +89,26 @@ impl<D: Db, C: Coordinator> ContinuallyRan for CoordinatorTask<D, C> {
|
||||||
|
|
||||||
txn.commit();
|
txn.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If this session signed its slash report, publish its signature
|
||||||
|
{
|
||||||
|
let mut txn = self.db.txn();
|
||||||
|
if let Some(slash_report_signature) = SlashReportSignature::try_recv(&mut txn, session) {
|
||||||
|
iterated = true;
|
||||||
|
|
||||||
|
self
|
||||||
|
.coordinator
|
||||||
|
.publish_slash_report_signature(
|
||||||
|
<_>::decode(&mut slash_report_signature.as_slice()).unwrap(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
format!("couldn't send slash report signature to the coordinator: {e:?}")
|
||||||
|
})?;
|
||||||
|
|
||||||
|
txn.commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Publish the Batches
|
// Publish the Batches
|
||||||
|
|
|
@ -17,6 +17,7 @@ db_channel! {
|
||||||
SignersGlobal {
|
SignersGlobal {
|
||||||
Cosign: (session: Session) -> (u64, [u8; 32]),
|
Cosign: (session: Session) -> (u64, [u8; 32]),
|
||||||
SlashReport: (session: Session) -> Vec<Slash>,
|
SlashReport: (session: Session) -> Vec<Slash>,
|
||||||
|
SlashReportSignature: (session: Session) -> Vec<u8>,
|
||||||
|
|
||||||
CoordinatorToCosignerMessages: (session: Session) -> CoordinatorMessage,
|
CoordinatorToCosignerMessages: (session: Session) -> CoordinatorMessage,
|
||||||
CosignerToCoordinatorMessages: (session: Session) -> ProcessorMessage,
|
CosignerToCoordinatorMessages: (session: Session) -> ProcessorMessage,
|
||||||
|
|
|
@ -10,8 +10,9 @@ use zeroize::Zeroizing;
|
||||||
use ciphersuite::{group::GroupEncoding, Ciphersuite, Ristretto};
|
use ciphersuite::{group::GroupEncoding, Ciphersuite, Ristretto};
|
||||||
use frost::dkg::{ThresholdCore, ThresholdKeys};
|
use frost::dkg::{ThresholdCore, ThresholdKeys};
|
||||||
|
|
||||||
|
use serai_primitives::Signature;
|
||||||
use serai_validator_sets_primitives::{Session, Slash};
|
use serai_validator_sets_primitives::{Session, Slash};
|
||||||
use serai_in_instructions_primitives::SignedBatch;
|
use serai_in_instructions_primitives::{Batch, SignedBatch};
|
||||||
|
|
||||||
use serai_db::{DbTxn, Db};
|
use serai_db::{DbTxn, Db};
|
||||||
|
|
||||||
|
@ -19,6 +20,7 @@ use messages::sign::{VariantSignId, ProcessorMessage, CoordinatorMessage};
|
||||||
|
|
||||||
use primitives::task::{Task, TaskHandle, ContinuallyRan};
|
use primitives::task::{Task, TaskHandle, ContinuallyRan};
|
||||||
use scheduler::{Transaction, SignableTransaction, TransactionFor};
|
use scheduler::{Transaction, SignableTransaction, TransactionFor};
|
||||||
|
use scanner::{ScannerFeed, Scheduler};
|
||||||
|
|
||||||
mod wrapped_schnorrkel;
|
mod wrapped_schnorrkel;
|
||||||
pub(crate) use wrapped_schnorrkel::WrappedSchnorrkelMachine;
|
pub(crate) use wrapped_schnorrkel::WrappedSchnorrkelMachine;
|
||||||
|
@ -31,6 +33,9 @@ use coordinator::CoordinatorTask;
|
||||||
mod batch;
|
mod batch;
|
||||||
use batch::BatchSignerTask;
|
use batch::BatchSignerTask;
|
||||||
|
|
||||||
|
mod slash_report;
|
||||||
|
use slash_report::SlashReportSignerTask;
|
||||||
|
|
||||||
mod transaction;
|
mod transaction;
|
||||||
use transaction::TransactionSignerTask;
|
use transaction::TransactionSignerTask;
|
||||||
|
|
||||||
|
@ -51,6 +56,12 @@ pub trait Coordinator: 'static + Send + Sync {
|
||||||
|
|
||||||
/// Publish a `SignedBatch`.
|
/// Publish a `SignedBatch`.
|
||||||
async fn publish_signed_batch(&mut self, batch: SignedBatch) -> Result<(), Self::EphemeralError>;
|
async fn publish_signed_batch(&mut self, batch: SignedBatch) -> Result<(), Self::EphemeralError>;
|
||||||
|
|
||||||
|
/// Publish a slash report's signature.
|
||||||
|
async fn publish_slash_report_signature(
|
||||||
|
&mut self,
|
||||||
|
signature: Signature,
|
||||||
|
) -> Result<(), Self::EphemeralError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An object capable of publishing a transaction.
|
/// An object capable of publishing a transaction.
|
||||||
|
@ -81,12 +92,17 @@ struct Tasks {
|
||||||
|
|
||||||
/// The signers used by a processor.
|
/// The signers used by a processor.
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
pub struct Signers<ST: SignableTransaction> {
|
pub struct Signers<S: ScannerFeed, Sch: Scheduler<S>> {
|
||||||
coordinator_handle: TaskHandle,
|
coordinator_handle: TaskHandle,
|
||||||
tasks: HashMap<Session, Tasks>,
|
tasks: HashMap<Session, Tasks>,
|
||||||
_ST: PhantomData<ST>,
|
_Sch: PhantomData<Sch>,
|
||||||
|
_S: PhantomData<S>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CiphersuiteFor<S, Sch> =
|
||||||
|
<<Sch as Scheduler<S>>::SignableTransaction as SignableTransaction>::Ciphersuite;
|
||||||
|
type SignableTransactionFor<S, Sch> = <Sch as Scheduler<S>>::SignableTransaction;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This is completely outside of consensus, so the worst that can happen is:
|
This is completely outside of consensus, so the worst that can happen is:
|
||||||
|
|
||||||
|
@ -99,14 +115,14 @@ pub struct Signers<ST: SignableTransaction> {
|
||||||
completion comes in *before* we registered a key, the signer will hold the signing protocol in
|
completion comes in *before* we registered a key, the signer will hold the signing protocol in
|
||||||
memory until the session is retired entirely.
|
memory until the session is retired entirely.
|
||||||
*/
|
*/
|
||||||
impl<ST: SignableTransaction> Signers<ST> {
|
impl<S: ScannerFeed, Sch: Scheduler<S>> Signers<S, Sch> {
|
||||||
/// Initialize the signers.
|
/// Initialize the signers.
|
||||||
///
|
///
|
||||||
/// This will spawn tasks for any historically registered keys.
|
/// This will spawn tasks for any historically registered keys.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
mut db: impl Db,
|
mut db: impl Db,
|
||||||
coordinator: impl Coordinator,
|
coordinator: impl Coordinator,
|
||||||
publisher: &impl TransactionPublisher<TransactionFor<ST>>,
|
publisher: &impl TransactionPublisher<TransactionFor<SignableTransactionFor<S, Sch>>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
/*
|
/*
|
||||||
On boot, perform any database cleanup which was queued.
|
On boot, perform any database cleanup which was queued.
|
||||||
|
@ -120,8 +136,7 @@ impl<ST: SignableTransaction> Signers<ST> {
|
||||||
let mut txn = db.txn();
|
let mut txn = db.txn();
|
||||||
for (session, external_key_bytes) in db::ToCleanup::get(&txn).unwrap_or(vec![]) {
|
for (session, external_key_bytes) in db::ToCleanup::get(&txn).unwrap_or(vec![]) {
|
||||||
let mut external_key_bytes = external_key_bytes.as_slice();
|
let mut external_key_bytes = external_key_bytes.as_slice();
|
||||||
let external_key =
|
let external_key = CiphersuiteFor::<S, Sch>::read_G(&mut external_key_bytes).unwrap();
|
||||||
<ST::Ciphersuite as Ciphersuite>::read_G(&mut external_key_bytes).unwrap();
|
|
||||||
assert!(external_key_bytes.is_empty());
|
assert!(external_key_bytes.is_empty());
|
||||||
|
|
||||||
// Drain the Batches to sign
|
// Drain the Batches to sign
|
||||||
|
@ -133,7 +148,12 @@ impl<ST: SignableTransaction> Signers<ST> {
|
||||||
|
|
||||||
// Drain the transactions to sign
|
// Drain the transactions to sign
|
||||||
// This will be fully populated by the scheduler before retiry
|
// This will be fully populated by the scheduler before retiry
|
||||||
while scheduler::TransactionsToSign::<ST>::try_recv(&mut txn, &external_key).is_some() {}
|
while scheduler::TransactionsToSign::<SignableTransactionFor<S, Sch>>::try_recv(
|
||||||
|
&mut txn,
|
||||||
|
&external_key,
|
||||||
|
)
|
||||||
|
.is_some()
|
||||||
|
{}
|
||||||
|
|
||||||
// Drain the completed Eventualities
|
// Drain the completed Eventualities
|
||||||
while scanner::CompletedEventualities::try_recv(&mut txn, &external_key).is_some() {}
|
while scanner::CompletedEventualities::try_recv(&mut txn, &external_key).is_some() {}
|
||||||
|
@ -170,11 +190,12 @@ impl<ST: SignableTransaction> Signers<ST> {
|
||||||
while !buf.is_empty() {
|
while !buf.is_empty() {
|
||||||
substrate_keys
|
substrate_keys
|
||||||
.push(ThresholdKeys::from(ThresholdCore::<Ristretto>::read(&mut buf).unwrap()));
|
.push(ThresholdKeys::from(ThresholdCore::<Ristretto>::read(&mut buf).unwrap()));
|
||||||
external_keys
|
external_keys.push(ThresholdKeys::from(
|
||||||
.push(ThresholdKeys::from(ThresholdCore::<ST::Ciphersuite>::read(&mut buf).unwrap()));
|
ThresholdCore::<CiphersuiteFor<S, Sch>>::read(&mut buf).unwrap(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Cosigner and slash report signers
|
// TODO: Cosigner
|
||||||
|
|
||||||
let (batch_task, batch_handle) = Task::new();
|
let (batch_task, batch_handle) = Task::new();
|
||||||
tokio::spawn(
|
tokio::spawn(
|
||||||
|
@ -187,9 +208,15 @@ impl<ST: SignableTransaction> Signers<ST> {
|
||||||
.continually_run(batch_task, vec![coordinator_handle.clone()]),
|
.continually_run(batch_task, vec![coordinator_handle.clone()]),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let (slash_report_task, slash_report_handle) = Task::new();
|
||||||
|
tokio::spawn(
|
||||||
|
SlashReportSignerTask::<_, S>::new(db.clone(), session, substrate_keys.clone())
|
||||||
|
.continually_run(slash_report_task, vec![coordinator_handle.clone()]),
|
||||||
|
);
|
||||||
|
|
||||||
let (transaction_task, transaction_handle) = Task::new();
|
let (transaction_task, transaction_handle) = Task::new();
|
||||||
tokio::spawn(
|
tokio::spawn(
|
||||||
TransactionSignerTask::<_, ST, _>::new(
|
TransactionSignerTask::<_, SignableTransactionFor<S, Sch>, _>::new(
|
||||||
db.clone(),
|
db.clone(),
|
||||||
publisher.clone(),
|
publisher.clone(),
|
||||||
session,
|
session,
|
||||||
|
@ -203,13 +230,13 @@ impl<ST: SignableTransaction> Signers<ST> {
|
||||||
Tasks {
|
Tasks {
|
||||||
cosigner: todo!("TODO"),
|
cosigner: todo!("TODO"),
|
||||||
batch: batch_handle,
|
batch: batch_handle,
|
||||||
slash_report: todo!("TODO"),
|
slash_report: slash_report_handle,
|
||||||
transaction: transaction_handle,
|
transaction: transaction_handle,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Self { coordinator_handle, tasks, _ST: PhantomData }
|
Self { coordinator_handle, tasks, _Sch: PhantomData, _S: PhantomData }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Register a set of keys to sign with.
|
/// Register a set of keys to sign with.
|
||||||
|
@ -220,7 +247,7 @@ impl<ST: SignableTransaction> Signers<ST> {
|
||||||
txn: &mut impl DbTxn,
|
txn: &mut impl DbTxn,
|
||||||
session: Session,
|
session: Session,
|
||||||
substrate_keys: Vec<ThresholdKeys<Ristretto>>,
|
substrate_keys: Vec<ThresholdKeys<Ristretto>>,
|
||||||
network_keys: Vec<ThresholdKeys<ST::Ciphersuite>>,
|
network_keys: Vec<ThresholdKeys<CiphersuiteFor<S, Sch>>>,
|
||||||
) {
|
) {
|
||||||
// Don't register already retired keys
|
// Don't register already retired keys
|
||||||
if Some(session.0) <= db::LatestRetiredSession::get(txn).map(|session| session.0) {
|
if Some(session.0) <= db::LatestRetiredSession::get(txn).map(|session| session.0) {
|
||||||
|
@ -246,7 +273,8 @@ impl<ST: SignableTransaction> Signers<ST> {
|
||||||
/// Retire the signers for a session.
|
/// Retire the signers for a session.
|
||||||
///
|
///
|
||||||
/// This MUST be called in order, for every session (even if we didn't register keys for this
|
/// This MUST be called in order, for every session (even if we didn't register keys for this
|
||||||
/// session).
|
/// session). This MUST only be called after slash report publication, or after that process
|
||||||
|
/// times out (not once the key is done with regards to the external network).
|
||||||
pub fn retire_session(
|
pub fn retire_session(
|
||||||
&mut self,
|
&mut self,
|
||||||
txn: &mut impl DbTxn,
|
txn: &mut impl DbTxn,
|
||||||
|
@ -324,7 +352,7 @@ impl<ST: SignableTransaction> Signers<ST> {
|
||||||
txn.commit();
|
txn.commit();
|
||||||
|
|
||||||
if let Some(tasks) = self.tasks.get(&session) {
|
if let Some(tasks) = self.tasks.get(&session) {
|
||||||
tasks.cosign.run_now();
|
tasks.cosigner.run_now();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -335,9 +363,9 @@ impl<ST: SignableTransaction> Signers<ST> {
|
||||||
&mut self,
|
&mut self,
|
||||||
mut txn: impl DbTxn,
|
mut txn: impl DbTxn,
|
||||||
session: Session,
|
session: Session,
|
||||||
slash_report: Vec<Slash>,
|
slash_report: &Vec<Slash>,
|
||||||
) {
|
) {
|
||||||
db::SlashReport::send(&mut txn, session, &slash_report);
|
db::SlashReport::send(&mut txn, session, slash_report);
|
||||||
txn.commit();
|
txn.commit();
|
||||||
|
|
||||||
if let Some(tasks) = self.tasks.get(&session) {
|
if let Some(tasks) = self.tasks.get(&session) {
|
||||||
|
|
120
processor/signers/src/slash_report.rs
Normal file
120
processor/signers/src/slash_report.rs
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
|
use ciphersuite::Ristretto;
|
||||||
|
use frost::dkg::ThresholdKeys;
|
||||||
|
|
||||||
|
use scale::Encode;
|
||||||
|
use serai_primitives::Signature;
|
||||||
|
use serai_validator_sets_primitives::{
|
||||||
|
Session, ValidatorSet, SlashReport as SlashReportStruct, report_slashes_message,
|
||||||
|
};
|
||||||
|
|
||||||
|
use serai_db::{DbTxn, Db};
|
||||||
|
|
||||||
|
use messages::sign::VariantSignId;
|
||||||
|
|
||||||
|
use primitives::task::ContinuallyRan;
|
||||||
|
use scanner::ScannerFeed;
|
||||||
|
|
||||||
|
use frost_attempt_manager::*;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
db::{
|
||||||
|
SlashReport, SlashReportSignature, CoordinatorToSlashReportSignerMessages,
|
||||||
|
SlashReportSignerToCoordinatorMessages,
|
||||||
|
},
|
||||||
|
WrappedSchnorrkelMachine,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fetches slash_reportes to sign and signs them.
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub(crate) struct SlashReportSignerTask<D: Db, S: ScannerFeed> {
|
||||||
|
db: D,
|
||||||
|
_S: PhantomData<S>,
|
||||||
|
|
||||||
|
session: Session,
|
||||||
|
keys: Vec<ThresholdKeys<Ristretto>>,
|
||||||
|
|
||||||
|
has_slash_report: bool,
|
||||||
|
attempt_manager: AttemptManager<D, WrappedSchnorrkelMachine>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D: Db, S: ScannerFeed> SlashReportSignerTask<D, S> {
|
||||||
|
pub(crate) fn new(db: D, session: Session, keys: Vec<ThresholdKeys<Ristretto>>) -> Self {
|
||||||
|
let attempt_manager = AttemptManager::new(
|
||||||
|
db.clone(),
|
||||||
|
session,
|
||||||
|
keys.first().expect("creating a slash_report signer with 0 keys").params().i(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Self { db, _S: PhantomData, session, keys, has_slash_report: false, attempt_manager }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl<D: Db, S: ScannerFeed> ContinuallyRan for SlashReportSignerTask<D, S> {
|
||||||
|
async fn run_iteration(&mut self) -> Result<bool, String> {
|
||||||
|
let mut iterated = false;
|
||||||
|
|
||||||
|
// Check for the slash report to sign
|
||||||
|
if !self.has_slash_report {
|
||||||
|
let mut txn = self.db.txn();
|
||||||
|
let Some(slash_report) = SlashReport::try_recv(&mut txn, self.session) else {
|
||||||
|
return Ok(false);
|
||||||
|
};
|
||||||
|
// We only commit this upon successfully signing this slash report
|
||||||
|
drop(txn);
|
||||||
|
iterated = true;
|
||||||
|
|
||||||
|
self.has_slash_report = true;
|
||||||
|
|
||||||
|
let mut machines = Vec::with_capacity(self.keys.len());
|
||||||
|
{
|
||||||
|
let message = report_slashes_message(
|
||||||
|
&ValidatorSet { network: S::NETWORK, session: self.session },
|
||||||
|
&SlashReportStruct(slash_report.try_into().unwrap()),
|
||||||
|
);
|
||||||
|
for keys in &self.keys {
|
||||||
|
machines.push(WrappedSchnorrkelMachine::new(keys.clone(), message.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut txn = self.db.txn();
|
||||||
|
for msg in self.attempt_manager.register(VariantSignId::SlashReport(self.session), machines) {
|
||||||
|
SlashReportSignerToCoordinatorMessages::send(&mut txn, self.session, &msg);
|
||||||
|
}
|
||||||
|
txn.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle any messages sent to us
|
||||||
|
loop {
|
||||||
|
let mut txn = self.db.txn();
|
||||||
|
let Some(msg) = CoordinatorToSlashReportSignerMessages::try_recv(&mut txn, self.session)
|
||||||
|
else {
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
iterated = true;
|
||||||
|
|
||||||
|
match self.attempt_manager.handle(msg) {
|
||||||
|
Response::Messages(msgs) => {
|
||||||
|
for msg in msgs {
|
||||||
|
SlashReportSignerToCoordinatorMessages::send(&mut txn, self.session, &msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Response::Signature { id, signature } => {
|
||||||
|
let VariantSignId::SlashReport(session) = id else {
|
||||||
|
panic!("SlashReportSignerTask signed a non-SlashReport")
|
||||||
|
};
|
||||||
|
assert_eq!(session, self.session);
|
||||||
|
// Drain the channel
|
||||||
|
SlashReport::try_recv(&mut txn, self.session).unwrap();
|
||||||
|
// Send the signature
|
||||||
|
SlashReportSignature::send(&mut txn, session, &Signature::from(signature).encode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
txn.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(iterated)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue