Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve error handling and string conversion in IBC crates. #726

Merged
merged 8 commits into from
Jul 5, 2023
2 changes: 2 additions & 0 deletions .changelog/unreleased/improvement/655-remove-unwrap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- Deny use of unwrap() throughout the crate
([#655](https://github.com/cosmos/ibc-rs/issues/655))
39 changes: 20 additions & 19 deletions crates/ibc/src/clients/ics07_tendermint/client_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -396,10 +396,7 @@ impl Ics2ClientState for ClientState {
_client_message: Any,
_update_kind: &UpdateKind,
) -> Result<(), ClientError> {
let frozen_client_state = self
.clone()
.with_frozen_height(Height::new(0, 1).unwrap())
.into_box();
let frozen_client_state = self.clone().with_frozen_height(Height::min(0)).into_box();

ctx.store_client_state(ClientStatePath::new(client_id), frozen_client_state)?;

Expand Down Expand Up @@ -813,7 +810,7 @@ mod tests {
trusting_period: Duration::new(64000, 0),
unbonding_period: Duration::new(128000, 0),
max_clock_drift: Duration::new(3, 0),
latest_height: Height::new(0, 10).unwrap(),
latest_height: Height::new(0, 10).expect("Never fails"),
proof_specs: ProofSpecs::default(),
upgrade_path: Default::default(),
allow_update: AllowUpdate {
Expand Down Expand Up @@ -919,15 +916,15 @@ mod tests {
Test {
name: "Invalid (too small) trusting trust threshold".to_string(),
params: ClientStateParams {
trust_level: TrustThreshold::new(1, 4).unwrap(),
trust_level: TrustThreshold::new(1, 4).expect("Never fails"),
..default_params.clone()
},
want_pass: false,
},
Test {
name: "Invalid latest height revision number (doesn't match chain)".to_string(),
params: ClientStateParams {
latest_height: Height::new(1, 1).unwrap(),
latest_height: Height::new(1, 1).expect("Never fails"),
..default_params.clone()
},
want_pass: false,
Expand Down Expand Up @@ -979,7 +976,7 @@ mod tests {
trusting_period: Duration::new(64000, 0),
unbonding_period: Duration::new(128000, 0),
max_clock_drift: Duration::new(3, 0),
latest_height: Height::new(1, 10).unwrap(),
latest_height: Height::new(1, 10).expect("Never fails"),
proof_specs: ProofSpecs::default(),
upgrade_path: Default::default(),
allow_update: AllowUpdate {
Expand All @@ -998,13 +995,13 @@ mod tests {
let tests = vec![
Test {
name: "Successful height verification".to_string(),
height: Height::new(1, 8).unwrap(),
height: Height::new(1, 8).expect("Never fails"),
setup: None,
want_pass: true,
},
Test {
name: "Invalid (too large) client height".to_string(),
height: Height::new(1, 12).unwrap(),
height: Height::new(1, 12).expect("Never fails"),
setup: None,
want_pass: false,
},
Expand All @@ -1023,7 +1020,7 @@ mod tests {
p.upgrade_path,
p.allow_update,
)
.unwrap();
.expect("Never fails");
let client_state = match test.setup {
Some(setup) => (setup)(client_state),
_ => client_state,
Expand All @@ -1050,13 +1047,17 @@ mod tests {
});
assert!(tm_client_state_from_raw.is_ok());

let any_from_tm_client_state =
Any::from(tm_client_state_from_raw.as_ref().unwrap().clone());
let any_from_tm_client_state = Any::from(
tm_client_state_from_raw
.as_ref()
.expect("Never fails")
.clone(),
);
let tm_client_state_from_any = TmClientState::try_from(any_from_tm_client_state);
assert!(tm_client_state_from_any.is_ok());
assert_eq!(
tm_client_state_from_raw.unwrap(),
tm_client_state_from_any.unwrap()
tm_client_state_from_raw.expect("Never fails"),
tm_client_state_from_any.expect("Never fails")
);

// check client state creation path from a tendermint header
Expand All @@ -1067,7 +1068,7 @@ mod tests {
assert!(tm_client_state_from_any.is_ok());
assert_eq!(
tm_client_state_from_header,
tm_client_state_from_any.unwrap()
tm_client_state_from_any.expect("Never fails")
);
}

Expand Down Expand Up @@ -1136,15 +1137,15 @@ pub mod test_util {
ChainId::chain_version(tm_header.chain_id.as_str()),
u64::from(tm_header.height),
)
.unwrap(),
.expect("Never fails"),
Default::default(),
Default::default(),
AllowUpdate {
after_expiry: false,
after_misbehaviour: false,
},
)
.unwrap()
.expect("Never fails")
}
}

Expand All @@ -1159,7 +1160,7 @@ pub mod test_util {
trusting_period: Some(Duration::from_secs(64000).into()),
unbonding_period: Some(Duration::from_secs(128000).into()),
max_clock_drift: Some(Duration::from_millis(3000).into()),
latest_height: Some(Height::new(0, 10).unwrap().into()),
latest_height: Some(Height::new(0, 10).expect("Never fails").into()),
proof_specs: ProofSpecs::default().into(),
upgrade_path: Default::default(),
frozen_height: Some(frozen_height),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,11 @@ impl ClientState {
};

let options = self.as_light_client_options()?;
let now = ctx.host_timestamp()?.into_tm_time().unwrap();
let now = ctx.host_timestamp()?.into_tm_time().ok_or_else(|| {
ClientError::ClientSpecific {
description: "host timestamp is not a valid TM timestamp".to_string(),
}
})?;

// main header verification, delegated to the tendermint-light-client crate.
self.verifier
Expand Down
12 changes: 6 additions & 6 deletions crates/ibc/src/clients/ics07_tendermint/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ pub mod test_util {
serde_json::from_str::<SignedHeader>(include_str!(
"../../../tests/support/signed_header.json"
))
.unwrap()
.expect("Never fails")
.header
}

Expand All @@ -295,7 +295,7 @@ pub mod test_util {
let shdr = serde_json::from_str::<SignedHeader>(include_str!(
"../../../tests/support/signed_header.json"
))
.unwrap();
.expect("Never fails");

// Build a set of validators.
// Below are test values inspired form `test_validator_set()` in tendermint-rs.
Expand All @@ -304,18 +304,18 @@ pub mod test_util {
&hex::decode_upper(
"F349539C7E5EF7C49549B09C4BFC2335318AB0FE51FBFAA2433B4F13E816F4A7",
)
.unwrap(),
.expect("Never fails"),
)
.unwrap(),
281_815_u64.try_into().unwrap(),
.expect("Never fails"),
281_815_u64.try_into().expect("Never fails"),
);

let vs = ValidatorSet::new(vec![v1.clone()], Some(v1));

Header {
signed_header: shdr,
validator_set: vs.clone(),
trusted_height: Height::new(0, 1).unwrap(),
trusted_height: Height::min(0),
trusted_next_validator_set: vs,
}
}
Expand Down
3 changes: 2 additions & 1 deletion crates/ibc/src/core/ics02_client/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,8 @@ impl From<HeaderAttribute> for abci::EventAttribute {
fn from(attr: HeaderAttribute) -> Self {
(
HEADER_ATTRIBUTE_KEY,
String::from_utf8(hex::encode(attr.header.value)).unwrap(),
String::from_utf8(hex::encode(attr.header.value))
.expect("Never fails because hexadecimal is valid UTF-8"),
)
.into()
}
Expand Down
7 changes: 7 additions & 0 deletions crates/ibc/src/core/ics02_client/height.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ impl Height {
})
}

pub fn min(revision_number: u64) -> Self {
Self {
revision_number,
revision_height: 1,
}
}

pub fn revision_number(&self) -> u64 {
self.revision_number
}
Expand Down
6 changes: 4 additions & 2 deletions crates/ibc/src/core/ics04_channel/events/packet_attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ impl TryFrom<PacketDataAttribute> for Vec<abci::EventAttribute> {
.into(),
(
PKT_DATA_HEX_ATTRIBUTE_KEY,
String::from_utf8(hex::encode(attr.packet_data)).unwrap(),
String::from_utf8(hex::encode(attr.packet_data))
.expect("Never fails because hexadecimal is valid UTF8"),
)
.into(),
];
Expand Down Expand Up @@ -332,7 +333,8 @@ impl TryFrom<AcknowledgementAttribute> for Vec<abci::EventAttribute> {
.into(),
(
PKT_ACK_HEX_ATTRIBUTE_KEY,
String::from_utf8(hex::encode(attr.acknowledgement)).unwrap(),
String::from_utf8(hex::encode(attr.acknowledgement))
.expect("Never fails because hexadecimal is always valid UTF-8"),
)
.into(),
];
Expand Down
11 changes: 8 additions & 3 deletions crates/ibc/src/core/ics23_commitment/commitment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ pub struct CommitmentRoot {

impl fmt::Debug for CommitmentRoot {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let hex = Hex::upper_case().encode_to_string(&self.bytes).unwrap();
let hex = Hex::upper_case()
.encode_to_string(&self.bytes)
.map_err(|_| fmt::Error)?;
f.debug_tuple("CommitmentRoot").field(&hex).finish()
}
}
Expand Down Expand Up @@ -72,7 +74,9 @@ pub struct CommitmentProofBytes {

impl fmt::Debug for CommitmentProofBytes {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let hex = Hex::upper_case().encode_to_string(&self.bytes).unwrap();
let hex = Hex::upper_case()
.encode_to_string(&self.bytes)
.map_err(|_| fmt::Error)?;
f.debug_tuple("CommitmentProof").field(&hex).finish()
}
}
Expand Down Expand Up @@ -100,7 +104,8 @@ impl TryFrom<RawMerkleProof> for CommitmentProofBytes {

fn try_from(proof: RawMerkleProof) -> Result<Self, Self::Error> {
let mut buf = Vec::new();
prost::Message::encode(&proof, &mut buf).unwrap();
prost::Message::encode(&proof, &mut buf)
.map_err(|e| Self::Error::EncodingFailure(e.to_string()))?;
buf.try_into()
}
}
Expand Down
3 changes: 3 additions & 0 deletions crates/ibc/src/core/ics23_commitment/error.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Defines the commitment error type

use alloc::string::String;
use displaydoc::Display;
use prost::DecodeError;

Expand All @@ -25,6 +26,8 @@ pub enum CommitmentError {
InvalidMerkleProof,
/// proof verification failed
VerificationFailure,
/// encoded commitment prefix is not a valid hex string: `{0}`
EncodingFailure(String),
}

#[cfg(feature = "std")]
Expand Down
23 changes: 4 additions & 19 deletions crates/ibc/src/core/ics23_commitment/merkle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,31 +39,16 @@ pub struct MerkleProof {
/// Ref. <https://github.com/informalsystems/ibc-rs/issues/853>
impl From<RawMerkleProof> for MerkleProof {
fn from(proof: RawMerkleProof) -> Self {
let proofs: Vec<CommitmentProof> = proof
.proofs
.into_iter()
.map(|p| {
let mut encoded = Vec::new();
prost::Message::encode(&p, &mut encoded).unwrap();
prost::Message::decode(&*encoded).unwrap()
})
.collect();
Self { proofs }
Self {
proofs: proof.proofs,
}
}
}

impl From<MerkleProof> for RawMerkleProof {
fn from(proof: MerkleProof) -> Self {
Self {
proofs: proof
.proofs
.into_iter()
.map(|p| {
let mut encoded = Vec::new();
prost::Message::encode(&p, &mut encoded).unwrap();
prost::Message::decode(&*encoded).unwrap()
})
.collect(),
proofs: proof.proofs,
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/ibc/src/core/ics24_host/identifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ impl FromStr for ClientId {

impl Default for ClientId {
fn default() -> Self {
Self::new(tm_client_type(), 0).unwrap()
Self::new(tm_client_type(), 0).expect("Never fails because we use a valid client type")
}
}

Expand Down
15 changes: 12 additions & 3 deletions crates/ibc/src/core/timestamp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,13 @@ impl Timestamp {
// about year 2554, there is no risk of overflowing `Time`
// or `OffsetDateTime`.
let ts = OffsetDateTime::from_unix_timestamp_nanos(nanoseconds as i128)
.unwrap()
.map_err(|e: time::error::ComponentRange| {
ParseTimestampError::DataOutOfRange(e.to_string())
})?
.try_into()
.unwrap();
.map_err(|e: tendermint::error::Error| {
ParseTimestampError::DataOutOfRange(e.to_string())
})?;
Ok(Timestamp { time: Some(ts) })
}
}
Expand Down Expand Up @@ -180,7 +184,9 @@ impl Timestamp {
let t: OffsetDateTime = time.into();
let s = t.unix_timestamp_nanos();
assert!(s >= 0, "time {time:?} has negative `.timestamp()`");
s.try_into().unwrap()
s.try_into().expect(
"Fails UNIX timestamp is negative, but we don't allow that to be constructed",
)
})
}

Expand Down Expand Up @@ -269,13 +275,16 @@ impl Sub<Duration> for Timestamp {
pub enum ParseTimestampError {
/// parsing u64 integer from string error: `{0}`
ParseInt(ParseIntError),
/// Out of Range: `{0}`
DataOutOfRange(String),
}

#[cfg(feature = "std")]
impl std::error::Error for ParseTimestampError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match &self {
ParseTimestampError::ParseInt(e) => Some(e),
ParseTimestampError::DataOutOfRange(_) => None,
}
}
}
Expand Down
4 changes: 1 addition & 3 deletions crates/ibc/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
// TODO: disable unwraps:
// https://github.com/informalsystems/ibc-rs/issues/987
// #![cfg_attr(not(test), deny(clippy::unwrap_used))]
#![cfg_attr(not(test), deny(clippy::unwrap_used))]
plafer marked this conversation as resolved.
Show resolved Hide resolved
#![no_std]
#![deny(
warnings,
Expand Down
6 changes: 2 additions & 4 deletions crates/ibc/src/mock/client_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ impl TryFrom<RawMockClientState> for MockClientState {
type Error = ClientError;

fn try_from(raw: RawMockClientState) -> Result<Self, Self::Error> {
Ok(Self::new(raw.header.unwrap().try_into()?))
Ok(Self::new(raw.header.expect("Never fails").try_into()?))
}
}

Expand Down Expand Up @@ -316,9 +316,7 @@ impl ClientState for MockClientState {
_client_message: Any,
_update_kind: &UpdateKind,
) -> Result<(), ClientError> {
let frozen_client_state = self
.with_frozen_height(Height::new(0, 1).unwrap())
.into_box();
let frozen_client_state = self.with_frozen_height(Height::min(0)).into_box();

ctx.store_client_state(ClientStatePath::new(client_id), frozen_client_state)?;

Expand Down
Loading