diff --git a/coordinator/src/tributary/db.rs b/coordinator/src/tributary/db.rs index 9a89139b..3a936f63 100644 --- a/coordinator/src/tributary/db.rs +++ b/coordinator/src/tributary/db.rs @@ -79,19 +79,24 @@ impl TributaryDb { } // If a validator has been fatally slashed - fn fatal_slash_key(genesis: [u8; 32]) -> Vec { - Self::tributary_key(b"fatal_slash", genesis) + fn fatal_slashes_key(genesis: [u8; 32]) -> Vec { + Self::tributary_key(b"fatal_slashes", genesis) } - pub fn set_fatally_slashed(txn: &mut D::Transaction<'_>, genesis: [u8; 32], id: [u8; 32]) { - let key = Self::fatal_slash_key(genesis); + fn fatally_slashed_key(account: [u8; 32]) -> Vec { + Self::tributary_key(b"fatally_slashed", account) + } + pub fn set_fatally_slashed(txn: &mut D::Transaction<'_>, genesis: [u8; 32], account: [u8; 32]) { + txn.put(Self::fatally_slashed_key(account), []); + + let key = Self::fatal_slashes_key(genesis); let mut existing = txn.get(&key).unwrap_or(vec![]); // Don't append if we already have it - if existing.chunks(32).any(|ex_id| ex_id == id) { + if existing.chunks(32).any(|existing| existing == account) { return; } - existing.extend(id); + existing.extend(account); txn.put(key, existing); } diff --git a/coordinator/src/tributary/handle.rs b/coordinator/src/tributary/handle.rs index 030e47d6..a1fe2939 100644 --- a/coordinator/src/tributary/handle.rs +++ b/coordinator/src/tributary/handle.rs @@ -7,7 +7,7 @@ use rand_core::SeedableRng; use rand_chacha::ChaCha20Rng; use transcript::{Transcript, RecommendedTranscript}; -use ciphersuite::{Ciphersuite, Ristretto}; +use ciphersuite::{group::GroupEncoding, Ciphersuite, Ristretto}; use frost::{ FrostError, dkg::{Participant, musig::musig}, @@ -235,6 +235,17 @@ pub fn generated_key_pair( DkgConfirmer::share(spec, key, attempt, preprocesses, key_pair) } +pub(crate) fn fatal_slash( + txn: &mut D::Transaction<'_>, + genesis: [u8; 32], + account: [u8; 32], + reason: &str, +) { + log::warn!("fatally slashing {}. reason: {}", hex::encode(account), reason); + TributaryDb::::set_fatally_slashed(txn, genesis, account); + // TODO: disconnect the node from network/ban from further participation in all Tributaries +} + pub(crate) async fn handle_application_tx< D: Db, Pro: Processors, @@ -253,20 +264,24 @@ pub(crate) async fn handle_application_tx< ) { let genesis = spec.genesis(); - let handle = |txn: &mut _, data_spec: &DataSpecification, bytes: Vec, signed: &Signed| { + let handle = |txn: &mut ::Transaction<'_>, + data_spec: &DataSpecification, + bytes: Vec, + signed: &Signed| { let Some(curr_attempt) = TributaryDb::::attempt(txn, genesis, data_spec.topic) else { - // TODO: Full slash - todo!(); + // Premature publication of a valid ID/publication of an invalid ID + fatal_slash::( + txn, + genesis, + signed.signer.to_bytes(), + "published data for ID without an attempt", + ); + return None; }; // If they've already published a TX for this attempt, slash - if let Some(data) = TributaryDb::::data(txn, genesis, data_spec, signed.signer) { - if data != bytes { - // TODO: Full slash - todo!(); - } - - // TODO: Slash + if let Some(_) = TributaryDb::::data(txn, genesis, data_spec, signed.signer) { + fatal_slash::(txn, genesis, signed.signer.to_bytes(), "published data multiple times"); return None; } @@ -275,9 +290,15 @@ pub(crate) async fn handle_application_tx< // TODO: Slash for being late return None; } + // If the attempt is greater, this is a premature publication, full slash if data_spec.attempt > curr_attempt { - // TODO: Full slash - todo!(); + fatal_slash::( + txn, + genesis, + signed.signer.to_bytes(), + "published data with an attempt which hasn't started", + ); + return None; } // TODO: We can also full slash if shares before all commitments, or share before the @@ -324,8 +345,8 @@ pub(crate) async fn handle_application_tx< Transaction::DkgShares { attempt, mut shares, confirmation_nonces, signed } => { if shares.len() != (usize::from(spec.n()) - 1) { - // TODO: Full slash - todo!(); + fatal_slash::(txn, genesis, signed.signer.to_bytes(), "invalid amount of DKG shares"); + return; } let sender_i = spec diff --git a/coordinator/src/tributary/scanner.rs b/coordinator/src/tributary/scanner.rs index 9fb7a80f..ed894928 100644 --- a/coordinator/src/tributary/scanner.rs +++ b/coordinator/src/tributary/scanner.rs @@ -22,7 +22,7 @@ use serai_db::DbTxn; use crate::{ Db, - tributary::handle::handle_application_tx, + tributary::handle::{fatal_slash, handle_application_tx}, processors::Processors, tributary::{TributaryDb, TributarySpec, Transaction}, P2p, @@ -84,9 +84,12 @@ async fn handle_block< // Since anything with evidence is fundamentally faulty behavior, not just temporal errors, // mark the node as fatally slashed - TributaryDb::::set_fatally_slashed(&mut txn, genesis, msgs.0.msg.sender); - - // TODO2: disconnect the node from network/ban from further participation in Tributary + fatal_slash::( + &mut txn, + genesis, + msgs.0.msg.sender, + &format!("invalid tendermint messages: {:?}", msgs), + ); } TributaryTransaction::Application(tx) => { handle_application_tx::(