Skip to content

Commit 72bfb7d

Browse files
zbucconorsch
authored andcommitted
Cleanup tendermint proxy code, fix timestamp handling
1 parent c4ea63f commit 72bfb7d

File tree

3 files changed

+128
-51
lines changed

3 files changed

+128
-51
lines changed

crates/proto/src/protobuf/tendermint_compat.rs

Lines changed: 114 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// library. accordingly, it is grouped by conversions needed for each RPC endpoint.
66

77
use crate::util::tendermint_proxy::v1 as penumbra_pb;
8+
use anyhow::anyhow;
89

910
// === get_tx ===
1011

@@ -323,9 +324,7 @@ impl From<tendermint::merkle::proof::ProofOp> for crate::tendermint::crypto::Pro
323324
// === get_block_by_height ===
324325

325326
impl TryFrom<tendermint_rpc::endpoint::block::Response> for penumbra_pb::GetBlockByHeightResponse {
326-
// TODO(kate): ideally this would not return a tonic status object, but we'll use this for
327-
// now to avoid invasively refactoring this code.
328-
type Error = tonic::Status;
327+
type Error = anyhow::Error;
329328
fn try_from(
330329
tendermint_rpc::endpoint::block::Response {
331330
block,
@@ -338,11 +337,8 @@ impl TryFrom<tendermint_rpc::endpoint::block::Response> for penumbra_pb::GetBloc
338337
})
339338
}
340339
}
341-
342340
impl TryFrom<tendermint::Block> for crate::tendermint::types::Block {
343-
// TODO(kate): ideally this would not return a tonic status object, but we'll use this for
344-
// now to avoid invasively refactoring this code.
345-
type Error = tonic::Status;
341+
type Error = anyhow::Error;
346342
fn try_from(
347343
tendermint::Block {
348344
header,
@@ -356,14 +352,76 @@ impl TryFrom<tendermint::Block> for crate::tendermint::types::Block {
356352
header: header.try_into().map(Some)?,
357353
data: Some(crate::tendermint::types::Data { txs: data }),
358354
evidence: evidence.try_into().map(Some)?,
359-
last_commit: Some(
360-
last_commit
361-
.map(crate::tendermint::types::Commit::try_from)
362-
.transpose()?
363-
// TODO(kate): this probably should not panic, but this is here to preserve
364-
// existing behavior. panic if no last commit is set.
365-
.expect("last_commit"),
366-
),
355+
last_commit: last_commit
356+
.map(crate::tendermint::types::Commit::try_from)
357+
.transpose()?,
358+
})
359+
}
360+
}
361+
362+
impl TryFrom<crate::tendermint::types::PartSetHeader> for tendermint::block::parts::Header {
363+
type Error = anyhow::Error;
364+
fn try_from(
365+
crate::tendermint::types::PartSetHeader { total, hash }: crate::tendermint::types::PartSetHeader,
366+
) -> Result<Self, Self::Error> {
367+
Ok(Self::new(total, hash.try_into()?)?)
368+
}
369+
}
370+
371+
impl TryFrom<crate::tendermint::types::Header> for tendermint::block::Header {
372+
type Error = anyhow::Error;
373+
fn try_from(
374+
crate::tendermint::types::Header {
375+
version,
376+
chain_id,
377+
height,
378+
time,
379+
last_block_id,
380+
last_commit_hash,
381+
data_hash,
382+
validators_hash,
383+
next_validators_hash,
384+
consensus_hash,
385+
app_hash,
386+
last_results_hash,
387+
evidence_hash,
388+
proposer_address,
389+
}: crate::tendermint::types::Header,
390+
) -> Result<Self, Self::Error> {
391+
Ok(Self {
392+
version: tendermint::block::header::Version {
393+
block: version.clone().ok_or(anyhow!("version"))?.block,
394+
app: version.ok_or(anyhow!("version"))?.app,
395+
},
396+
chain_id: tendermint::chain::Id::try_from(chain_id)?,
397+
height: tendermint::block::Height::try_from(height)?,
398+
time: tendermint::Time::from_unix_timestamp(
399+
time.clone().ok_or(anyhow!("time"))?.seconds,
400+
time.clone()
401+
.ok_or(anyhow!("missing time"))?
402+
.nanos
403+
.try_into()?,
404+
)?,
405+
last_block_id: match last_block_id {
406+
Some(last_block_id) => Some(tendermint::block::Id {
407+
hash: tendermint::Hash::try_from(last_block_id.hash)?,
408+
part_set_header: tendermint::block::parts::Header::try_from(
409+
last_block_id
410+
.part_set_header
411+
.ok_or(anyhow::anyhow!("bad part set header"))?,
412+
)?,
413+
}),
414+
None => None,
415+
},
416+
last_commit_hash: Some(last_commit_hash.try_into()?),
417+
data_hash: Some(data_hash.try_into()?),
418+
validators_hash: validators_hash.try_into()?,
419+
next_validators_hash: next_validators_hash.try_into()?,
420+
consensus_hash: consensus_hash.try_into()?,
421+
app_hash: app_hash.try_into()?,
422+
last_results_hash: Some(last_results_hash.try_into()?),
423+
evidence_hash: Some(evidence_hash.try_into()?),
424+
proposer_address: proposer_address.try_into()?,
367425
})
368426
}
369427
}
@@ -394,7 +452,11 @@ impl TryFrom<tendermint::block::Header> for crate::tendermint::types::Header {
394452
// around a `time::PrimitiveDateTime` however it's private so we
395453
// have to use string parsing to get to the prost type we want :(
396454
let header_time = chrono::DateTime::parse_from_rfc3339(time.to_rfc3339().as_str())
397-
.expect("timestamp should roundtrip to string");
455+
.or_else(|_| {
456+
Err(tonic::Status::invalid_argument(
457+
"timestamp should roundtrip to string",
458+
))
459+
})?;
398460
Ok(Self {
399461
version: Some(crate::tendermint::version::Consensus {
400462
block: version.block,
@@ -404,10 +466,7 @@ impl TryFrom<tendermint::block::Header> for crate::tendermint::types::Header {
404466
height: height.into(),
405467
time: Some(pbjson_types::Timestamp {
406468
seconds: header_time.timestamp(),
407-
nanos: header_time
408-
.timestamp_nanos_opt()
409-
.ok_or_else(|| tonic::Status::invalid_argument("missing header_time nanos"))?
410-
as i32,
469+
nanos: header_time.timestamp_subsec_nanos() as i32,
411470
}),
412471
last_block_id: last_block_id.map(|id| crate::tendermint::types::BlockId {
413472
hash: id.hash.into(),
@@ -430,9 +489,7 @@ impl TryFrom<tendermint::block::Header> for crate::tendermint::types::Header {
430489
}
431490

432491
impl TryFrom<tendermint::evidence::List> for crate::tendermint::types::EvidenceList {
433-
// TODO(kate): ideally this would not return a tonic status object, but we'll use this for
434-
// now to avoid invasively refactoring this code.
435-
type Error = tonic::Status;
492+
type Error = anyhow::Error;
436493
fn try_from(list: tendermint::evidence::List) -> Result<Self, Self::Error> {
437494
list.into_vec()
438495
.into_iter()
@@ -443,11 +500,9 @@ impl TryFrom<tendermint::evidence::List> for crate::tendermint::types::EvidenceL
443500
}
444501

445502
// TODO(kate): this should be decomposed further at a later point, i am refraining from doing
446-
// so right now. there are `Option::expect()` calls below that should be considered.
503+
// so right now.
447504
impl TryFrom<tendermint::evidence::Evidence> for crate::tendermint::types::Evidence {
448-
// TODO(kate): ideally this would not return a tonic status object, but we'll use this for
449-
// now to avoid invasively refactoring this code.
450-
type Error = tonic::Status;
505+
type Error = anyhow::Error;
451506
fn try_from(evidence: tendermint::evidence::Evidence) -> Result<Self, Self::Error> {
452507
use {chrono::DateTime, std::ops::Deref};
453508
Ok(Self {
@@ -469,21 +524,27 @@ impl TryFrom<tendermint::evidence::Evidence> for crate::tendermint::types::Evide
469524
height: e.votes().0.height.into(),
470525
round: e.votes().0.round.into(),
471526
block_id: Some(crate::tendermint::types::BlockId {
472-
hash: e.votes().0.block_id.expect("block id").hash.into(),
527+
hash: e
528+
.votes()
529+
.0
530+
.block_id
531+
.ok_or(anyhow!("block id"))?
532+
.hash
533+
.into(),
473534
part_set_header: Some(
474535
crate::tendermint::types::PartSetHeader {
475536
total: e
476537
.votes()
477538
.0
478539
.block_id
479-
.expect("block id")
540+
.ok_or(anyhow!("block id"))?
480541
.part_set_header
481542
.total,
482543
hash: e
483544
.votes()
484545
.0
485546
.block_id
486-
.expect("block id")
547+
.ok_or(anyhow!("block id"))?
487548
.part_set_header
488549
.hash
489550
.into(),
@@ -492,18 +553,25 @@ impl TryFrom<tendermint::evidence::Evidence> for crate::tendermint::types::Evide
492553
}),
493554
timestamp: Some(pbjson_types::Timestamp {
494555
seconds: DateTime::parse_from_rfc3339(
495-
&e.votes().0.timestamp.expect("timestamp").to_rfc3339(),
556+
&e.votes()
557+
.0
558+
.timestamp
559+
.ok_or(tonic::Status::invalid_argument(
560+
"bad timestamp",
561+
))?
562+
.to_rfc3339(),
496563
)
497-
.expect("timestamp should roundtrip to string")
564+
.or_else(|_| {
565+
Err(tonic::Status::invalid_argument("bad timestamp"))
566+
})?
498567
.timestamp(),
499568
nanos: DateTime::parse_from_rfc3339(
500569
&e.votes().0.timestamp.expect("timestamp").to_rfc3339(),
501570
)
502571
.expect("timestamp should roundtrip to string")
503-
.timestamp_nanos_opt()
504-
.ok_or_else(|| {
505-
tonic::Status::invalid_argument("missing timestamp nanos")
506-
})? as i32,
572+
.timestamp_subsec_nanos()
573+
.try_into()
574+
.expect("good round trip timestamps"),
507575
}),
508576
validator_address: e.votes().0.validator_address.into(),
509577
validator_index: e.votes().0.validator_index.into(),
@@ -558,10 +626,9 @@ impl TryFrom<tendermint::evidence::Evidence> for crate::tendermint::types::Evide
558626
&e.votes().1.timestamp.expect("timestamp").to_rfc3339(),
559627
)
560628
.expect("timestamp should roundtrip to string")
561-
.timestamp_nanos_opt()
562-
.ok_or_else(|| {
563-
tonic::Status::invalid_argument("missing timestamp nanos")
564-
})? as i32,
629+
.timestamp_subsec_nanos()
630+
.try_into()
631+
.expect("good round trip timestamps"),
565632
}),
566633
validator_address: e.votes().1.validator_address.into(),
567634
validator_index: e.votes().1.validator_index.into(),
@@ -652,12 +719,11 @@ impl TryFrom<tendermint::block::CommitSig> for crate::tendermint::types::CommitS
652719
.timestamp(),
653720
nanos: DateTime::parse_from_rfc3339(&timestamp.to_rfc3339())
654721
.expect("timestamp should roundtrip to string")
655-
.timestamp_nanos_opt()
656-
.ok_or_else(|| {
657-
tonic::Status::invalid_argument("missing timestamp nanos")
658-
})? as i32,
722+
.timestamp_subsec_nanos()
723+
.try_into()
724+
.expect("good round trip timestamps"),
659725
}),
660-
signature: signature.expect("signature").into(),
726+
signature: signature.map(Into::into).unwrap_or_default(),
661727
},
662728
tendermint::block::CommitSig::BlockIdFlagNil {
663729
validator_address,
@@ -672,10 +738,9 @@ impl TryFrom<tendermint::block::CommitSig> for crate::tendermint::types::CommitS
672738
.timestamp(),
673739
nanos: DateTime::parse_from_rfc3339(&timestamp.to_rfc3339())
674740
.expect("timestamp should roundtrip to string")
675-
.timestamp_nanos_opt()
676-
.ok_or_else(|| {
677-
tonic::Status::invalid_argument("missing timestamp nanos")
678-
})? as i32,
741+
.timestamp_subsec_nanos()
742+
.try_into()
743+
.expect("good round trip timestamps"),
679744
}),
680745
signature: signature.expect("signature").into(),
681746
},

crates/test/mock-tendermint-proxy/src/proxy.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,11 @@ impl TendermintProxyService for TestNodeProxy {
211211
.get(&height)
212212
.cloned()
213213
.map(penumbra_proto::tendermint::types::Block::try_from)
214-
.transpose()?;
214+
.transpose()
215+
.or_else(|e| {
216+
tracing::warn!(?height, error = ?e, "proxy: error fetching blocks");
217+
Err(tonic::Status::internal("error fetching blocks"))
218+
})?;
215219
let block_id = block
216220
.as_ref() // is this off-by-one? should we be getting the id of the last commit?
217221
.and_then(|b| b.last_commit.as_ref())

crates/util/tendermint-proxy/src/tendermint_proxy.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,15 @@ impl TendermintProxyService for TendermintProxy {
197197
.block(height)
198198
.await
199199
.map_err(|e| tonic::Status::unavailable(format!("error querying abci: {e}")))
200-
.and_then(GetBlockByHeightResponse::try_from)
200+
.and_then(|b| {
201+
match GetBlockByHeightResponse::try_from(b) {
202+
Ok(b) => Ok(b),
203+
Err(e) => {
204+
tracing::warn!(?height, error = ?e, "proxy: error deserializing GetBlockByHeightResponse");
205+
Err(tonic::Status::internal("error deserializing GetBlockByHeightResponse"))
206+
}
207+
}
208+
})
201209
.map(tonic::Response::new)
202210
}
203211
}

0 commit comments

Comments
 (0)