tendermint/block/
commit_sig.rs1use tendermint_proto::google::protobuf::Timestamp;
4
5use crate::{account, prelude::*, Signature, Time};
6
7const ZERO_TIMESTAMP: Timestamp = Timestamp {
23 seconds: -62135596800,
24 nanos: 0,
25};
26
27#[derive(Clone, Debug, PartialEq, Eq)]
30pub enum CommitSig {
31 BlockIdFlagAbsent,
33 BlockIdFlagCommit {
35 validator_address: account::Id,
37 timestamp: Time,
39 signature: Option<Signature>,
41 },
42 BlockIdFlagNil {
44 validator_address: account::Id,
46 timestamp: Time,
48 signature: Option<Signature>,
50 },
51}
52
53impl CommitSig {
54 pub fn validator_address(&self) -> Option<account::Id> {
56 match self {
57 Self::BlockIdFlagCommit {
58 validator_address, ..
59 } => Some(*validator_address),
60 Self::BlockIdFlagNil {
61 validator_address, ..
62 } => Some(*validator_address),
63 _ => None,
64 }
65 }
66
67 pub fn is_absent(&self) -> bool {
69 self == &Self::BlockIdFlagAbsent
70 }
71
72 pub fn is_commit(&self) -> bool {
74 matches!(self, Self::BlockIdFlagCommit { .. })
75 }
76
77 pub fn is_nil(&self) -> bool {
79 matches!(self, Self::BlockIdFlagNil { .. })
80 }
81}
82
83tendermint_pb_modules! {
84 use super::{CommitSig, ZERO_TIMESTAMP};
85 use crate::{error::Error, prelude::*, Signature};
86
87 use pb::types::{BlockIdFlag, CommitSig as RawCommitSig};
88
89 impl TryFrom<RawCommitSig> for CommitSig {
90 type Error = Error;
91
92 fn try_from(value: RawCommitSig) -> Result<Self, Self::Error> {
93 if value.block_id_flag == BlockIdFlag::Absent as i32 {
94 if value.timestamp.is_some() {
95 let timestamp = value.timestamp.unwrap();
96 if timestamp.nanos != 0 || timestamp.seconds != -62135596800 {
98 return Err(Error::invalid_timestamp(
99 "absent commitsig has non-zero timestamp".to_string(),
100 ));
101 }
102 }
103
104 if !value.signature.is_empty() {
105 return Err(Error::invalid_signature(
106 "expected empty signature for absent commitsig".to_string(),
107 ));
108 }
109
110 return Ok(CommitSig::BlockIdFlagAbsent);
111 }
112
113 if value.block_id_flag == BlockIdFlag::Commit as i32 {
114 if value.signature.is_empty() {
115 return Err(Error::invalid_signature(
116 "expected non-empty signature for regular commitsig".to_string(),
117 ));
118 }
119
120 if value.validator_address.is_empty() {
121 return Err(Error::invalid_validator_address());
122 }
123
124 let timestamp = value
125 .timestamp
126 .ok_or_else(Error::missing_timestamp)?
127 .try_into()?;
128
129 return Ok(CommitSig::BlockIdFlagCommit {
130 validator_address: value.validator_address.try_into()?,
131 timestamp,
132 signature: Signature::new(value.signature)?,
133 });
134 }
135 if value.block_id_flag == BlockIdFlag::Nil as i32 {
136 if value.signature.is_empty() {
137 return Err(Error::invalid_signature(
138 "nil commitsig has no signature".to_string(),
139 ));
140 }
141 if value.validator_address.is_empty() {
142 return Err(Error::invalid_validator_address());
143 }
144 return Ok(CommitSig::BlockIdFlagNil {
145 validator_address: value.validator_address.try_into()?,
146 timestamp: value
147 .timestamp
148 .ok_or_else(Error::missing_timestamp)?
149 .try_into()?,
150 signature: Signature::new(value.signature)?,
151 });
152 }
153 Err(Error::block_id_flag())
154 }
155 }
156
157 impl From<CommitSig> for RawCommitSig {
158 fn from(commit: CommitSig) -> RawCommitSig {
159 match commit {
160 CommitSig::BlockIdFlagAbsent => RawCommitSig {
161 block_id_flag: BlockIdFlag::Absent as i32,
162 validator_address: Vec::new(),
163 timestamp: Some(ZERO_TIMESTAMP),
164 signature: Vec::new(),
165 },
166 CommitSig::BlockIdFlagNil {
167 validator_address,
168 timestamp,
169 signature,
170 } => RawCommitSig {
171 block_id_flag: BlockIdFlag::Nil as i32,
172 validator_address: validator_address.into(),
173 timestamp: Some(timestamp.into()),
174 signature: signature.map(|s| s.into_bytes()).unwrap_or_default(),
175 },
176 CommitSig::BlockIdFlagCommit {
177 validator_address,
178 timestamp,
179 signature,
180 } => RawCommitSig {
181 block_id_flag: BlockIdFlag::Commit as i32,
182 validator_address: validator_address.into(),
183 timestamp: Some(timestamp.into()),
184 signature: signature.map(|s| s.into_bytes()).unwrap_or_default(),
185 },
186 }
187 }
188 }
189
190 #[test]
191 #[cfg(test)]
192 fn test_block_id_flag_absent_serialization() {
193 let absent = CommitSig::BlockIdFlagAbsent;
194 let raw_absent = RawCommitSig::from(absent);
195 let expected = r#"{"block_id_flag":1,"validator_address":"","timestamp":"0001-01-01T00:00:00Z","signature":""}"#;
196 let output = serde_json::to_string(&raw_absent).unwrap();
197 assert_eq!(expected, &output);
198 }
199
200 #[test]
201 #[cfg(test)]
202 fn test_block_id_flag_absent_deserialization() {
203 let json = r#"{"block_id_flag":1,"validator_address":"","timestamp":"0001-01-01T00:00:00Z","signature":""}"#;
204 let raw_commit_sg = serde_json::from_str::<RawCommitSig>(json).unwrap();
205 let commit_sig = CommitSig::try_from(raw_commit_sg).unwrap();
206 assert_eq!(commit_sig, CommitSig::BlockIdFlagAbsent);
207 }
208}