penumbra_sdk_proto/protobuf/
tendermint_compat.rs

1//! Facilities to help interoperate with [`tendermint`] and [`tendermint_rpc`] types.
2//
3//  NOTE: this submodule is tightly focused on helping `penumbra-tendermint-proxy` function.
4//  this is not an exhaustive pass at providing compatibility between all of the types in either
5//  library. accordingly, it is grouped by conversions needed for each RPC endpoint.
6
7use crate::util::tendermint_proxy::v1 as penumbra_sdk_pb;
8use anyhow::anyhow;
9
10// === get_tx ===
11
12impl From<tendermint_rpc::endpoint::tx::Response> for penumbra_sdk_pb::GetTxResponse {
13    fn from(
14        tendermint_rpc::endpoint::tx::Response {
15            hash,
16            height,
17            index,
18            tx_result,
19            tx,
20            proof: _,
21        }: tendermint_rpc::endpoint::tx::Response,
22    ) -> Self {
23        Self {
24            height: height.value(),
25            index: index as u64,
26            hash: hash.as_bytes().to_vec(),
27            tx_result: Some(tx_result.into()),
28            tx,
29        }
30    }
31}
32
33impl From<tendermint::abci::types::ExecTxResult> for penumbra_sdk_pb::TxResult {
34    fn from(
35        tendermint::abci::types::ExecTxResult {
36            log,
37            gas_wanted,
38            gas_used,
39            events,
40            code: _,
41            data: _,
42            info: _,
43            codespace: _,
44        }: tendermint::abci::types::ExecTxResult,
45    ) -> Self {
46        use tendermint::abci::Event;
47        Self {
48            log: log.to_string(),
49            // TODO: validation here, fix mismatch between i64 <> u64
50            gas_wanted: gas_wanted as u64,
51            gas_used: gas_used as u64,
52            tags: events
53                .into_iter()
54                .flat_map(|Event { attributes, .. }: Event| {
55                    attributes.into_iter().map(penumbra_sdk_pb::Tag::from)
56                })
57                .collect(),
58        }
59    }
60}
61
62impl From<tendermint::abci::EventAttribute> for penumbra_sdk_pb::Tag {
63    fn from(event_attr: tendermint::abci::EventAttribute) -> Self {
64        Self {
65            key: event_attr.key_bytes().into(),
66            value: event_attr.value_bytes().into(),
67            // TODO(kate): this was set to false previously, but it should probably use the
68            // index field from the tendermint object. for now, carry out a refactor and avoid
69            // changing behavior while doing so.
70            index: false,
71        }
72    }
73}
74
75// impl From<tendermint::abci::event::v0_37::EventAttribute> for penumbra_sdk_pb::Tag {
76//     fn from(
77//         tendermint::abci::event::v0_37::EventAttribute {
78//             key,
79//             value,
80//             index: _,
81//         }: tendermint::abci::EventAttribute,
82//     ) -> Self {
83//         Self {
84//             key: key.into_bytes(),
85//             value: value.into_bytes(),
86//             // TODO(kate): this was set to false previously, but it should probably use the
87//             // index field from the tendermint object. for now, carry out a refactor and avoid
88//             // changing behavior while doing so.
89//             index: false,
90//         }
91//     }
92// }
93
94// === broadcast_tx_async ===
95
96impl From<tendermint_rpc::endpoint::broadcast::tx_async::Response>
97    for penumbra_sdk_pb::BroadcastTxAsyncResponse
98{
99    fn from(
100        tendermint_rpc::endpoint::broadcast::tx_async::Response {
101            code,
102            data,
103            log,
104            hash,
105            ..
106        }: tendermint_rpc::endpoint::broadcast::tx_async::Response,
107    ) -> Self {
108        Self {
109            code: u32::from(code) as u64,
110            data: data.to_vec(),
111            log,
112            hash: hash.as_bytes().to_vec(),
113        }
114    }
115}
116
117// === broadcast_tx_sync ===
118
119impl From<tendermint_rpc::endpoint::broadcast::tx_sync::Response>
120    for penumbra_sdk_pb::BroadcastTxSyncResponse
121{
122    fn from(
123        tendermint_rpc::endpoint::broadcast::tx_sync::Response {
124            code,
125            data,
126            log,
127            hash,
128            ..
129        }: tendermint_rpc::endpoint::broadcast::tx_sync::Response,
130    ) -> Self {
131        Self {
132            code: u32::from(code) as u64,
133            data: data.to_vec(),
134            log,
135            hash: hash.as_bytes().to_vec(),
136        }
137    }
138}
139
140// === get_status ===
141
142impl From<tendermint_rpc::endpoint::status::Response> for penumbra_sdk_pb::GetStatusResponse {
143    fn from(
144        tendermint_rpc::endpoint::status::Response {
145            node_info,
146            sync_info,
147            validator_info,
148        }: tendermint_rpc::endpoint::status::Response,
149    ) -> Self {
150        Self {
151            node_info: Some(node_info.into()),
152            sync_info: Some(sync_info.into()),
153            validator_info: Some(validator_info.into()),
154        }
155    }
156}
157
158impl From<tendermint::node::Info> for crate::tendermint::p2p::DefaultNodeInfo {
159    fn from(
160        tendermint::node::Info {
161            protocol_version,
162            id,
163            listen_addr,
164            network,
165            version,
166            channels,
167            moniker,
168            other,
169        }: tendermint::node::Info,
170    ) -> Self {
171        Self {
172            protocol_version: Some(protocol_version.into()),
173            default_node_id: id.to_string(),
174            listen_addr: listen_addr.to_string(),
175            network: network.to_string(),
176            version: version.to_string(),
177            channels: channels.to_string().as_bytes().to_vec(),
178            moniker: moniker.to_string(),
179            other: Some(crate::tendermint::p2p::DefaultNodeInfoOther {
180                tx_index: match other.tx_index {
181                    tendermint::node::info::TxIndexStatus::On => "on".to_string(),
182                    tendermint::node::info::TxIndexStatus::Off => "off".to_string(),
183                },
184                rpc_address: other.rpc_address.to_string(),
185            }),
186        }
187    }
188}
189
190impl From<tendermint_rpc::endpoint::status::SyncInfo> for penumbra_sdk_pb::SyncInfo {
191    fn from(
192        tendermint_rpc::endpoint::status::SyncInfo {
193            latest_block_hash,
194            latest_app_hash,
195            latest_block_height,
196            latest_block_time,
197            catching_up,
198            earliest_block_hash: _,
199            earliest_app_hash: _,
200            earliest_block_height: _,
201            earliest_block_time: _,
202        }: tendermint_rpc::endpoint::status::SyncInfo,
203    ) -> Self {
204        // The tendermint-rs `Timestamp` type is a newtype wrapper
205        // around a `time::PrimitiveDateTime` however it's private so we
206        // have to use string parsing to get to the prost type we want :(
207        let latest_block_time =
208            chrono::DateTime::parse_from_rfc3339(latest_block_time.to_rfc3339().as_str())
209                .expect("timestamp should roundtrip to string");
210
211        Self {
212            latest_block_hash: latest_block_hash.to_string().as_bytes().to_vec(),
213            latest_app_hash: latest_app_hash.to_string().as_bytes().to_vec(),
214            latest_block_height: latest_block_height.value(),
215            latest_block_time: Some(pbjson_types::Timestamp {
216                seconds: latest_block_time.timestamp(),
217                nanos: latest_block_time.timestamp_subsec_nanos() as i32,
218            }),
219            catching_up,
220            // These don't exist in tendermint-rpc right now.
221            // earliest_app_hash: res.sync_info.earliest_app_hash.to_string().as_bytes().to_vec(),
222            // earliest_block_hash: res.sync_info.earliest_block_hash.to_string().as_bytes().to_vec(),
223            // earliest_block_height: res.sync_info.earliest_block_height.value(),
224            // earliest_block_time: Some(pbjson_types::Timestamp{
225            //     seconds: earliest_block_time.timestamp(),
226            //     nanos: earliest_block_time.timestamp_nanos() as i32,
227            // }),
228        }
229    }
230}
231
232impl From<tendermint::validator::Info> for crate::tendermint::types::Validator {
233    fn from(
234        tendermint::validator::Info {
235            address,
236            pub_key,
237            power,
238            proposer_priority,
239            name: _,
240        }: tendermint::validator::Info,
241    ) -> Self {
242        use crate::tendermint::crypto::{public_key::Sum::Ed25519, PublicKey};
243        Self {
244            address: address.to_string().as_bytes().to_vec(),
245            pub_key: Some(PublicKey {
246                sum: Some(Ed25519(pub_key.to_bytes().to_vec())),
247            }),
248            voting_power: power.into(),
249            proposer_priority: proposer_priority.into(),
250        }
251    }
252}
253
254impl From<tendermint::node::info::ProtocolVersionInfo> for crate::tendermint::p2p::ProtocolVersion {
255    fn from(
256        tendermint::node::info::ProtocolVersionInfo {
257                p2p,
258                block,
259                app
260            }: tendermint::node::info::ProtocolVersionInfo,
261    ) -> Self {
262        Self { p2p, block, app }
263    }
264}
265
266// === abci_query ===
267
268#[derive(Debug, thiserror::Error)]
269#[error("height '{height}' from tendermint overflowed i64, this should never happen")]
270pub struct HeightOverflowError {
271    height: u64,
272    #[source]
273    source: <i64 as TryFrom<u64>>::Error,
274}
275
276impl TryFrom<tendermint_rpc::endpoint::abci_query::AbciQuery>
277    for penumbra_sdk_pb::AbciQueryResponse
278{
279    type Error = HeightOverflowError;
280    fn try_from(
281        tendermint_rpc::endpoint::abci_query::AbciQuery {
282            code,
283            log,
284            info,
285            index,
286            key,
287            value,
288            proof,
289            height,
290            codespace,
291        }: tendermint_rpc::endpoint::abci_query::AbciQuery,
292    ) -> Result<Self, Self::Error> {
293        let proof_ops = proof.map(crate::tendermint::crypto::ProofOps::from);
294        let height = i64::try_from(height.value()).map_err(|source| HeightOverflowError {
295            height: height.value(),
296            source,
297        })?;
298        Ok(Self {
299            code: u32::from(code),
300            log,
301            info,
302            index,
303            key,
304            value,
305            proof_ops,
306            height,
307            codespace,
308        })
309    }
310}
311
312impl From<tendermint::merkle::proof::ProofOps> for crate::tendermint::crypto::ProofOps {
313    fn from(
314        tendermint::merkle::proof::ProofOps { ops }: tendermint::merkle::proof::ProofOps,
315    ) -> Self {
316        Self {
317            ops: ops
318                .into_iter()
319                .map(crate::tendermint::crypto::ProofOp::from)
320                .collect(),
321        }
322    }
323}
324
325impl From<tendermint::merkle::proof::ProofOp> for crate::tendermint::crypto::ProofOp {
326    fn from(
327        tendermint::merkle::proof::ProofOp {
328            field_type,
329            key,
330            data,
331        }: tendermint::merkle::proof::ProofOp,
332    ) -> Self {
333        Self {
334            r#type: field_type,
335            key,
336            data,
337        }
338    }
339}
340
341// === get_block_by_height ===
342
343impl TryFrom<tendermint_rpc::endpoint::block::Response>
344    for penumbra_sdk_pb::GetBlockByHeightResponse
345{
346    type Error = anyhow::Error;
347    fn try_from(
348        tendermint_rpc::endpoint::block::Response {
349                block,
350                block_id,
351            }: tendermint_rpc::endpoint::block::Response,
352    ) -> Result<Self, Self::Error> {
353        Ok(Self {
354            block: block.try_into().map(Some)?,
355            block_id: Some(block_id.into()),
356        })
357    }
358}
359impl TryFrom<tendermint::Block> for crate::tendermint::types::Block {
360    type Error = anyhow::Error;
361    fn try_from(
362        tendermint::Block {
363            header,
364            data,
365            evidence,
366            last_commit,
367            ..
368        }: tendermint::Block,
369    ) -> Result<Self, Self::Error> {
370        Ok(crate::tendermint::types::Block {
371            header: header.try_into().map(Some)?,
372            data: Some(crate::tendermint::types::Data { txs: data }),
373            evidence: evidence.try_into().map(Some)?,
374            last_commit: last_commit
375                .map(crate::tendermint::types::Commit::try_from)
376                .transpose()?,
377        })
378    }
379}
380
381impl TryFrom<crate::tendermint::types::PartSetHeader> for tendermint::block::parts::Header {
382    type Error = anyhow::Error;
383    fn try_from(
384        crate::tendermint::types::PartSetHeader { total, hash }: crate::tendermint::types::PartSetHeader,
385    ) -> Result<Self, Self::Error> {
386        Ok(Self::new(total, hash.try_into()?)?)
387    }
388}
389
390impl TryFrom<crate::tendermint::types::Header> for tendermint::block::Header {
391    type Error = anyhow::Error;
392    fn try_from(
393        crate::tendermint::types::Header {
394            version,
395            chain_id,
396            height,
397            time,
398            last_block_id,
399            last_commit_hash,
400            data_hash,
401            validators_hash,
402            next_validators_hash,
403            consensus_hash,
404            app_hash,
405            last_results_hash,
406            evidence_hash,
407            proposer_address,
408        }: crate::tendermint::types::Header,
409    ) -> Result<Self, Self::Error> {
410        Ok(Self {
411            version: tendermint::block::header::Version {
412                block: version.clone().ok_or(anyhow!("version"))?.block,
413                app: version.ok_or(anyhow!("version"))?.app,
414            },
415            chain_id: tendermint::chain::Id::try_from(chain_id)?,
416            height: tendermint::block::Height::try_from(height)?,
417            time: tendermint::Time::from_unix_timestamp(
418                time.clone().ok_or(anyhow!("time"))?.seconds,
419                time.clone()
420                    .ok_or(anyhow!("missing time"))?
421                    .nanos
422                    .try_into()?,
423            )?,
424            last_block_id: match last_block_id {
425                Some(last_block_id) => Some(tendermint::block::Id {
426                    hash: tendermint::Hash::try_from(last_block_id.hash)?,
427                    part_set_header: tendermint::block::parts::Header::try_from(
428                        last_block_id
429                            .part_set_header
430                            .ok_or(anyhow::anyhow!("bad part set header"))?,
431                    )?,
432                }),
433                None => None,
434            },
435            last_commit_hash: Some(last_commit_hash.try_into()?),
436            data_hash: Some(data_hash.try_into()?),
437            validators_hash: validators_hash.try_into()?,
438            next_validators_hash: next_validators_hash.try_into()?,
439            consensus_hash: consensus_hash.try_into()?,
440            app_hash: app_hash.try_into()?,
441            last_results_hash: Some(last_results_hash.try_into()?),
442            evidence_hash: Some(evidence_hash.try_into()?),
443            proposer_address: proposer_address.try_into()?,
444        })
445    }
446}
447
448impl TryFrom<tendermint::block::Header> for crate::tendermint::types::Header {
449    // TODO(kate): ideally this would not return a tonic status object, but we'll use this for
450    // now to avoid invasively refactoring this code.
451    type Error = tonic::Status;
452    fn try_from(
453        tendermint::block::Header {
454            version,
455            chain_id,
456            height,
457            time,
458            last_block_id,
459            last_commit_hash,
460            data_hash,
461            validators_hash,
462            next_validators_hash,
463            consensus_hash,
464            app_hash,
465            last_results_hash,
466            evidence_hash,
467            proposer_address,
468        }: tendermint::block::Header,
469    ) -> Result<Self, Self::Error> {
470        // The tendermint-rs `Timestamp` type is a newtype wrapper
471        // around a `time::PrimitiveDateTime` however it's private so we
472        // have to use string parsing to get to the prost type we want :(
473        let header_time = chrono::DateTime::parse_from_rfc3339(time.to_rfc3339().as_str())
474            .or_else(|_| {
475                Err(tonic::Status::invalid_argument(
476                    "timestamp should roundtrip to string",
477                ))
478            })?;
479        Ok(Self {
480            version: Some(crate::tendermint::version::Consensus {
481                block: version.block,
482                app: version.app,
483            }),
484            chain_id: chain_id.into(),
485            height: height.into(),
486            time: Some(pbjson_types::Timestamp {
487                seconds: header_time.timestamp(),
488                nanos: header_time.timestamp_subsec_nanos() as i32,
489            }),
490            last_block_id: last_block_id.map(|id| crate::tendermint::types::BlockId {
491                hash: id.hash.into(),
492                part_set_header: Some(crate::tendermint::types::PartSetHeader {
493                    total: id.part_set_header.total,
494                    hash: id.part_set_header.hash.into(),
495                }),
496            }),
497            last_commit_hash: last_commit_hash.map(Into::into).unwrap_or_default(),
498            data_hash: data_hash.map(Into::into).unwrap_or_default(),
499            validators_hash: validators_hash.into(),
500            next_validators_hash: next_validators_hash.into(),
501            consensus_hash: consensus_hash.into(),
502            app_hash: app_hash.into(),
503            last_results_hash: last_results_hash.map(Into::into).unwrap_or_default(),
504            evidence_hash: evidence_hash.map(Into::into).unwrap_or_default(),
505            proposer_address: proposer_address.into(),
506        })
507    }
508}
509
510impl TryFrom<tendermint::evidence::List> for crate::tendermint::types::EvidenceList {
511    type Error = anyhow::Error;
512    fn try_from(list: tendermint::evidence::List) -> Result<Self, Self::Error> {
513        list.into_vec()
514            .into_iter()
515            .map(crate::tendermint::types::Evidence::try_from)
516            .collect::<Result<Vec<_>, _>>()
517            .map(|evidence| Self { evidence })
518    }
519}
520
521// TODO(kate): this should be decomposed further at a later point, i am refraining from doing
522// so right now.
523impl TryFrom<tendermint::evidence::Evidence> for crate::tendermint::types::Evidence {
524    type Error = anyhow::Error;
525    fn try_from(evidence: tendermint::evidence::Evidence) -> Result<Self, Self::Error> {
526        use {chrono::DateTime, std::ops::Deref};
527        Ok(Self {
528            sum: Some(match evidence {
529                tendermint::evidence::Evidence::DuplicateVote(e) => {
530                    let e2 =
531                        tendermint_proto::types::DuplicateVoteEvidence::from(e.deref().clone());
532                    crate::tendermint::types::evidence::Sum::DuplicateVoteEvidence(
533                        crate::tendermint::types::DuplicateVoteEvidence {
534                            vote_a: Some(crate::tendermint::types::Vote {
535                                r#type: match e.votes().0.vote_type {
536                                    tendermint::vote::Type::Prevote => {
537                                        crate::tendermint::types::SignedMsgType::Prevote as i32
538                                    }
539                                    tendermint::vote::Type::Precommit => {
540                                        crate::tendermint::types::SignedMsgType::Precommit as i32
541                                    }
542                                },
543                                height: e.votes().0.height.into(),
544                                round: e.votes().0.round.into(),
545                                block_id: Some(crate::tendermint::types::BlockId {
546                                    hash: e
547                                        .votes()
548                                        .0
549                                        .block_id
550                                        .ok_or(anyhow!("block id"))?
551                                        .hash
552                                        .into(),
553                                    part_set_header: Some(
554                                        crate::tendermint::types::PartSetHeader {
555                                            total: e
556                                                .votes()
557                                                .0
558                                                .block_id
559                                                .ok_or(anyhow!("block id"))?
560                                                .part_set_header
561                                                .total,
562                                            hash: e
563                                                .votes()
564                                                .0
565                                                .block_id
566                                                .ok_or(anyhow!("block id"))?
567                                                .part_set_header
568                                                .hash
569                                                .into(),
570                                        },
571                                    ),
572                                }),
573                                timestamp: Some(pbjson_types::Timestamp {
574                                    seconds: DateTime::parse_from_rfc3339(
575                                        &e.votes()
576                                            .0
577                                            .timestamp
578                                            .ok_or(tonic::Status::invalid_argument(
579                                                "bad timestamp",
580                                            ))?
581                                            .to_rfc3339(),
582                                    )
583                                    .or_else(|_| {
584                                        Err(tonic::Status::invalid_argument("bad timestamp"))
585                                    })?
586                                    .timestamp(),
587                                    nanos: DateTime::parse_from_rfc3339(
588                                        &e.votes().0.timestamp.expect("timestamp").to_rfc3339(),
589                                    )
590                                    .expect("timestamp should roundtrip to string")
591                                    .timestamp_subsec_nanos()
592                                    .try_into()
593                                    .expect("good round trip timestamps"),
594                                }),
595                                validator_address: e.votes().0.validator_address.into(),
596                                validator_index: e.votes().0.validator_index.into(),
597                                signature: e
598                                    .votes()
599                                    .0
600                                    .signature
601                                    .clone()
602                                    .expect("signed vote")
603                                    .into(),
604                            }),
605                            vote_b: Some(crate::tendermint::types::Vote {
606                                r#type: match e.votes().1.vote_type {
607                                    tendermint::vote::Type::Prevote => {
608                                        crate::tendermint::types::SignedMsgType::Prevote as i32
609                                    }
610                                    tendermint::vote::Type::Precommit => {
611                                        crate::tendermint::types::SignedMsgType::Precommit as i32
612                                    }
613                                },
614                                height: e.votes().1.height.into(),
615                                round: e.votes().1.round.into(),
616                                block_id: Some(crate::tendermint::types::BlockId {
617                                    hash: e.votes().1.block_id.expect("block id").hash.into(),
618                                    part_set_header: Some(
619                                        crate::tendermint::types::PartSetHeader {
620                                            total: e
621                                                .votes()
622                                                .1
623                                                .block_id
624                                                .expect("block id")
625                                                .part_set_header
626                                                .total,
627                                            hash: e
628                                                .votes()
629                                                .1
630                                                .block_id
631                                                .expect("block id")
632                                                .part_set_header
633                                                .hash
634                                                .into(),
635                                        },
636                                    ),
637                                }),
638                                timestamp: Some(pbjson_types::Timestamp {
639                                    seconds: DateTime::parse_from_rfc3339(
640                                        &e.votes().1.timestamp.expect("timestamp").to_rfc3339(),
641                                    )
642                                    .expect("timestamp should roundtrip to string")
643                                    .timestamp(),
644                                    nanos: DateTime::parse_from_rfc3339(
645                                        &e.votes().1.timestamp.expect("timestamp").to_rfc3339(),
646                                    )
647                                    .expect("timestamp should roundtrip to string")
648                                    .timestamp_subsec_nanos()
649                                    .try_into()
650                                    .expect("good round trip timestamps"),
651                                }),
652                                validator_address: e.votes().1.validator_address.into(),
653                                validator_index: e.votes().1.validator_index.into(),
654                                signature: e
655                                    .votes()
656                                    .1
657                                    .signature
658                                    .clone()
659                                    .expect("signed vote")
660                                    .into(),
661                            }),
662                            total_voting_power: e2.total_voting_power,
663                            validator_power: e2.validator_power,
664                            timestamp: e2.timestamp.map(|t| pbjson_types::Timestamp {
665                                seconds: t.seconds,
666                                nanos: t.nanos,
667                            }),
668                        },
669                    )
670                }
671                tendermint::evidence::Evidence::LightClientAttack(e) => {
672                    use crate::Message;
673                    let e2 =
674                        tendermint_proto::types::LightClientAttackEvidence::from(e.deref().clone());
675                    let e2_bytes = e2.encode_to_vec();
676                    let e3 = crate::tendermint::types::LightClientAttackEvidence::decode(
677                        e2_bytes.as_slice(),
678                    )
679                    .expect("can decode encoded data");
680                    crate::tendermint::types::evidence::Sum::LightClientAttackEvidence(e3)
681                }
682            }),
683        })
684    }
685}
686
687impl TryFrom<tendermint::block::Commit> for crate::tendermint::types::Commit {
688    // TODO(kate): ideally this would not return a tonic status object, but we'll use this for
689    // now to avoid invasively refactoring this code.
690    type Error = tonic::Status;
691    fn try_from(
692        tendermint::block::Commit {
693            height,
694            round,
695            block_id,
696            signatures,
697        }: tendermint::block::Commit,
698    ) -> Result<Self, Self::Error> {
699        Ok(Self {
700            height: height.into(),
701            round: round.into(),
702            block_id: Some(block_id.into()),
703            signatures: signatures
704                .into_iter()
705                .map(crate::tendermint::types::CommitSig::try_from)
706                .collect::<Result<_, _>>()?,
707        })
708    }
709}
710
711impl TryFrom<tendermint::block::CommitSig> for crate::tendermint::types::CommitSig {
712    // TODO(kate): ideally this would not return a tonic status object, but we'll use this for
713    // now to avoid invasively refactoring this code.
714    type Error = tonic::Status;
715    fn try_from(signature: tendermint::block::CommitSig) -> Result<Self, Self::Error> {
716        use chrono::DateTime;
717        Ok({
718            match signature {
719                tendermint::block::CommitSig::BlockIdFlagAbsent => {
720                    crate::tendermint::types::CommitSig {
721                        block_id_flag: crate::tendermint::types::BlockIdFlag::Absent as i32,
722                        // No validator address, or timestamp is recorded for this variant. Not sure if this is a bug in tendermint-rs or not.
723                        validator_address: vec![],
724                        timestamp: None,
725                        signature: vec![],
726                    }
727                }
728                tendermint::block::CommitSig::BlockIdFlagCommit {
729                    validator_address,
730                    timestamp,
731                    signature,
732                } => crate::tendermint::types::CommitSig {
733                    block_id_flag: crate::tendermint::types::BlockIdFlag::Commit as i32,
734                    validator_address: validator_address.into(),
735                    timestamp: Some(pbjson_types::Timestamp {
736                        seconds: DateTime::parse_from_rfc3339(&timestamp.to_rfc3339())
737                            .expect("timestamp should roundtrip to string")
738                            .timestamp(),
739                        nanos: DateTime::parse_from_rfc3339(&timestamp.to_rfc3339())
740                            .expect("timestamp should roundtrip to string")
741                            .timestamp_subsec_nanos()
742                            .try_into()
743                            .expect("good round trip timestamps"),
744                    }),
745                    signature: signature.map(Into::into).unwrap_or_default(),
746                },
747                tendermint::block::CommitSig::BlockIdFlagNil {
748                    validator_address,
749                    timestamp,
750                    signature,
751                } => crate::tendermint::types::CommitSig {
752                    block_id_flag: crate::tendermint::types::BlockIdFlag::Nil as i32,
753                    validator_address: validator_address.into(),
754                    timestamp: Some(pbjson_types::Timestamp {
755                        seconds: DateTime::parse_from_rfc3339(&timestamp.to_rfc3339())
756                            .expect("timestamp should roundtrip to string")
757                            .timestamp(),
758                        nanos: DateTime::parse_from_rfc3339(&timestamp.to_rfc3339())
759                            .expect("timestamp should roundtrip to string")
760                            .timestamp_subsec_nanos()
761                            .try_into()
762                            .expect("good round trip timestamps"),
763                    }),
764                    signature: signature.expect("signature").into(),
765                },
766            }
767        })
768    }
769}
770
771impl From<tendermint::block::Id> for crate::tendermint::types::BlockId {
772    fn from(
773        tendermint::block::Id {
774            hash,
775            part_set_header,
776        }: tendermint::block::Id,
777    ) -> Self {
778        Self {
779            hash: hash.into(),
780            part_set_header: Some(part_set_header.into()),
781        }
782    }
783}
784
785impl From<tendermint::block::parts::Header> for crate::tendermint::types::PartSetHeader {
786    fn from(
787        tendermint::block::parts::Header { total, hash, .. }: tendermint::block::parts::Header,
788    ) -> Self {
789        Self {
790            total,
791            hash: hash.into(),
792        }
793    }
794}