1use cometbft_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
83cometbft_old_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}
209
210mod v1 {
211 use super::{CommitSig, ZERO_TIMESTAMP};
212 use crate::{error::Error, prelude::*, Signature};
213 use cometbft_proto::types::v1::{self as pb, BlockIdFlag};
214
215 impl TryFrom<pb::CommitSig> for CommitSig {
216 type Error = Error;
217
218 fn try_from(value: pb::CommitSig) -> Result<Self, Self::Error> {
219 if value.block_id_flag == BlockIdFlag::Absent as i32 {
220 if value.timestamp.is_some() {
221 let timestamp = value.timestamp.unwrap();
222 if timestamp.nanos != 0 || timestamp.seconds != -62135596800 {
224 return Err(Error::invalid_timestamp(
225 "absent commitsig has non-zero timestamp".to_string(),
226 ));
227 }
228 }
229
230 if !value.signature.is_empty() {
231 return Err(Error::invalid_signature(
232 "expected empty signature for absent commitsig".to_string(),
233 ));
234 }
235
236 return Ok(CommitSig::BlockIdFlagAbsent);
237 }
238
239 if value.block_id_flag == BlockIdFlag::Commit as i32 {
240 if value.signature.is_empty() {
241 return Err(Error::invalid_signature(
242 "expected non-empty signature for regular commitsig".to_string(),
243 ));
244 }
245
246 if value.validator_address.is_empty() {
247 return Err(Error::invalid_validator_address());
248 }
249
250 let timestamp = value
251 .timestamp
252 .ok_or_else(Error::missing_timestamp)?
253 .try_into()?;
254
255 return Ok(CommitSig::BlockIdFlagCommit {
256 validator_address: value.validator_address.try_into()?,
257 timestamp,
258 signature: Signature::new(value.signature)?,
259 });
260 }
261 if value.block_id_flag == BlockIdFlag::Nil as i32 {
262 if value.signature.is_empty() {
263 return Err(Error::invalid_signature(
264 "nil commitsig has no signature".to_string(),
265 ));
266 }
267 if value.validator_address.is_empty() {
268 return Err(Error::invalid_validator_address());
269 }
270 return Ok(CommitSig::BlockIdFlagNil {
271 validator_address: value.validator_address.try_into()?,
272 timestamp: value
273 .timestamp
274 .ok_or_else(Error::missing_timestamp)?
275 .try_into()?,
276 signature: Signature::new(value.signature)?,
277 });
278 }
279 Err(Error::block_id_flag())
280 }
281 }
282
283 impl From<CommitSig> for pb::CommitSig {
284 fn from(commit: CommitSig) -> pb::CommitSig {
285 match commit {
286 CommitSig::BlockIdFlagAbsent => pb::CommitSig {
287 block_id_flag: BlockIdFlag::Absent as i32,
288 validator_address: Vec::new(),
289 timestamp: Some(ZERO_TIMESTAMP),
290 signature: Vec::new(),
291 },
292 CommitSig::BlockIdFlagNil {
293 validator_address,
294 timestamp,
295 signature,
296 } => pb::CommitSig {
297 block_id_flag: BlockIdFlag::Nil as i32,
298 validator_address: validator_address.into(),
299 timestamp: Some(timestamp.into()),
300 signature: signature.map(|s| s.into_bytes()).unwrap_or_default(),
301 },
302 CommitSig::BlockIdFlagCommit {
303 validator_address,
304 timestamp,
305 signature,
306 } => pb::CommitSig {
307 block_id_flag: BlockIdFlag::Commit as i32,
308 validator_address: validator_address.into(),
309 timestamp: Some(timestamp.into()),
310 signature: signature.map(|s| s.into_bytes()).unwrap_or_default(),
311 },
312 }
313 }
314 }
315
316 #[test]
317 #[cfg(test)]
318 fn test_block_id_flag_absent_serialization() {
319 let absent = CommitSig::BlockIdFlagAbsent;
320 let raw_absent = pb::CommitSig::from(absent);
321 let expected = r#"{"block_id_flag":1,"validator_address":"","timestamp":"0001-01-01T00:00:00Z","signature":""}"#;
322 let output = serde_json::to_string(&raw_absent).unwrap();
323 assert_eq!(expected, &output);
324 }
325
326 #[test]
327 #[cfg(test)]
328 fn test_block_id_flag_absent_deserialization() {
329 let json = r#"{"block_id_flag":1,"validator_address":"","timestamp":"0001-01-01T00:00:00Z","signature":""}"#;
330 let raw_commit_sg = serde_json::from_str::<pb::CommitSig>(json).unwrap();
331 let commit_sig = CommitSig::try_from(raw_commit_sg).unwrap();
332 assert_eq!(commit_sig, CommitSig::BlockIdFlagAbsent);
333 }
334}
335
336mod v1beta1 {
337 use super::{CommitSig, ZERO_TIMESTAMP};
338 use crate::{error::Error, prelude::*, Signature};
339 use cometbft_proto::types::v1beta1::{self as pb, BlockIdFlag};
340
341 impl TryFrom<pb::CommitSig> for CommitSig {
342 type Error = Error;
343
344 fn try_from(value: pb::CommitSig) -> Result<Self, Self::Error> {
345 if value.block_id_flag == BlockIdFlag::Absent as i32 {
346 if value.timestamp.is_some() {
347 let timestamp = value.timestamp.unwrap();
348 if timestamp.nanos != 0 || timestamp.seconds != -62135596800 {
350 return Err(Error::invalid_timestamp(
351 "absent commitsig has non-zero timestamp".to_string(),
352 ));
353 }
354 }
355
356 if !value.signature.is_empty() {
357 return Err(Error::invalid_signature(
358 "expected empty signature for absent commitsig".to_string(),
359 ));
360 }
361
362 return Ok(CommitSig::BlockIdFlagAbsent);
363 }
364
365 if value.block_id_flag == BlockIdFlag::Commit as i32 {
366 if value.signature.is_empty() {
367 return Err(Error::invalid_signature(
368 "expected non-empty signature for regular commitsig".to_string(),
369 ));
370 }
371
372 if value.validator_address.is_empty() {
373 return Err(Error::invalid_validator_address());
374 }
375
376 let timestamp = value
377 .timestamp
378 .ok_or_else(Error::missing_timestamp)?
379 .try_into()?;
380
381 return Ok(CommitSig::BlockIdFlagCommit {
382 validator_address: value.validator_address.try_into()?,
383 timestamp,
384 signature: Signature::new(value.signature)?,
385 });
386 }
387 if value.block_id_flag == BlockIdFlag::Nil as i32 {
388 if value.signature.is_empty() {
389 return Err(Error::invalid_signature(
390 "nil commitsig has no signature".to_string(),
391 ));
392 }
393 if value.validator_address.is_empty() {
394 return Err(Error::invalid_validator_address());
395 }
396 return Ok(CommitSig::BlockIdFlagNil {
397 validator_address: value.validator_address.try_into()?,
398 timestamp: value
399 .timestamp
400 .ok_or_else(Error::missing_timestamp)?
401 .try_into()?,
402 signature: Signature::new(value.signature)?,
403 });
404 }
405 Err(Error::block_id_flag())
406 }
407 }
408
409 impl From<CommitSig> for pb::CommitSig {
410 fn from(commit: CommitSig) -> pb::CommitSig {
411 match commit {
412 CommitSig::BlockIdFlagAbsent => pb::CommitSig {
413 block_id_flag: BlockIdFlag::Absent as i32,
414 validator_address: Vec::new(),
415 timestamp: Some(ZERO_TIMESTAMP),
416 signature: Vec::new(),
417 },
418 CommitSig::BlockIdFlagNil {
419 validator_address,
420 timestamp,
421 signature,
422 } => pb::CommitSig {
423 block_id_flag: BlockIdFlag::Nil as i32,
424 validator_address: validator_address.into(),
425 timestamp: Some(timestamp.into()),
426 signature: signature.map(|s| s.into_bytes()).unwrap_or_default(),
427 },
428 CommitSig::BlockIdFlagCommit {
429 validator_address,
430 timestamp,
431 signature,
432 } => pb::CommitSig {
433 block_id_flag: BlockIdFlag::Commit as i32,
434 validator_address: validator_address.into(),
435 timestamp: Some(timestamp.into()),
436 signature: signature.map(|s| s.into_bytes()).unwrap_or_default(),
437 },
438 }
439 }
440 }
441
442 #[test]
443 #[cfg(test)]
444 fn test_block_id_flag_absent_serialization() {
445 let absent = CommitSig::BlockIdFlagAbsent;
446 let raw_absent = pb::CommitSig::from(absent);
447 let expected = r#"{"block_id_flag":1,"validator_address":"","timestamp":"0001-01-01T00:00:00Z","signature":""}"#;
448 let output = serde_json::to_string(&raw_absent).unwrap();
449 assert_eq!(expected, &output);
450 }
451
452 #[test]
453 #[cfg(test)]
454 fn test_block_id_flag_absent_deserialization() {
455 let json = r#"{"block_id_flag":1,"validator_address":"","timestamp":"0001-01-01T00:00:00Z","signature":""}"#;
456 let raw_commit_sg = serde_json::from_str::<pb::CommitSig>(json).unwrap();
457 let commit_sig = CommitSig::try_from(raw_commit_sg).unwrap();
458 assert_eq!(commit_sig, CommitSig::BlockIdFlagAbsent);
459 }
460}