celestia_core/
block.rs

1//! Blocks within the chains of a Tendermint network
2
3mod commit;
4pub mod commit_sig;
5mod data;
6pub mod header;
7mod height;
8mod id;
9mod meta;
10pub mod parts;
11mod round;
12pub mod signed_header;
13mod size;
14
15use celestia_core_proto::v0_34::types::Block as RawBlock;
16use serde::{Deserialize, Serialize};
17
18pub use self::{
19    commit::*,
20    commit_sig::*,
21    data::Data,
22    header::Header,
23    height::*,
24    id::{Id, ParseId},
25    meta::Meta,
26    round::*,
27    size::Size,
28};
29use crate::{error::Error, evidence, prelude::*};
30
31/// Blocks consist of a header, transactions, votes (the commit), and a list of
32/// evidence of malfeasance (i.e. signing conflicting votes).
33///
34/// <https://github.com/tendermint/spec/blob/d46cd7f573a2c6a2399fcab2cde981330aa63f37/spec/core/data_structures.md#block>
35// Default serialization - all fields serialize; used by /block endpoint
36#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
37#[non_exhaustive]
38#[serde(try_from = "RawBlock", into = "RawBlock")]
39pub struct Block {
40    /// Block header
41    pub header: Header,
42
43    /// Transaction data
44    pub data: Data,
45
46    /// Evidence of malfeasance
47    pub evidence: evidence::List,
48
49    /// Last commit
50    pub last_commit: Option<Commit>,
51}
52
53mod v0_34 {
54    use super::{Block, Commit, Header};
55    use crate::{prelude::*, Error};
56    use celestia_core_proto::v0_34::types::Block as RawBlock;
57    use celestia_core_proto::Protobuf;
58
59    impl Protobuf<RawBlock> for Block {}
60
61    impl TryFrom<RawBlock> for Block {
62        type Error = Error;
63
64        fn try_from(value: RawBlock) -> Result<Self, Self::Error> {
65            let header: Header = value.header.ok_or_else(Error::missing_header)?.try_into()?;
66            // if last_commit is Commit::Default, it is considered nil by Go.
67            let last_commit = value
68                .last_commit
69                .map(TryInto::try_into)
70                .transpose()?
71                .filter(|c| c != &Commit::default());
72            if last_commit.is_none() && header.height.value() != 1 {
73                return Err(Error::invalid_block(
74                    "last_commit is empty on non-first block".to_string(),
75                ));
76            }
77
78            // Todo: Figure out requirements.
79            // if last_commit.is_some() && header.height.value() == 1 {
80            //    return Err(Kind::InvalidFirstBlock.context("last_commit is not null on first
81            // height").into());
82            //}
83
84            Ok(Block {
85                header,
86                data: value.data.ok_or_else(Error::missing_data)?.try_into()?,
87                evidence: value
88                    .evidence
89                    .map(TryInto::try_into)
90                    .transpose()?
91                    .unwrap_or_default(),
92                last_commit,
93            })
94        }
95    }
96
97    impl From<Block> for RawBlock {
98        fn from(value: Block) -> Self {
99            RawBlock {
100                header: Some(value.header.into()),
101                data: Some(value.data.into()),
102                evidence: Some(value.evidence.into()),
103                last_commit: value.last_commit.map(Into::into),
104            }
105        }
106    }
107}
108
109impl Block {
110    /// constructor
111    pub fn new(
112        header: Header,
113        data: Data,
114        evidence: evidence::List,
115        last_commit: Option<Commit>,
116    ) -> Result<Self, Error> {
117        if last_commit.is_none() && header.height.value() != 1 {
118            return Err(Error::invalid_block(
119                "last_commit is empty on non-first block".to_string(),
120            ));
121        }
122        if last_commit.is_some() && header.height.value() == 1 {
123            return Err(Error::invalid_block(
124                "last_commit is filled on first block".to_string(),
125            ));
126        }
127        Ok(Block {
128            header,
129            data,
130            evidence,
131            last_commit,
132        })
133    }
134
135    /// Get header
136    pub fn header(&self) -> &Header {
137        &self.header
138    }
139
140    /// Get data
141    pub fn data(&self) -> &Data {
142        &self.data
143    }
144
145    /// Get evidence
146    pub fn evidence(&self) -> &evidence::List {
147        &self.evidence
148    }
149
150    /// Get last commit
151    pub fn last_commit(&self) -> &Option<Commit> {
152        &self.last_commit
153    }
154}