1use dusk_bytes::Serializable as DuskSerializeble;
8use dusk_core::signatures::bls::{
9 self as core_bls, Error as BlsSigError,
10 MultisigPublicKey as BlsMultisigPublicKey,
11 MultisigSignature as BlsMultisigSignature,
12};
13use dusk_core::stake::EPOCH;
14use thiserror::Error;
15use tracing::error;
16
17use super::*;
18use crate::bls::PublicKey;
19use crate::hard_fork::bls_version_at;
20use crate::message::payload::{
21 Candidate, Ratification, RatificationResult, Validation, Vote,
22};
23use crate::message::{ConsensusHeader, SignInfo, SignedStepMessage};
24
25#[derive(Debug, Clone)]
26#[cfg_attr(any(feature = "faker", test), derive(fake::Dummy, Eq, PartialEq))]
27pub enum Fault {
28 DoubleCandidate(FaultData<Hash>, FaultData<Hash>),
29 DoubleRatificationVote(FaultData<Vote>, FaultData<Vote>),
30 DoubleValidationVote(FaultData<Vote>, FaultData<Vote>),
31}
32
33impl Fault {
34 pub fn size(&self) -> usize {
35 const FAULT_CONSENSUS_HEADER_SIZE: usize = 32 + u64::SIZE + u8::SIZE;
37 const FAULT_SIG_INFO_SIZE: usize =
39 BlsMultisigPublicKey::SIZE + BlsMultisigSignature::SIZE;
40
41 const HEADERS: usize = FAULT_CONSENSUS_HEADER_SIZE * 2;
42 const SIG_INFOS: usize = FAULT_SIG_INFO_SIZE * 2;
43 let faults_data_size = match self {
44 Fault::DoubleCandidate(..) => 32 * 2,
45 Fault::DoubleRatificationVote(a, b) => {
46 a.data.size() + b.data.size()
47 }
48 Fault::DoubleValidationVote(a, b) => a.data.size() + b.data.size(),
49 };
50
51 HEADERS + SIG_INFOS + faults_data_size
52 }
53}
54
55#[derive(Debug, Error)]
56pub enum InvalidFault {
57 #[error("Inner faults have same data")]
58 Duplicated,
59 #[error("Fault is expired")]
60 Expired,
61 #[error("Fault is from future")]
62 Future,
63 #[error("Fault is from genesis block")]
64 Genesis,
65 #[error("Previous hash mismatch")]
66 PrevHashMismatch,
67 #[error("Iteration mismatch")]
68 IterationMismatch,
69 #[error("Faults related to emergency iteration")]
70 EmergencyIteration,
71 #[error("Round mismatch")]
72 RoundMismatch,
73 #[error("Inner faults signer mismatch")]
74 SignerMismatch,
75 #[error("Invalid Signature {0}")]
76 InvalidSignature(BlsSigError),
77 #[error("Generic error {0}")]
78 Other(String),
79}
80
81impl From<BlsSigError> for InvalidFault {
82 fn from(value: BlsSigError) -> Self {
83 Self::InvalidSignature(value)
84 }
85}
86
87impl Fault {
88 pub fn id(&self) -> [u8; 32] {
91 self.digest()
92 }
93
94 pub fn digest(&self) -> [u8; 32] {
96 let mut b = vec![];
97 self.write(&mut b).expect("Write to a vec shall not fail");
98 sha3::Sha3_256::digest(&b[..]).into()
99 }
100
101 pub fn same(&self, other: &Fault) -> bool {
102 let (a, b) = &self.faults_id();
103 other.has_id(a) && other.has_id(b)
104 }
105
106 fn has_id(&self, id: &[u8; 32]) -> bool {
107 let (a, b) = &self.faults_id();
108 a == id || b == id
109 }
110
111 fn faults_id(&self) -> (Hash, Hash) {
113 match self {
114 Fault::DoubleCandidate(a, b) => {
115 let seed = Candidate::SIGN_SEED;
116 let a = sha3::Sha3_256::digest(a.get_signed_data(seed)).into();
117 let b = sha3::Sha3_256::digest(b.get_signed_data(seed)).into();
118 (a, b)
119 }
120 Fault::DoubleRatificationVote(a, b) => {
121 let seed = Ratification::SIGN_SEED;
122 let a = sha3::Sha3_256::digest(a.get_signed_data(seed)).into();
123 let b = sha3::Sha3_256::digest(b.get_signed_data(seed)).into();
124 (a, b)
125 }
126 Fault::DoubleValidationVote(a, b) => {
127 let seed = Validation::SIGN_SEED;
128 let a = sha3::Sha3_256::digest(a.get_signed_data(seed)).into();
129 let b = sha3::Sha3_256::digest(b.get_signed_data(seed)).into();
130 (a, b)
131 }
132 }
133 }
134
135 fn to_culprit(&self) -> PublicKey {
136 match self {
137 Fault::DoubleRatificationVote(a, _)
138 | Fault::DoubleValidationVote(a, _) => a.sig.signer.clone(),
139 Fault::DoubleCandidate(a, _) => a.sig.signer.clone(),
140 }
141 }
142
143 fn consensus_header(&self) -> (&ConsensusHeader, &ConsensusHeader) {
145 match self {
146 Fault::DoubleRatificationVote(a, b)
147 | Fault::DoubleValidationVote(a, b) => (&a.header, &b.header),
148 Fault::DoubleCandidate(a, b) => (&a.header, &b.header),
149 }
150 }
151
152 fn signers(&self) -> (&PublicKey, &PublicKey) {
154 match self {
158 Fault::DoubleRatificationVote(a, b)
159 | Fault::DoubleValidationVote(a, b) => {
160 (&a.sig.signer, &b.sig.signer)
161 }
162 Fault::DoubleCandidate(a, b) => (&a.sig.signer, &b.sig.signer),
163 }
164 }
165
166 pub fn validate(
171 &self,
172 current_height: u64,
173 ) -> Result<&ConsensusHeader, InvalidFault> {
174 let (h1, h2) = self.consensus_header();
175 if h1.iteration != h2.iteration {
177 return Err(InvalidFault::IterationMismatch);
178 }
179 if h1.round != h2.round {
180 return Err(InvalidFault::RoundMismatch);
181 }
182 if h1.prev_block_hash != h2.prev_block_hash {
183 return Err(InvalidFault::PrevHashMismatch);
184 }
185
186 let (s1, s2) = self.signers();
188 if s1 != s2 {
189 return Err(InvalidFault::SignerMismatch);
190 }
191
192 let (id_a, id_b) = self.faults_id();
194 if id_a == id_b {
195 return Err(InvalidFault::Duplicated);
196 }
197
198 if h1.round == 0 {
199 return Err(InvalidFault::Genesis);
200 }
201
202 if h1.round < current_height.saturating_sub(EPOCH) {
204 return Err(InvalidFault::Expired);
205 }
206
207 if h1.round > current_height {
209 return Err(InvalidFault::Future);
210 }
211
212 self.verify_sigs()?;
214
215 Ok(h1)
216 }
217
218 fn verify_sigs(&self) -> Result<(), BlsSigError> {
220 match self {
221 Fault::DoubleCandidate(a, b) => {
222 let seed = Candidate::SIGN_SEED;
223 let msg = a.get_signed_data(seed);
224 Self::verify_signature(a.header.round, &a.sig, &msg)?;
225 let msg = b.get_signed_data(seed);
226 Self::verify_signature(b.header.round, &b.sig, &msg)?;
227 Ok(())
228 }
229 Fault::DoubleRatificationVote(a, b) => {
230 let seed = Ratification::SIGN_SEED;
231 let msg = a.get_signed_data(seed);
232 Self::verify_signature(a.header.round, &a.sig, &msg)?;
233 let msg = b.get_signed_data(seed);
234 Self::verify_signature(b.header.round, &b.sig, &msg)?;
235 Ok(())
236 }
237 Fault::DoubleValidationVote(a, b) => {
238 let seed = Validation::SIGN_SEED;
239 let msg = a.get_signed_data(seed);
240 Self::verify_signature(a.header.round, &a.sig, &msg)?;
241 let msg = b.get_signed_data(seed);
242 Self::verify_signature(b.header.round, &b.sig, &msg)?;
243 Ok(())
244 }
245 }
246 }
247
248 fn verify_signature(
249 block_height: u64,
250 sign_info: &SignInfo,
251 msg: &[u8],
252 ) -> Result<(), BlsSigError> {
253 let bls_version = bls_version_at(block_height);
254 let sig =
255 BlsMultisigSignature::from_bytes(sign_info.signature.inner())?;
256 let apk =
257 core_bls::aggregate(&[*sign_info.signer.inner()], bls_version)?;
258 core_bls::verify_multisig(&apk, &sig, msg, bls_version)
259 }
260}
261
262impl FaultData<Hash> {
263 fn get_signed_data(&self, seed: &[u8]) -> Vec<u8> {
264 let mut signable = self.header.signable();
265 signable.extend_from_slice(seed);
266 signable.extend_from_slice(&self.data);
267 signable
268 }
269}
270impl FaultData<Vote> {
271 fn get_signed_data(&self, seed: &[u8]) -> Vec<u8> {
272 let mut signable = self.header.signable();
273 signable.extend_from_slice(seed);
274 self.data
275 .write(&mut signable)
276 .expect("Writing to vec should succeed");
277 signable
278 }
279}
280
281#[derive(Debug, Clone)]
282#[cfg_attr(any(feature = "faker", test), derive(fake::Dummy, Eq, PartialEq))]
283#[allow(clippy::large_enum_variant)]
284pub struct FaultData<V> {
285 header: ConsensusHeader,
286 sig: SignInfo,
287 data: V,
288}
289
290impl<V: Serializable> Serializable for FaultData<V> {
291 fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
292 self.header.write(w)?;
293 self.sig.write(w)?;
294 self.data.write(w)?;
295 Ok(())
296 }
297
298 fn read<R: Read>(r: &mut R) -> io::Result<Self>
299 where
300 Self: Sized,
301 {
302 let header = ConsensusHeader::read(r)?;
303 let sig = SignInfo::read(r)?;
304 let data = V::read(r)?;
305
306 Ok(Self { header, sig, data })
307 }
308}
309
310impl Serializable for Fault {
311 fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
312 match self {
313 Fault::DoubleCandidate(a, b) => {
314 w.write_all(&[0u8])?;
315 a.write(w)?;
316 b.write(w)?;
317 }
318 Fault::DoubleRatificationVote(a, b) => {
319 w.write_all(&[1u8])?;
320 a.write(w)?;
321 b.write(w)?;
322 }
323 Fault::DoubleValidationVote(a, b) => {
324 w.write_all(&[2u8])?;
325 a.write(w)?;
326 b.write(w)?;
327 }
328 }
329
330 Ok(())
331 }
332
333 fn read<R: Read>(r: &mut R) -> io::Result<Self>
334 where
335 Self: Sized,
336 {
337 let fault = Self::read_u8(r)?;
338 let fault = match fault {
339 0 => {
340 Fault::DoubleCandidate(FaultData::read(r)?, FaultData::read(r)?)
341 }
342 1 => Fault::DoubleRatificationVote(
343 FaultData::read(r)?,
344 FaultData::read(r)?,
345 ),
346 2 => Fault::DoubleValidationVote(
347 FaultData::read(r)?,
348 FaultData::read(r)?,
349 ),
350 p => Err(io::Error::new(
351 io::ErrorKind::InvalidData,
352 format!("Invalid fault: {p}"),
353 ))?,
354 };
355 Ok(fault)
356 }
357}
358
359#[derive(Clone, Debug)]
360pub struct Slash {
361 pub provisioner: PublicKey,
362 pub r#type: SlashType,
363}
364
365#[derive(Clone, Debug)]
366pub enum SlashType {
367 Soft,
368 Hard,
369 HardWithSeverity(u8),
370}
371
372impl Slash {
373 fn from_iteration_info(
374 value: &IterationInfo,
375 ) -> Result<Option<Self>, dusk_bytes::Error> {
376 let (attestation, provisioner) = value;
377 let slash = match attestation.result {
378 RatificationResult::Fail(Vote::NoCandidate) => SlashType::Soft,
379 RatificationResult::Fail(Vote::Invalid(_)) => SlashType::Hard,
380 _ => {
381 return Ok(None);
382 }
383 };
384 let provisioner =
385 (*provisioner.inner()).try_into().inspect_err(|e| {
386 error!(
387 "Unable to generate provisioners from IterationInfo: {e:?}"
388 );
389 })?;
390 Ok(Some(Self {
391 provisioner,
392 r#type: slash,
393 }))
394 }
395
396 pub fn from_block(block: &Block) -> Result<Vec<Slash>, io::Error> {
397 Self::from_iterations_and_faults(
398 &block.header().failed_iterations,
399 block.faults(),
400 )
401 }
402
403 pub fn from_iterations_and_faults(
404 failed_iterations: &IterationsInfo,
405 faults: &[Fault],
406 ) -> Result<Vec<Slash>, io::Error> {
407 let mut slashing = failed_iterations
408 .att_list
409 .iter()
410 .flatten()
411 .flat_map(Slash::from_iteration_info)
412 .flatten()
413 .collect::<Vec<_>>();
414 slashing.extend(faults.iter().map(Slash::from));
415 Ok(slashing)
416 }
417}
418
419impl From<&Fault> for Slash {
420 fn from(value: &Fault) -> Self {
421 let slash_type = match value {
422 Fault::DoubleCandidate(_, _)
423 | Fault::DoubleRatificationVote(_, _)
424 | Fault::DoubleValidationVote(_, _) => {
425 SlashType::HardWithSeverity(2u8)
426 }
427 };
428 let provisioner = value.to_culprit();
429 Self {
430 provisioner,
431 r#type: slash_type,
432 }
433 }
434}
435
436#[cfg(test)]
437mod tests {
438 use super::*;
439
440 fn header() -> ConsensusHeader {
441 ConsensusHeader {
442 prev_block_hash: [7; 32],
443 round: 10,
444 iteration: 1,
445 }
446 }
447
448 fn fault_data(signer_seed: u64, vote: Vote) -> FaultData<Vote> {
449 FaultData {
450 header: header(),
451 sig: SignInfo {
452 signer: PublicKey::from_sk_seed_u64(signer_seed),
453 signature: Signature::default(),
454 },
455 data: vote,
456 }
457 }
458
459 #[test]
460 fn validate_rejects_mismatched_signers() {
461 let a = fault_data(1, Vote::Valid([1; 32]));
462 let b = fault_data(2, Vote::Valid([2; 32]));
463 let fault = Fault::DoubleValidationVote(a, b);
464
465 let err = fault
466 .validate(10)
467 .expect_err("fault with mismatched signers must be rejected");
468 assert!(matches!(err, InvalidFault::SignerMismatch));
469 }
470
471 #[test]
472 fn validate_does_not_report_signer_mismatch_for_same_signer() {
473 let a = fault_data(1, Vote::Valid([1; 32]));
474 let b = fault_data(1, Vote::Valid([2; 32]));
475 let fault = Fault::DoubleValidationVote(a, b);
476
477 let err = fault.validate(10).expect_err(
478 "fault with same signer still has invalid test signatures",
479 );
480 assert!(matches!(err, InvalidFault::InvalidSignature(_)));
481 }
482}