cometbft_light_client_verifier/
types.rs

1//! Defines or just re-exports the main datatypes used by the light client.
2
3use cometbft::{
4    account::Id as TMAccountId,
5    block::{
6        header::Header as TMHeader, signed_header::SignedHeader as TMSignedHeader,
7        Commit as TMCommit,
8    },
9    chain::Id as ChainId,
10    trust_threshold::TrustThresholdFraction,
11    validator::{Info as TMValidatorInfo, Set as TMValidatorSet},
12};
13pub use cometbft::{block::Height, hash::Hash, time::Time};
14use derive_more::Display;
15use serde::{Deserialize, Serialize};
16
17use crate::prelude::*;
18
19/// Peer ID (public key) of a full node
20pub type PeerId = cometbft::node::Id;
21
22/// defines what fraction of the total voting power of a known
23/// and trusted validator set is sufficient for a commit to be
24/// accepted going forward.
25pub type TrustThreshold = TrustThresholdFraction;
26
27/// A header contains metadata about the block and about the
28/// consensus, as well as commitments to the data in the current block, the
29/// previous block, and the results returned by the application.
30pub type Header = TMHeader;
31
32/// Set of validators
33pub type ValidatorSet = TMValidatorSet;
34
35/// Info about a single validator
36pub type Validator = TMValidatorInfo;
37
38/// Validator address
39pub type ValidatorAddress = TMAccountId;
40
41/// A commit contains the justification (ie. a set of signatures)
42/// that a block was consensus, as committed by a set previous block of validators.
43pub type Commit = TMCommit;
44
45/// A signed header contains both a `Header` and its corresponding `Commit`.
46pub type SignedHeader = TMSignedHeader;
47
48/// A type alias for a `LightBlock`.
49pub type TrustedState = LightBlock;
50
51/// Verification status of a light block.
52#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
53pub enum Status {
54    /// The light block has failed verification.
55    Failed,
56    /// The light has not been verified yet.
57    Unverified,
58    /// The light block has been successfully verified.
59    Verified,
60    /// The light block has been successfully verified and has passed fork detection.
61    Trusted,
62}
63
64impl Status {
65    /// Return a slice of all the possible values for this enum.
66    pub fn iter() -> &'static [Self] {
67        use Status::*;
68        static ALL: &[Status] = &[Unverified, Verified, Trusted, Failed];
69        ALL
70    }
71
72    /// Returns the most trusted status between the two given one.
73    ///
74    /// From least to most trusted: `Failed`, `Unverified`, `Verified`, `Trusted`.
75    pub fn most_trusted(a: Self, b: Self) -> Self {
76        core::cmp::max(a, b)
77    }
78}
79
80/// Trusted block parameters needed for light client verification.
81pub struct TrustedBlockState<'a> {
82    pub chain_id: &'a ChainId,
83    pub header_time: Time,
84    pub height: Height,
85    pub next_validators: &'a ValidatorSet,
86    pub next_validators_hash: Hash,
87}
88
89/// Untrusted block parameters needed for light client verification.
90pub struct UntrustedBlockState<'a> {
91    pub signed_header: &'a SignedHeader,
92    pub validators: &'a ValidatorSet,
93    pub next_validators: Option<&'a ValidatorSet>,
94}
95
96impl<'a> UntrustedBlockState<'a> {
97    /// Convenience method to expose the height of the associated header.
98    pub fn height(&self) -> Height {
99        self.signed_header.header.height
100    }
101}
102
103/// A light block is the core data structure used by the light client.
104/// It records everything the light client needs to know about a block.
105#[derive(Clone, Debug, Display, PartialEq, Eq, Serialize, Deserialize)]
106#[display(fmt = "{self:?}")]
107pub struct LightBlock {
108    /// Header and commit of this block
109    pub signed_header: SignedHeader,
110    /// Validator set at the block height
111    #[serde(rename = "validator_set")]
112    pub validators: ValidatorSet,
113    /// Validator set at the next block height
114    #[serde(rename = "next_validator_set")]
115    pub next_validators: ValidatorSet,
116    /// The peer ID of the node that provided this block
117    pub provider: PeerId,
118}
119
120impl LightBlock {
121    /// Constructs a new light block
122    pub fn new(
123        signed_header: SignedHeader,
124        validators: ValidatorSet,
125        next_validators: ValidatorSet,
126        provider: PeerId,
127    ) -> LightBlock {
128        Self {
129            signed_header,
130            validators,
131            next_validators,
132            provider,
133        }
134    }
135
136    /// Returns the height of this block.
137    ///
138    /// ## Note
139    /// This is a shorthand for `block.signed_header.header.height`.
140    pub fn height(&self) -> Height {
141        self.signed_header.header.height
142    }
143
144    /// Returns the time at which this block was created.
145    ///
146    /// ## Note
147    /// This is a shorthand for `block.signed_header.header.time`.
148    pub fn time(&self) -> Time {
149        self.signed_header.header.time
150    }
151
152    /// Obtain the verification parameters for the light block when using it as
153    /// trusted state.
154    pub fn as_trusted_state(&self) -> TrustedBlockState<'_> {
155        TrustedBlockState {
156            chain_id: &self.signed_header.header.chain_id,
157            header_time: self.signed_header.header.time,
158            height: self.signed_header.header.height,
159            next_validators: &self.next_validators,
160            next_validators_hash: self.signed_header.header.next_validators_hash,
161        }
162    }
163
164    /// Obtain the verification parameters for the light block when using it as
165    /// untrusted state.
166    pub fn as_untrusted_state(&self) -> UntrustedBlockState<'_> {
167        UntrustedBlockState {
168            signed_header: &self.signed_header,
169            validators: &self.validators,
170            next_validators: Some(&self.next_validators),
171        }
172    }
173}
174
175/// Contains the local status information, like the latest height, latest block and valset hashes,
176/// list of of connected full nodes (primary and witnesses).
177#[derive(Clone, Debug, Display, PartialEq, Eq, Serialize, Deserialize)]
178#[display(fmt = "{self:?}")]
179pub struct LatestStatus {
180    /// The latest height we are trusting.
181    pub height: Option<u64>,
182    /// The latest block hash we are trusting.
183    #[serde(with = "cometbft::serializers::option_hash")]
184    pub block_hash: Option<Hash>,
185    /// The latest validator set we are trusting.
186    /// Note that this potentially did not yet sign a header yet.
187    #[serde(with = "cometbft::serializers::option_hash")]
188    pub valset_hash: Option<Hash>,
189    /// The list of fullnodes we are connected to, primary and witnesses.
190    pub connected_nodes: Vec<PeerId>,
191}
192
193impl LatestStatus {
194    /// Builds a new instance of this struct.
195    pub fn new(
196        height: Option<u64>,
197        block_hash: Option<Hash>,
198        valset_hash: Option<Hash>,
199        connected_nodes: Vec<PeerId>,
200    ) -> Self {
201        Self {
202            height,
203            block_hash,
204            valset_hash,
205            connected_nodes,
206        }
207    }
208}
209
210#[cfg(test)]
211mod tests {
212
213    mod status {
214        use Status::*;
215
216        use crate::{prelude::*, types::Status};
217
218        #[test]
219        fn ord_impl() {
220            assert!(Trusted > Verified);
221            assert!(Verified > Unverified);
222            assert!(Unverified > Failed);
223        }
224
225        #[test]
226        fn most_trusted() {
227            for (a, b) in cross(Status::iter()) {
228                if a > b {
229                    assert_eq!(Status::most_trusted(a, b), a);
230                } else {
231                    assert_eq!(Status::most_trusted(a, b), b);
232                }
233            }
234        }
235
236        fn cross<T>(xs: &[T]) -> Vec<(T, T)>
237        where
238            T: Copy,
239        {
240            xs.iter()
241                .copied()
242                .flat_map(|y| xs.iter().copied().map(move |x| (x, y)))
243                .collect()
244        }
245    }
246}