1use crate::util::tendermint_proxy::v1 as penumbra_sdk_pb;
8use anyhow::anyhow;
9
10impl 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 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 index: false,
71 }
72 }
73}
74
75impl 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
117impl 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
140impl 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 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 }
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#[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
341impl 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 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 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
521impl 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 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 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 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(×tamp.to_rfc3339())
737 .expect("timestamp should roundtrip to string")
738 .timestamp(),
739 nanos: DateTime::parse_from_rfc3339(×tamp.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(×tamp.to_rfc3339())
756 .expect("timestamp should roundtrip to string")
757 .timestamp(),
758 nanos: DateTime::parse_from_rfc3339(×tamp.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}