Skip to main content

celestia_tendermint/block/
commit_sig.rs

1//! CommitSig within Commit
2
3use crate::{account, prelude::*, Signature, Time};
4
5/// CommitSig represents a signature of a validator.
6/// It's a part of the Commit and can be used to reconstruct the vote set given the validator set.
7#[derive(Clone, Debug, PartialEq, Eq)]
8pub enum CommitSig {
9    /// no vote was received from a validator.
10    BlockIdFlagAbsent,
11    /// voted for the Commit.BlockID.
12    BlockIdFlagCommit {
13        /// Validator address
14        validator_address: account::Id,
15        /// Timestamp of vote
16        timestamp: Time,
17        /// Signature of vote
18        signature: Option<Signature>,
19    },
20    /// voted for nil.
21    BlockIdFlagNil {
22        /// Validator address
23        validator_address: account::Id,
24        /// Timestamp of vote
25        timestamp: Time,
26        /// Signature of vote
27        signature: Option<Signature>,
28    },
29}
30
31impl CommitSig {
32    /// Get the address of this validator if a vote was received.
33    pub fn validator_address(&self) -> Option<account::Id> {
34        match self {
35            Self::BlockIdFlagCommit {
36                validator_address, ..
37            } => Some(*validator_address),
38            Self::BlockIdFlagNil {
39                validator_address, ..
40            } => Some(*validator_address),
41            _ => None,
42        }
43    }
44
45    /// Whether this signature is absent (no vote was received from validator)
46    pub fn is_absent(&self) -> bool {
47        self == &Self::BlockIdFlagAbsent
48    }
49
50    /// Whether this signature is a commit  (validator voted for the Commit.BlockId)
51    pub fn is_commit(&self) -> bool {
52        matches!(self, Self::BlockIdFlagCommit { .. })
53    }
54
55    /// Whether this signature is nil (validator voted for nil)
56    pub fn is_nil(&self) -> bool {
57        matches!(self, Self::BlockIdFlagNil { .. })
58    }
59}
60
61tendermint_pb_modules! {
62    use super::CommitSig;
63    use crate::{error::Error, prelude::*, Signature};
64    use num_traits::ToPrimitive;
65    use pb::types::{BlockIdFlag, CommitSig as RawCommitSig};
66
67    // Todo: https://github.com/informalsystems/tendermint-rs/issues/259 - CommitSig Timestamp can be zero time
68    // Todo: https://github.com/informalsystems/tendermint-rs/issues/260 - CommitSig validator address missing in Absent vote
69    impl TryFrom<RawCommitSig> for CommitSig {
70        type Error = Error;
71
72        fn try_from(value: RawCommitSig) -> Result<Self, Self::Error> {
73            if value.block_id_flag == BlockIdFlag::Absent.to_i32().unwrap() {
74                if value.timestamp.is_some() {
75                    let timestamp = value.timestamp.unwrap();
76                    // 0001-01-01T00:00:00.000Z translates to EPOCH-62135596800 seconds
77                    if timestamp.nanos != 0 || timestamp.seconds != -62135596800 {
78                        return Err(Error::invalid_timestamp(
79                            "absent commitsig has non-zero timestamp".to_string(),
80                        ));
81                    }
82                }
83
84                if !value.signature.is_empty() {
85                    return Err(Error::invalid_signature(
86                        "expected empty signature for absent commitsig".to_string(),
87                    ));
88                }
89
90                return Ok(CommitSig::BlockIdFlagAbsent);
91            }
92
93            if value.block_id_flag == BlockIdFlag::Commit.to_i32().unwrap() {
94                if value.signature.is_empty() {
95                    return Err(Error::invalid_signature(
96                        "expected non-empty signature for regular commitsig".to_string(),
97                    ));
98                }
99
100                if value.validator_address.is_empty() {
101                    return Err(Error::invalid_validator_address());
102                }
103
104                let timestamp = value
105                    .timestamp
106                    .ok_or_else(Error::missing_timestamp)?
107                    .try_into()?;
108
109                return Ok(CommitSig::BlockIdFlagCommit {
110                    validator_address: value.validator_address.try_into()?,
111                    timestamp,
112                    signature: Signature::new(value.signature)?,
113                });
114            }
115            if value.block_id_flag == BlockIdFlag::Nil.to_i32().unwrap() {
116                if value.signature.is_empty() {
117                    return Err(Error::invalid_signature(
118                        "nil commitsig has no signature".to_string(),
119                    ));
120                }
121                if value.validator_address.is_empty() {
122                    return Err(Error::invalid_validator_address());
123                }
124                return Ok(CommitSig::BlockIdFlagNil {
125                    validator_address: value.validator_address.try_into()?,
126                    timestamp: value
127                        .timestamp
128                        .ok_or_else(Error::missing_timestamp)?
129                        .try_into()?,
130                    signature: Signature::new(value.signature)?,
131                });
132            }
133            Err(Error::block_id_flag())
134        }
135    }
136
137    impl From<CommitSig> for RawCommitSig {
138        fn from(commit: CommitSig) -> RawCommitSig {
139            match commit {
140                CommitSig::BlockIdFlagAbsent => RawCommitSig {
141                    block_id_flag: BlockIdFlag::Absent.to_i32().unwrap(),
142                    validator_address: Vec::new(),
143                    timestamp: None,
144                    signature: Vec::new(),
145                },
146                CommitSig::BlockIdFlagNil {
147                    validator_address,
148                    timestamp,
149                    signature,
150                } => RawCommitSig {
151                    block_id_flag: BlockIdFlag::Nil.to_i32().unwrap(),
152                    validator_address: validator_address.into(),
153                    timestamp: Some(timestamp.into()),
154                    signature: signature.map(|s| s.to_bytes()).unwrap_or_default(),
155                },
156                CommitSig::BlockIdFlagCommit {
157                    validator_address,
158                    timestamp,
159                    signature,
160                } => RawCommitSig {
161                    block_id_flag: BlockIdFlag::Commit.to_i32().unwrap(),
162                    validator_address: validator_address.into(),
163                    timestamp: Some(timestamp.into()),
164                    signature: signature.map(|s| s.to_bytes()).unwrap_or_default(),
165                },
166            }
167        }
168    }
169}