dusk_node_data/ledger/
faults.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4//
5// Copyright (c) DUSK NETWORK. All rights reserved.
6
7use 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        // prev_block_hash + round + iter
34        const FAULT_CONSENSUS_HEADER_SIZE: usize = 32 + u64::SIZE + u8::SIZE;
35        // signer + signature
36        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    // TODO: change to HEIGHT|TYPE|PROV_KEY once faults collection is
85    // implemented
86    pub fn id(&self) -> [u8; 32] {
87        self.digest()
88    }
89
90    /// Digest the serialized form
91    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    /// Return the IDs of the inner faults.
108    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    /// Get the ConsensusHeader related to the inner FaultDatas
140    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    /// Check if both faults are related to the same consensus header and
149    /// validate their signatures.
150    ///
151    /// Return the related ConsensusHeader
152    pub fn validate(
153        &self,
154        current_height: u64,
155    ) -> Result<&ConsensusHeader, InvalidFault> {
156        let (h1, h2) = self.consensus_header();
157        // Check that both consensus headers are the same
158        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        // Check that fault refers to different fault_data
169        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        // Check that fault is not expired. A fault expires after an epoch
179        if h1.round < current_height.saturating_sub(EPOCH) {
180            return Err(InvalidFault::Expired);
181        }
182
183        // Check that fault is related to something that is already processed
184        if h1.round > current_height {
185            return Err(InvalidFault::Future);
186        }
187
188        // Verify signatures
189        self.verify_sigs()?;
190
191        Ok(h1)
192    }
193
194    /// Check if both faults are signed properly
195    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}