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 {
157 Fault::DoubleRatificationVote(a, b)
158 | Fault::DoubleValidationVote(a, b) => {
159 (&a.sig.signer, &b.sig.signer)
160 }
161 Fault::DoubleCandidate(a, b) => (&a.sig.signer, &b.sig.signer),
162 }
163 }
164
165 pub fn validate(
170 &self,
171 current_height: u64,
172 ) -> Result<&ConsensusHeader, InvalidFault> {
173 let (h1, h2) = self.consensus_header();
174 if h1.iteration != h2.iteration {
176 return Err(InvalidFault::IterationMismatch);
177 }
178 if h1.round != h2.round {
179 return Err(InvalidFault::RoundMismatch);
180 }
181 if h1.prev_block_hash != h2.prev_block_hash {
182 return Err(InvalidFault::PrevHashMismatch);
183 }
184
185 let (s1, s2) = self.signers();
187 if s1 != s2 {
188 return Err(InvalidFault::SignerMismatch);
189 }
190
191 let (id_a, id_b) = self.faults_id();
193 if id_a == id_b {
194 return Err(InvalidFault::Duplicated);
195 }
196
197 if h1.round == 0 {
198 return Err(InvalidFault::Genesis);
199 }
200
201 if h1.round < current_height.saturating_sub(EPOCH) {
203 return Err(InvalidFault::Expired);
204 }
205
206 if h1.round > current_height {
208 return Err(InvalidFault::Future);
209 }
210
211 self.verify_sigs()?;
213
214 Ok(h1)
215 }
216
217 fn verify_sigs(&self) -> Result<(), BlsSigError> {
219 match self {
220 Fault::DoubleCandidate(a, b) => {
221 let seed = Candidate::SIGN_SEED;
222 let msg = a.get_signed_data(seed);
223 Self::verify_signature(a.header.round, &a.sig, &msg)?;
224 let msg = b.get_signed_data(seed);
225 Self::verify_signature(b.header.round, &b.sig, &msg)?;
226 Ok(())
227 }
228 Fault::DoubleRatificationVote(a, b) => {
229 let seed = Ratification::SIGN_SEED;
230 let msg = a.get_signed_data(seed);
231 Self::verify_signature(a.header.round, &a.sig, &msg)?;
232 let msg = b.get_signed_data(seed);
233 Self::verify_signature(b.header.round, &b.sig, &msg)?;
234 Ok(())
235 }
236 Fault::DoubleValidationVote(a, b) => {
237 let seed = Validation::SIGN_SEED;
238 let msg = a.get_signed_data(seed);
239 Self::verify_signature(a.header.round, &a.sig, &msg)?;
240 let msg = b.get_signed_data(seed);
241 Self::verify_signature(b.header.round, &b.sig, &msg)?;
242 Ok(())
243 }
244 }
245 }
246
247 fn verify_signature(
248 block_height: u64,
249 sign_info: &SignInfo,
250 msg: &[u8],
251 ) -> Result<(), BlsSigError> {
252 let bls_version = bls_version_at(block_height);
253 let sig =
254 BlsMultisigSignature::from_bytes(sign_info.signature.inner())?;
255 let apk =
256 core_bls::aggregate(&[*sign_info.signer.inner()], bls_version)?;
257 core_bls::verify_multisig(&apk, &sig, msg, bls_version)
258 }
259}
260
261impl FaultData<Hash> {
262 fn get_signed_data(&self, seed: &[u8]) -> Vec<u8> {
263 let mut signable = self.header.signable();
264 signable.extend_from_slice(seed);
265 signable.extend_from_slice(&self.data);
266 signable
267 }
268}
269impl FaultData<Vote> {
270 fn get_signed_data(&self, seed: &[u8]) -> Vec<u8> {
271 let mut signable = self.header.signable();
272 signable.extend_from_slice(seed);
273 self.data
274 .write(&mut signable)
275 .expect("Writing to vec should succeed");
276 signable
277 }
278}
279
280#[derive(Debug, Clone)]
281#[cfg_attr(any(feature = "faker", test), derive(fake::Dummy, Eq, PartialEq))]
282#[allow(clippy::large_enum_variant)]
283pub struct FaultData<V> {
284 header: ConsensusHeader,
285 sig: SignInfo,
286 data: V,
287}
288
289impl<V: Serializable> Serializable for FaultData<V> {
290 fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
291 self.header.write(w)?;
292 self.sig.write(w)?;
293 self.data.write(w)?;
294 Ok(())
295 }
296
297 fn read<R: Read>(r: &mut R) -> io::Result<Self>
298 where
299 Self: Sized,
300 {
301 let header = ConsensusHeader::read(r)?;
302 let sig = SignInfo::read(r)?;
303 let data = V::read(r)?;
304
305 Ok(Self { header, sig, data })
306 }
307}
308
309impl Serializable for Fault {
310 fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
311 match self {
312 Fault::DoubleCandidate(a, b) => {
313 w.write_all(&[0u8])?;
314 a.write(w)?;
315 b.write(w)?;
316 }
317 Fault::DoubleRatificationVote(a, b) => {
318 w.write_all(&[1u8])?;
319 a.write(w)?;
320 b.write(w)?;
321 }
322 Fault::DoubleValidationVote(a, b) => {
323 w.write_all(&[2u8])?;
324 a.write(w)?;
325 b.write(w)?;
326 }
327 }
328
329 Ok(())
330 }
331
332 fn read<R: Read>(r: &mut R) -> io::Result<Self>
333 where
334 Self: Sized,
335 {
336 let fault = Self::read_u8(r)?;
337 let fault = match fault {
338 0 => {
339 Fault::DoubleCandidate(FaultData::read(r)?, FaultData::read(r)?)
340 }
341 1 => Fault::DoubleRatificationVote(
342 FaultData::read(r)?,
343 FaultData::read(r)?,
344 ),
345 2 => Fault::DoubleValidationVote(
346 FaultData::read(r)?,
347 FaultData::read(r)?,
348 ),
349 p => Err(io::Error::new(
350 io::ErrorKind::InvalidData,
351 format!("Invalid fault: {p}"),
352 ))?,
353 };
354 Ok(fault)
355 }
356}
357
358#[derive(Clone, Debug)]
359pub struct Slash {
360 pub provisioner: PublicKey,
361 pub r#type: SlashType,
362}
363
364#[derive(Clone, Debug)]
365pub enum SlashType {
366 Soft,
367 Hard,
368 HardWithSeverity(u8),
369}
370
371impl Slash {
372 fn from_iteration_info(
373 value: &IterationInfo,
374 ) -> Result<Option<Self>, dusk_bytes::Error> {
375 let (attestation, provisioner) = value;
376 let slash = match attestation.result {
377 RatificationResult::Fail(Vote::NoCandidate) => SlashType::Soft,
378 RatificationResult::Fail(Vote::Invalid(_)) => SlashType::Hard,
379 _ => {
380 return Ok(None);
381 }
382 };
383 let provisioner =
384 (*provisioner.inner()).try_into().inspect_err(|e| {
385 error!(
386 "Unable to generate provisioners from IterationInfo: {e:?}"
387 );
388 })?;
389 Ok(Some(Self {
390 provisioner,
391 r#type: slash,
392 }))
393 }
394
395 pub fn from_block(block: &Block) -> Result<Vec<Slash>, io::Error> {
396 Self::from_iterations_and_faults(
397 &block.header().failed_iterations,
398 block.faults(),
399 )
400 }
401
402 pub fn from_iterations_and_faults(
403 failed_iterations: &IterationsInfo,
404 faults: &[Fault],
405 ) -> Result<Vec<Slash>, io::Error> {
406 let mut slashing = failed_iterations
407 .att_list
408 .iter()
409 .flatten()
410 .flat_map(Slash::from_iteration_info)
411 .flatten()
412 .collect::<Vec<_>>();
413 slashing.extend(faults.iter().map(Slash::from));
414 Ok(slashing)
415 }
416}
417
418impl From<&Fault> for Slash {
419 fn from(value: &Fault) -> Self {
420 let slash_type = match value {
421 Fault::DoubleCandidate(_, _)
422 | Fault::DoubleRatificationVote(_, _)
423 | Fault::DoubleValidationVote(_, _) => {
424 SlashType::HardWithSeverity(2u8)
425 }
426 };
427 let provisioner = value.to_culprit();
428 Self {
429 provisioner,
430 r#type: slash_type,
431 }
432 }
433}
434
435#[cfg(test)]
436mod tests {
437 use super::*;
438
439 fn header() -> ConsensusHeader {
440 ConsensusHeader {
441 prev_block_hash: [7; 32],
442 round: 10,
443 iteration: 1,
444 }
445 }
446
447 fn fault_data(signer_seed: u64, vote: Vote) -> FaultData<Vote> {
448 FaultData {
449 header: header(),
450 sig: SignInfo {
451 signer: PublicKey::from_sk_seed_u64(signer_seed),
452 signature: Signature::default(),
453 },
454 data: vote,
455 }
456 }
457
458 #[test]
459 fn validate_rejects_mismatched_signers() {
460 let a = fault_data(1, Vote::Valid([1; 32]));
461 let b = fault_data(2, Vote::Valid([2; 32]));
462 let fault = Fault::DoubleValidationVote(a, b);
463
464 let err = fault
465 .validate(10)
466 .expect_err("fault with mismatched signers must be rejected");
467 assert!(matches!(err, InvalidFault::SignerMismatch));
468 }
469
470 #[test]
471 fn validate_does_not_report_signer_mismatch_for_same_signer() {
472 let a = fault_data(1, Vote::Valid([1; 32]));
473 let b = fault_data(1, Vote::Valid([2; 32]));
474 let fault = Fault::DoubleValidationVote(a, b);
475
476 let err = fault.validate(10).expect_err(
477 "fault with same signer still has invalid test signatures",
478 );
479 assert!(matches!(err, InvalidFault::InvalidSignature(_)));
480 }
481}