unc_primitives/
challenge.rs

1use crate::hash::CryptoHash;
2use crate::merkle::MerklePath;
3use crate::sharding::{EncodedShardChunk, ShardChunk, ShardChunkHeader};
4use crate::types::AccountId;
5use crate::validator_signer::ValidatorSigner;
6use borsh::{BorshDeserialize, BorshSerialize};
7use unc_crypto::Signature;
8
9/// Serialized TrieNodeWithSize or state value.
10pub type TrieValue = std::sync::Arc<[u8]>;
11
12#[derive(BorshSerialize, BorshDeserialize, serde::Serialize, Debug, Clone, Eq, PartialEq)]
13/// TODO (#8984): consider supporting format containing trie values only for
14/// state part boundaries and storing state items for state part range.
15pub enum PartialState {
16    /// State represented by the set of unique trie values (`RawTrieNodeWithSize`s and state values).
17    TrieValues(Vec<TrieValue>),
18}
19
20impl Default for PartialState {
21    fn default() -> Self {
22        PartialState::TrieValues(vec![])
23    }
24}
25
26impl PartialState {
27    pub fn len(&self) -> usize {
28        let Self::TrieValues(values) = self;
29        values.len()
30    }
31}
32
33/// Double signed block.
34#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Clone, Debug)]
35pub struct BlockDoubleSign {
36    pub left_block_header: Vec<u8>,
37    pub right_block_header: Vec<u8>,
38}
39
40impl std::fmt::Display for BlockDoubleSign {
41    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
42        write!(f, "{:?}", self)
43    }
44}
45
46/// Invalid chunk (body of the chunk doesn't match proofs or invalid encoding).
47#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Clone, Debug)]
48pub struct ChunkProofs {
49    /// Encoded block header that contains invalid chunk.
50    pub block_header: Vec<u8>,
51    /// Merkle proof of inclusion of this chunk.
52    pub merkle_proof: MerklePath,
53    /// Invalid chunk in an encoded form or in a decoded form.
54    pub chunk: Box<MaybeEncodedShardChunk>,
55}
56
57/// Either `EncodedShardChunk` or `ShardChunk`. Used for `ChunkProofs`.
58/// `Decoded` is used to avoid re-encoding an already decoded chunk to construct a challenge.
59/// `Encoded` is still needed in case a challenge challenges an invalid encoded chunk that can't be
60/// decoded.
61#[allow(clippy::large_enum_variant)] // both variants are large
62#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Clone, Debug)]
63pub enum MaybeEncodedShardChunk {
64    Encoded(EncodedShardChunk),
65    Decoded(ShardChunk),
66}
67
68/// Doesn't match post-{state root, outgoing receipts, gas used, etc} results after applying previous chunk.
69#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Clone, Debug)]
70pub struct ChunkState {
71    /// Encoded prev block header.
72    pub prev_block_header: Vec<u8>,
73    /// Encoded block header that contains invalid chunnk.
74    pub block_header: Vec<u8>,
75    /// Merkle proof in inclusion of prev chunk.
76    pub prev_merkle_proof: MerklePath,
77    /// Previous chunk that contains transactions.
78    pub prev_chunk: ShardChunk,
79    /// Merkle proof of inclusion of this chunk.
80    pub merkle_proof: MerklePath,
81    /// Invalid chunk header.
82    pub chunk_header: ShardChunkHeader,
83    /// Partial state that was affected by transactions of given chunk.
84    pub partial_state: PartialState,
85}
86
87#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Clone, Debug)]
88// TODO(#1313): Use Box
89#[allow(clippy::large_enum_variant)]
90pub enum ChallengeBody {
91    BlockDoubleSign(BlockDoubleSign),
92    ChunkProofs(ChunkProofs),
93    ChunkState(ChunkState),
94}
95
96#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Clone, Debug)]
97#[borsh(init=init)]
98pub struct Challenge {
99    pub body: ChallengeBody,
100    pub account_id: AccountId,
101    pub signature: Signature,
102
103    #[borsh(skip)]
104    pub hash: CryptoHash,
105}
106
107impl Challenge {
108    pub fn init(&mut self) {
109        self.hash = CryptoHash::hash_borsh(&self.body);
110    }
111
112    pub fn produce(body: ChallengeBody, signer: &dyn ValidatorSigner) -> Self {
113        let (hash, signature) = signer.sign_challenge(&body);
114        Self { body, account_id: signer.validator_id().clone(), signature, hash }
115    }
116}
117
118pub type Challenges = Vec<Challenge>;
119
120#[derive(
121    BorshSerialize,
122    BorshDeserialize,
123    PartialEq,
124    Eq,
125    Clone,
126    Debug,
127    serde::Serialize,
128    serde::Deserialize,
129)]
130pub struct SlashedValidator {
131    pub account_id: AccountId,
132    pub is_double_sign: bool,
133}
134
135impl SlashedValidator {
136    pub fn new(account_id: AccountId, is_double_sign: bool) -> Self {
137        SlashedValidator { account_id, is_double_sign }
138    }
139}
140
141/// Result of checking challenge, contains which accounts to slash.
142/// If challenge is invalid this is sender, otherwise author of chunk (and possibly other participants that signed invalid blocks).
143pub type ChallengesResult = Vec<SlashedValidator>;