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