diff --git a/ci/cw-check/Cargo.lock b/ci/cw-check/Cargo.lock index b7b3e2a48..2ba932c42 100644 --- a/ci/cw-check/Cargo.lock +++ b/ci/cw-check/Cargo.lock @@ -852,6 +852,7 @@ dependencies = [ "ibc-core-client", "ibc-core-commitment-types", "ibc-core-connection", + "ibc-core-connection-types", "ibc-core-handler-types", "ibc-core-host", "ibc-core-router", diff --git a/ibc-clients/ics07-tendermint/src/client_state.rs b/ibc-clients/ics07-tendermint/src/client_state.rs index 0adb68f22..ecfbba885 100644 --- a/ibc-clients/ics07-tendermint/src/client_state.rs +++ b/ibc-clients/ics07-tendermint/src/client_state.rs @@ -8,7 +8,7 @@ //! Rust). As such, this module also includes some trait implementations that //! serve to pass through traits implemented on the wrapped `ClientState` type. -use ibc_client_tendermint_types::error::Error; +use ibc_client_tendermint_types::error::TendermintClientError; use ibc_client_tendermint_types::proto::v1::ClientState as RawTmClientState; use ibc_client_tendermint_types::ClientState as ClientStateType; use ibc_core_client::types::error::ClientError; @@ -44,7 +44,7 @@ impl ClientState { impl Protobuf for ClientState {} impl TryFrom for ClientState { - type Error = Error; + type Error = TendermintClientError; fn try_from(raw: RawTmClientState) -> Result { Ok(Self(ClientStateType::try_from(raw)?)) diff --git a/ibc-clients/ics07-tendermint/src/client_state/common.rs b/ibc-clients/ics07-tendermint/src/client_state/common.rs index ef5b04469..66ac52aec 100644 --- a/ibc-clients/ics07-tendermint/src/client_state/common.rs +++ b/ibc-clients/ics07-tendermint/src/client_state/common.rs @@ -67,7 +67,7 @@ impl ClientStateCommon for ClientState { let (upgrade_path_prefix, upgrade_path) = match upgrade_path.len() { 0 => { return Err(UpgradeClientError::InvalidUpgradePath { - reason: "no upgrade path has been set".to_string(), + description: "no upgrade path has been set".to_string(), } .into()); } @@ -78,7 +78,7 @@ impl ClientStateCommon for ClientState { ), _ => { return Err(UpgradeClientError::InvalidUpgradePath { - reason: "upgrade path is too long".to_string(), + description: "upgrade path is too long".to_string(), } .into()); } @@ -162,15 +162,11 @@ pub fn verify_consensus_state( let tm_consensus_state = TmConsensusState::try_from(consensus_state)?; if tm_consensus_state.root().is_empty() { - return Err(ClientError::Other { - description: "empty commitment root".into(), - }); + Err(CommitmentError::EmptyCommitmentRoot)?; }; if consensus_state_status(&tm_consensus_state, host_timestamp, trusting_period)?.is_expired() { - return Err(ClientError::ClientNotActive { - status: Status::Expired, - }); + return Err(ClientError::InvalidStatus(Status::Expired)); } Ok(()) @@ -214,8 +210,8 @@ pub fn validate_proof_height( if latest_height < proof_height { return Err(ClientError::InvalidProofHeight { - latest_height, - proof_height, + actual: latest_height, + expected: proof_height, }); } @@ -258,7 +254,7 @@ pub fn verify_upgrade_client( // the upgrade height This condition checks both the revision number and // the height if latest_height >= upgraded_tm_client_state_height { - Err(UpgradeClientError::LowUpgradeHeight { + Err(UpgradeClientError::InsufficientUpgradeHeight { upgraded_height: upgraded_tm_client_state_height, client_height: latest_height, })? @@ -301,17 +297,16 @@ pub fn verify_membership( value: Vec, ) -> Result<(), ClientError> { if prefix.is_empty() { - return Err(ClientError::Ics23Verification( - CommitmentError::EmptyCommitmentPrefix, - )); + Err(CommitmentError::EmptyCommitmentPrefix)?; } let merkle_path = MerklePath::new(vec![prefix.as_bytes().to_vec().into(), path]); - let merkle_proof = MerkleProof::try_from(proof).map_err(ClientError::InvalidCommitmentProof)?; - merkle_proof - .verify_membership::(proof_specs, root.clone().into(), merkle_path, value, 0) - .map_err(ClientError::Ics23Verification) + let merkle_proof = MerkleProof::try_from(proof)?; + + merkle_proof.verify_membership::(proof_specs, root.clone().into(), merkle_path, value, 0)?; + + Ok(()) } /// Verify that the given value does not belong in the client's merkle proof. @@ -327,9 +322,10 @@ pub fn verify_non_membership( path: PathBytes, ) -> Result<(), ClientError> { let merkle_path = MerklePath::new(vec![prefix.as_bytes().to_vec().into(), path]); - let merkle_proof = MerkleProof::try_from(proof).map_err(ClientError::InvalidCommitmentProof)?; - merkle_proof - .verify_non_membership::(proof_specs, root.clone().into(), merkle_path) - .map_err(ClientError::Ics23Verification) + let merkle_proof = MerkleProof::try_from(proof)?; + + merkle_proof.verify_non_membership::(proof_specs, root.clone().into(), merkle_path)?; + + Ok(()) } diff --git a/ibc-clients/ics07-tendermint/src/client_state/misbehaviour.rs b/ibc-clients/ics07-tendermint/src/client_state/misbehaviour.rs index ea40fa717..29e672c6b 100644 --- a/ibc-clients/ics07-tendermint/src/client_state/misbehaviour.rs +++ b/ibc-clients/ics07-tendermint/src/client_state/misbehaviour.rs @@ -1,4 +1,4 @@ -use ibc_client_tendermint_types::error::{Error, IntoResult}; +use ibc_client_tendermint_types::error::{IntoResult, TendermintClientError}; use ibc_client_tendermint_types::{ ConsensusState as ConsensusStateType, Header as TmHeader, Misbehaviour as TmMisbehaviour, }; @@ -101,14 +101,11 @@ where let duration_since_consensus_state = current_timestamp.duration_since(&trusted_timestamp).ok_or( - ClientError::InvalidConsensusStateTimestamp { - time1: trusted_timestamp, - time2: current_timestamp, - }, + ClientError::InvalidConsensusStateTimestamp(trusted_timestamp), )?; if duration_since_consensus_state >= options.trusting_period { - return Err(Error::ConsensusStateTimestampGteTrustingPeriod { + return Err(TendermintClientError::InsufficientTrustingPeriod { duration_since_consensus_state, trusting_period: options.trusting_period, } diff --git a/ibc-clients/ics07-tendermint/src/client_state/update_client.rs b/ibc-clients/ics07-tendermint/src/client_state/update_client.rs index 03da355ff..40ec52751 100644 --- a/ibc-clients/ics07-tendermint/src/client_state/update_client.rs +++ b/ibc-clients/ics07-tendermint/src/client_state/update_client.rs @@ -1,4 +1,4 @@ -use ibc_client_tendermint_types::error::{Error, IntoResult}; +use ibc_client_tendermint_types::error::{IntoResult, TendermintClientError}; use ibc_client_tendermint_types::{ConsensusState as ConsensusStateType, Header as TmHeader}; use ibc_core_client::context::{Convertible, ExtClientValidationContext}; use ibc_core_client::types::error::ClientError; @@ -63,10 +63,10 @@ where .trusted_height .revision_height() .try_into() - .map_err(|_| ClientError::ClientSpecific { - description: Error::InvalidHeaderHeight { - height: header.trusted_height.revision_height(), - } + .map_err(|_| ClientError::FailedHeaderVerification { + description: TendermintClientError::InvalidHeaderHeight( + header.trusted_height.revision_height(), + ) .to_string(), })?, next_validators: &header.trusted_next_validator_set, diff --git a/ibc-clients/ics07-tendermint/src/client_state/validation.rs b/ibc-clients/ics07-tendermint/src/client_state/validation.rs index b1a028749..4cd1a9cc8 100644 --- a/ibc-clients/ics07-tendermint/src/client_state/validation.rs +++ b/ibc-clients/ics07-tendermint/src/client_state/validation.rs @@ -278,5 +278,5 @@ where && subject_proof_specs == &substitute_proof_specs && subject_upgrade_path == &substitute_upgrade_path) .then_some(()) - .ok_or(ClientError::ClientRecoveryStateMismatch) + .ok_or(ClientError::MismatchedClientRecoveryStates) } diff --git a/ibc-clients/ics07-tendermint/src/consensus_state.rs b/ibc-clients/ics07-tendermint/src/consensus_state.rs index 3f64ce085..6d0ed7f63 100644 --- a/ibc-clients/ics07-tendermint/src/consensus_state.rs +++ b/ibc-clients/ics07-tendermint/src/consensus_state.rs @@ -6,7 +6,7 @@ //! implementations that serve to pass through traits implemented on the wrapped //! `ConsensusState` type. -use ibc_client_tendermint_types::error::Error; +use ibc_client_tendermint_types::error::TendermintClientError; use ibc_client_tendermint_types::proto::v1::ConsensusState as RawTmConsensusState; use ibc_client_tendermint_types::ConsensusState as ConsensusStateType; use ibc_core_client::context::consensus_state::ConsensusState as ConsensusStateTrait; @@ -52,7 +52,7 @@ impl From for ConsensusStateType { impl Protobuf for ConsensusState {} impl TryFrom for ConsensusState { - type Error = Error; + type Error = TendermintClientError; fn try_from(raw: RawTmConsensusState) -> Result { Ok(Self(ConsensusStateType::try_from(raw)?)) diff --git a/ibc-clients/ics07-tendermint/types/src/client_state.rs b/ibc-clients/ics07-tendermint/types/src/client_state.rs index ee30dbdb0..98ce38460 100644 --- a/ibc-clients/ics07-tendermint/types/src/client_state.rs +++ b/ibc-clients/ics07-tendermint/types/src/client_state.rs @@ -18,7 +18,7 @@ use tendermint::chain::id::MAX_LENGTH as MaxChainIdLen; use tendermint::trust_threshold::TrustThresholdFraction as TendermintTrustThresholdFraction; use tendermint_light_client_verifier::options::Options; -use crate::error::Error; +use crate::error::TendermintClientError; use crate::header::Header as TmHeader; use crate::trust_threshold::TrustThreshold; @@ -88,7 +88,7 @@ impl ClientState { proof_specs: ProofSpecs, upgrade_path: Vec, allow_update: AllowUpdate, - ) -> Result { + ) -> Result { let client_state = Self::new_without_validation( chain_id, trust_level, @@ -105,7 +105,7 @@ impl ClientState { Ok(client_state) } - pub fn with_header(self, header: TmHeader) -> Result { + pub fn with_header(self, header: TmHeader) -> Result { Ok(Self { latest_height: max(header.height(), self.latest_height), ..self @@ -119,14 +119,14 @@ impl ClientState { } } - pub fn validate(&self) -> Result<(), Error> { + pub fn validate(&self) -> Result<(), TendermintClientError> { self.chain_id.validate_length(3, MaxChainIdLen as u64)?; // `TrustThreshold` is guaranteed to be in the range `[0, 1)`, but a `TrustThreshold::ZERO` // value is invalid in this context if self.trust_level == TrustThreshold::ZERO { - return Err(Error::InvalidTrustThreshold { - reason: "ClientState trust-level cannot be zero".to_string(), + return Err(TendermintClientError::InvalidTrustThreshold { + description: "ClientState trust-level cannot be zero".to_string(), }); } @@ -134,12 +134,18 @@ impl ClientState { self.trust_level.numerator(), self.trust_level.denominator(), ) - .map_err(Error::InvalidTendermintTrustThreshold)?; + .map_err(|_| TendermintClientError::InvalidTrustThreshold { + description: format!( + "invalid Tendermint trust threshold: {:?}/{:?}", + self.trust_level.numerator(), + self.trust_level.denominator() + ), + })?; // Basic validation of trusting period and unbonding period: each should be non-zero. if self.trusting_period <= Duration::new(0, 0) { - return Err(Error::InvalidTrustThreshold { - reason: format!( + return Err(TendermintClientError::InvalidTrustThreshold { + description: format!( "ClientState trusting period ({:?}) must be greater than zero", self.trusting_period ), @@ -147,8 +153,8 @@ impl ClientState { } if self.unbonding_period <= Duration::new(0, 0) { - return Err(Error::InvalidTrustThreshold { - reason: format!( + return Err(TendermintClientError::InvalidTrustThreshold { + description: format!( "ClientState unbonding period ({:?}) must be greater than zero", self.unbonding_period ), @@ -156,23 +162,21 @@ impl ClientState { } if self.trusting_period >= self.unbonding_period { - return Err(Error::InvalidTrustThreshold { - reason: format!( + return Err(TendermintClientError::InvalidTrustThreshold { + description: format!( "ClientState trusting period ({:?}) must be smaller than unbonding period ({:?})", self.trusting_period, self.unbonding_period ), }); } if self.max_clock_drift <= Duration::new(0, 0) { - return Err(Error::InvalidMaxClockDrift { - reason: "ClientState max-clock-drift must be greater than zero".to_string(), - }); + return Err(TendermintClientError::InvalidMaxClockDrift); } if self.latest_height.revision_number() != self.chain_id.revision_number() { - return Err(Error::InvalidLatestHeight { - reason: "ClientState latest-height revision number must match chain-id version" - .to_string(), + return Err(TendermintClientError::MismatchedRevisionHeights { + expected: self.chain_id.revision_number(), + actual: self.latest_height.revision_number(), }); } @@ -180,13 +184,9 @@ impl ClientState { self.proof_specs.validate()?; // `upgrade_path` itself may be empty, but if not then each key must be non-empty - for (idx, key) in self.upgrade_path.iter().enumerate() { + for key in self.upgrade_path.iter() { if key.trim().is_empty() { - return Err(Error::Validation { - reason: format!( - "ClientState upgrade-path key at index {idx:?} cannot be empty" - ), - }); + return Err(TendermintClientError::EmptyUpgradePathKey); } } @@ -200,11 +200,11 @@ impl ClientState { /// Helper method to produce a [`Options`] struct for use in /// Tendermint-specific light client verification. - pub fn as_light_client_options(&self) -> Result { + pub fn as_light_client_options(&self) -> Result { Ok(Options { trust_threshold: self.trust_level.try_into().map_err(|e: ClientError| { - Error::InvalidTrustThreshold { - reason: e.to_string(), + TendermintClientError::InvalidTrustThreshold { + description: e.to_string(), } })?, trusting_period: self.trusting_period, @@ -234,49 +234,54 @@ impl ClientState { impl Protobuf for ClientState {} impl TryFrom for ClientState { - type Error = Error; + type Error = TendermintClientError; fn try_from(raw: RawTmClientState) -> Result { let chain_id = ChainId::from_str(raw.chain_id.as_str())?; let trust_level = { - let trust_level = raw.trust_level.ok_or(Error::MissingTrustingPeriod)?; + let trust_level = raw + .trust_level + .ok_or(TendermintClientError::MissingTrustingPeriod)?; trust_level .try_into() - .map_err(|e| Error::InvalidTrustThreshold { - reason: format!("{e}"), + .map_err(|e| TendermintClientError::InvalidTrustThreshold { + description: format!("{e}"), })? }; let trusting_period = raw .trusting_period - .ok_or(Error::MissingTrustingPeriod)? + .ok_or(TendermintClientError::MissingTrustingPeriod)? .try_into() - .map_err(|_| Error::MissingTrustingPeriod)?; + .map_err(|_| TendermintClientError::MissingTrustingPeriod)?; let unbonding_period = raw .unbonding_period - .ok_or(Error::MissingUnbondingPeriod)? + .ok_or(TendermintClientError::MissingUnbondingPeriod)? .try_into() - .map_err(|_| Error::MissingUnbondingPeriod)?; + .map_err(|_| TendermintClientError::MissingUnbondingPeriod)?; let max_clock_drift = raw .max_clock_drift - .ok_or(Error::NegativeMaxClockDrift)? + .ok_or(TendermintClientError::InvalidMaxClockDrift)? .try_into() - .map_err(|_| Error::NegativeMaxClockDrift)?; + .map_err(|_| TendermintClientError::InvalidMaxClockDrift)?; let latest_height = raw .latest_height - .ok_or(Error::MissingLatestHeight)? + .ok_or(TendermintClientError::MissingLatestHeight)? .try_into() - .map_err(|_| Error::MissingLatestHeight)?; + .map_err(|_| TendermintClientError::MissingLatestHeight)?; // NOTE: In `RawClientState`, a `frozen_height` of `0` means "not // frozen". See: // https://github.com/cosmos/ibc-go/blob/8422d0c4c35ef970539466c5bdec1cd27369bab3/modules/light-clients/07-tendermint/types/client_state.go#L74 - let frozen_height = - Height::try_from(raw.frozen_height.ok_or(Error::MissingFrozenHeight)?).ok(); + let frozen_height = Height::try_from( + raw.frozen_height + .ok_or(TendermintClientError::MissingFrozenHeight)?, + ) + .ok(); // We use set this deprecated field just so that we can properly convert // it back in its raw form @@ -346,9 +351,7 @@ impl TryFrom for ClientState { match raw.type_url.as_str() { TENDERMINT_CLIENT_STATE_TYPE_URL => decode_client_state(&raw.value), - _ => Err(ClientError::UnknownClientStateType { - client_state_type: raw.type_url, - }), + _ => Err(ClientError::InvalidClientStateType(raw.type_url)), } } } @@ -514,7 +517,7 @@ mod tests { for test in tests { let p = test.params.clone(); - let cs_result: Result = ClientState::new( + let cs_result: Result = ClientState::new( p.id, p.trust_level, p.trusting_period, diff --git a/ibc-clients/ics07-tendermint/types/src/consensus_state.rs b/ibc-clients/ics07-tendermint/types/src/consensus_state.rs index bc4e675c8..ec66fb264 100644 --- a/ibc-clients/ics07-tendermint/types/src/consensus_state.rs +++ b/ibc-clients/ics07-tendermint/types/src/consensus_state.rs @@ -11,7 +11,7 @@ use tendermint::time::Time; use tendermint::Hash; use tendermint_proto::google::protobuf as tpb; -use crate::error::Error; +use crate::error::TendermintClientError; use crate::header::Header; pub const TENDERMINT_CONSENSUS_STATE_TYPE_URL: &str = @@ -47,32 +47,33 @@ impl ConsensusState { impl Protobuf for ConsensusState {} impl TryFrom for ConsensusState { - type Error = Error; + type Error = TendermintClientError; fn try_from(raw: RawConsensusState) -> Result { let proto_root = raw .root - .ok_or(Error::InvalidRawClientState { - reason: "missing commitment root".into(), + .ok_or(TendermintClientError::InvalidRawClientState { + description: "missing commitment root".into(), })? .hash; let ibc_proto::google::protobuf::Timestamp { seconds, nanos } = - raw.timestamp.ok_or(Error::InvalidRawClientState { - reason: "missing timestamp".into(), - })?; + raw.timestamp + .ok_or(TendermintClientError::InvalidRawClientState { + description: "missing timestamp".into(), + })?; // FIXME: shunts like this are necessary due to // https://github.com/informalsystems/tendermint-rs/issues/1053 let proto_timestamp = tpb::Timestamp { seconds, nanos }; - let timestamp = proto_timestamp - .try_into() - .map_err(|e| Error::InvalidRawClientState { - reason: format!("invalid timestamp: {e}"), - })?; + let timestamp = proto_timestamp.try_into().map_err(|e| { + TendermintClientError::InvalidRawClientState { + description: format!("invalid timestamp: {e}"), + } + })?; let next_validators_hash = Hash::from_bytes(Algorithm::Sha256, &raw.next_validators_hash) - .map_err(|e| Error::InvalidRawClientState { - reason: e.to_string(), + .map_err(|e| TendermintClientError::InvalidRawClientState { + description: e.to_string(), })?; Ok(Self { @@ -116,9 +117,7 @@ impl TryFrom for ConsensusState { match raw.type_url.as_str() { TENDERMINT_CONSENSUS_STATE_TYPE_URL => decode_consensus_state(&raw.value), - _ => Err(ClientError::UnknownConsensusStateType { - consensus_state_type: raw.type_url, - }), + _ => Err(ClientError::InvalidConsensusStateType(raw.type_url)), } } } diff --git a/ibc-clients/ics07-tendermint/types/src/error.rs b/ibc-clients/ics07-tendermint/types/src/error.rs index 264e398a0..45f00354e 100644 --- a/ibc-clients/ics07-tendermint/types/src/error.rs +++ b/ibc-clients/ics07-tendermint/types/src/error.rs @@ -4,10 +4,8 @@ use core::time::Duration; use displaydoc::Display; use ibc_core_client_types::error::ClientError; -use ibc_core_client_types::Height; use ibc_core_commitment_types::error::CommitmentError; use ibc_core_host_types::error::IdentifierError; -use ibc_core_host_types::identifiers::ClientId; use ibc_primitives::prelude::*; use ibc_primitives::TimestampError; use tendermint::{Error as TendermintError, Hash}; @@ -15,32 +13,29 @@ use tendermint_light_client_verifier::errors::VerificationErrorDetail as LightCl use tendermint_light_client_verifier::operations::VotingPowerTally; use tendermint_light_client_verifier::Verdict; -/// The main error type +/// The main error type for the Tendermint light client #[derive(Debug, Display)] -pub enum Error { +pub enum TendermintClientError { /// invalid identifier: `{0}` InvalidIdentifier(IdentifierError), - /// invalid header, failed basic validation: `{reason}`, error: `{error}` - InvalidHeader { - reason: String, - error: TendermintError, - }, - /// invalid client state trust threshold: `{reason}` - InvalidTrustThreshold { reason: String }, - /// invalid tendermint client state trust threshold error: `{0}` - InvalidTendermintTrustThreshold(TendermintError), - /// invalid client state max clock drift: `{reason}` - InvalidMaxClockDrift { reason: String }, - /// invalid client state latest height: `{reason}` - InvalidLatestHeight { reason: String }, - /// missing signed header - MissingSignedHeader, - /// invalid header, failed basic validation: `{reason}` - Validation { reason: String }, + /// invalid client state trust threshold: `{description}` + InvalidTrustThreshold { description: String }, + /// invalid clock drift; must be greater than 0 + InvalidMaxClockDrift, /// invalid client proof specs: `{0}` InvalidProofSpec(CommitmentError), - /// invalid raw client state: `{reason}` - InvalidRawClientState { reason: String }, + /// invalid raw client state: `{description}` + InvalidRawClientState { description: String }, + /// invalid raw header error: `{0}` + InvalidRawHeader(TendermintError), + /// invalid raw misbehaviour: `{description}` + InvalidRawMisbehaviour { description: String }, + /// invalid header timestamp: `{0}` + InvalidHeaderTimestamp(TimestampError), + /// invalid header height: `{0}` + InvalidHeaderHeight(u64), + /// missing signed header + MissingSignedHeader, /// missing validator set MissingValidatorSet, /// missing trusted next validator set @@ -51,77 +46,55 @@ pub enum Error { MissingTrustingPeriod, /// missing unbonding period MissingUnbondingPeriod, - /// negative max clock drift - NegativeMaxClockDrift, /// missing the latest height MissingLatestHeight, - /// invalid raw header error: `{0}` - InvalidRawHeader(TendermintError), - /// invalid raw misbehaviour: `{reason}` - InvalidRawMisbehaviour { reason: String }, - /// invalid header timestamp: `{0}` - InvalidHeaderTimestamp(TimestampError), - /// header revision height = `{height}` is invalid - InvalidHeaderHeight { height: u64 }, - /// frozen height is missing + /// missing frozen height MissingFrozenHeight, - /// the header's trusted revision number (`{trusted_revision}`) and the update's revision number (`{header_revision}`) should be the same - MismatchHeightRevisions { - trusted_revision: u64, - header_revision: u64, - }, - /// the given chain-id (`{given}`) does not match the chain-id of the client (`{expected}`) - MismatchHeaderChainId { given: String, expected: String }, - /// not enough trust because insufficient validators overlap: `{reason}` - NotEnoughTrustedValsSigned { reason: VotingPowerTally }, - /// verification failed: `{detail}` - VerificationError { detail: Box }, - /// Processed time or height for the client `{client_id}` at height `{height}` not found - UpdateMetaDataNotFound { client_id: ClientId, height: Height }, - /// The given hash of the validators does not match the given hash in the signed header. Expected: `{signed_header_validators_hash}`, got: `{validators_hash}` - MismatchValidatorsHashes { - validators_hash: Hash, - signed_header_validators_hash: Hash, - }, - /// current timestamp minus the latest consensus state timestamp is greater than or equal to the trusting period (`{duration_since_consensus_state:?}` >= `{trusting_period:?}`) - ConsensusStateTimestampGteTrustingPeriod { + /// mismatched revision heights: expected `{expected}`, actual `{actual}` + MismatchedRevisionHeights { expected: u64, actual: u64 }, + /// mismatched header chain ids: expected `{expected}`, actual `{actual}` + MismatchedHeaderChainIds { expected: String, actual: String }, + /// mismatched validator hashes: expected `{expected}`, actual `{actual}` + MismatchedValidatorHashes { expected: Hash, actual: Hash }, + /// empty client state upgrade-path key + EmptyUpgradePathKey, + /// failed to verify header: `{0}` + FailedToVerifyHeader(Box), + /// insufficient validator overlap: `{0}` + InsufficientValidatorOverlap(VotingPowerTally), + /// insufficient trusting period `{trusting_period:?}`; should be > consensus state timestamp `{duration_since_consensus_state:?}` + InsufficientTrustingPeriod { duration_since_consensus_state: Duration, trusting_period: Duration, }, - /// headers block hashes are equal - MisbehaviourHeadersBlockHashesEqual, - /// headers are not at the same height and are monotonically increasing - MisbehaviourHeadersNotAtSameHeight, } #[cfg(feature = "std")] -impl std::error::Error for Error { +impl std::error::Error for TendermintClientError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match &self { Self::InvalidIdentifier(e) => Some(e), - Self::InvalidHeader { error: e, .. } - | Self::InvalidTendermintTrustThreshold(e) - | Self::InvalidRawHeader(e) => Some(e), + Self::InvalidRawHeader(e) => Some(e), _ => None, } } } -impl From for ClientError { - fn from(e: Error) -> Self { +impl From for ClientError { + fn from(e: TendermintClientError) -> Self { Self::ClientSpecific { description: e.to_string(), } } } -impl From for Error { +impl From for TendermintClientError { fn from(e: IdentifierError) -> Self { Self::InvalidIdentifier(e) } } -impl From for Error { +impl From for TendermintClientError { fn from(e: CommitmentError) -> Self { Self::InvalidProofSpec(e) } @@ -131,14 +104,16 @@ pub trait IntoResult { fn into_result(self) -> Result; } -impl IntoResult<(), Error> for Verdict { - fn into_result(self) -> Result<(), Error> { +impl IntoResult<(), TendermintClientError> for Verdict { + fn into_result(self) -> Result<(), TendermintClientError> { match self { Verdict::Success => Ok(()), - Verdict::NotEnoughTrust(reason) => Err(Error::NotEnoughTrustedValsSigned { reason }), - Verdict::Invalid(detail) => Err(Error::VerificationError { - detail: Box::new(detail), - }), + Verdict::NotEnoughTrust(tally) => { + Err(TendermintClientError::InsufficientValidatorOverlap(tally)) + } + Verdict::Invalid(detail) => Err(TendermintClientError::FailedToVerifyHeader(Box::new( + detail, + ))), } } } diff --git a/ibc-clients/ics07-tendermint/types/src/header.rs b/ibc-clients/ics07-tendermint/types/src/header.rs index 6ee8069f0..72fc83333 100644 --- a/ibc-clients/ics07-tendermint/types/src/header.rs +++ b/ibc-clients/ics07-tendermint/types/src/header.rs @@ -20,7 +20,7 @@ use tendermint::validator::Set as ValidatorSet; use tendermint::{Hash, Time}; use tendermint_light_client_verifier::types::{TrustedBlockState, UntrustedBlockState}; -use crate::error::Error; +use crate::error::TendermintClientError; pub const TENDERMINT_HEADER_TYPE_URL: &str = "/ibc.lightclients.tendermint.v1.Header"; @@ -47,12 +47,12 @@ impl Display for Header { } impl Header { - pub fn timestamp(&self) -> Result { + pub fn timestamp(&self) -> Result { self.signed_header .header .time .try_into() - .map_err(Error::InvalidHeaderTimestamp) + .map_err(TendermintClientError::InvalidHeaderTimestamp) } pub fn height(&self) -> Height { @@ -78,7 +78,7 @@ impl Header { chain_id: &'a TmChainId, header_time: Time, next_validators_hash: Hash, - ) -> Result, Error> { + ) -> Result, TendermintClientError> { Ok(TrustedBlockState { chain_id, header_time, @@ -86,18 +86,23 @@ impl Header { .trusted_height .revision_height() .try_into() - .map_err(|_| Error::InvalidHeaderHeight { - height: self.trusted_height.revision_height(), + .map_err(|_| { + TendermintClientError::InvalidHeaderHeight( + self.trusted_height.revision_height(), + ) })?, next_validators: &self.trusted_next_validator_set, next_validators_hash, }) } - pub fn verify_chain_id_version_matches_height(&self, chain_id: &ChainId) -> Result<(), Error> { + pub fn verify_chain_id_version_matches_height( + &self, + chain_id: &ChainId, + ) -> Result<(), TendermintClientError> { if self.height().revision_number() != chain_id.revision_number() { - return Err(Error::MismatchHeaderChainId { - given: self.signed_header.header.chain_id.to_string(), + return Err(TendermintClientError::MismatchedHeaderChainIds { + actual: self.signed_header.header.chain_id.to_string(), expected: chain_id.to_string(), }); } @@ -114,8 +119,8 @@ impl Header { if &self.trusted_next_validator_set.hash_with::() == trusted_next_validator_hash { Ok(()) } else { - Err(ClientError::HeaderVerificationFailure { - reason: + Err(ClientError::FailedHeaderVerification { + description: "header trusted next validator set hash does not match hash stored on chain" .to_string(), }) @@ -123,11 +128,13 @@ impl Header { } /// Checks if the fields of a given header are consistent with the trusted fields of this header. - pub fn validate_basic(&self) -> Result<(), Error> { + pub fn validate_basic( + &self, + ) -> Result<(), TendermintClientError> { if self.height().revision_number() != self.trusted_height.revision_number() { - return Err(Error::MismatchHeightRevisions { - trusted_revision: self.trusted_height.revision_number(), - header_revision: self.height().revision_number(), + return Err(TendermintClientError::MismatchedRevisionHeights { + expected: self.trusted_height.revision_number(), + actual: self.height().revision_number(), }); } @@ -136,17 +143,17 @@ impl Header { // based on) must be smaller than height of the new header that we're // installing. if self.trusted_height >= self.height() { - return Err(Error::InvalidHeaderHeight { - height: self.height().revision_height(), - }); + return Err(TendermintClientError::InvalidHeaderHeight( + self.height().revision_height(), + )); } let validators_hash = self.validator_set.hash_with::(); if validators_hash != self.signed_header.header.validators_hash { - return Err(Error::MismatchValidatorsHashes { - signed_header_validators_hash: self.signed_header.header.validators_hash, - validators_hash, + return Err(TendermintClientError::MismatchedValidatorHashes { + expected: self.signed_header.header.validators_hash, + actual: validators_hash, }); } @@ -157,32 +164,29 @@ impl Header { impl Protobuf for Header {} impl TryFrom for Header { - type Error = Error; + type Error = TendermintClientError; fn try_from(raw: RawHeader) -> Result { let header = Self { signed_header: raw .signed_header - .ok_or(Error::MissingSignedHeader)? + .ok_or(TendermintClientError::MissingSignedHeader)? .try_into() - .map_err(|e| Error::InvalidHeader { - reason: "signed header conversion".to_string(), - error: e, - })?, + .map_err(TendermintClientError::InvalidRawHeader)?, validator_set: raw .validator_set - .ok_or(Error::MissingValidatorSet)? + .ok_or(TendermintClientError::MissingValidatorSet)? .try_into() - .map_err(Error::InvalidRawHeader)?, + .map_err(TendermintClientError::InvalidRawHeader)?, trusted_height: raw .trusted_height .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or(Error::MissingTrustedHeight)?, + .ok_or(TendermintClientError::MissingTrustedHeight)?, trusted_next_validator_set: raw .trusted_validators - .ok_or(Error::MissingTrustedNextValidatorSet)? + .ok_or(TendermintClientError::MissingTrustedNextValidatorSet)? .try_into() - .map_err(Error::InvalidRawHeader)?, + .map_err(TendermintClientError::InvalidRawHeader)?, }; Ok(header) @@ -203,9 +207,7 @@ impl TryFrom for Header { } match raw.type_url.as_str() { TENDERMINT_HEADER_TYPE_URL => decode_header(&raw.value), - _ => Err(ClientError::UnknownHeaderType { - header_type: raw.type_url, - }), + _ => Err(ClientError::InvalidHeaderType(raw.type_url)), } } } diff --git a/ibc-clients/ics07-tendermint/types/src/misbehaviour.rs b/ibc-clients/ics07-tendermint/types/src/misbehaviour.rs index c51f1892f..dc35a34ec 100644 --- a/ibc-clients/ics07-tendermint/types/src/misbehaviour.rs +++ b/ibc-clients/ics07-tendermint/types/src/misbehaviour.rs @@ -9,7 +9,7 @@ use ibc_proto::Protobuf; use tendermint::crypto::Sha256; use tendermint::merkle::MerkleHash; -use crate::error::Error; +use crate::error::TendermintClientError; use crate::header::Header; pub const TENDERMINT_MISBEHAVIOUR_TYPE_URL: &str = "/ibc.lightclients.tendermint.v1.Misbehaviour"; @@ -44,20 +44,22 @@ impl Misbehaviour { &self.header2 } - pub fn validate_basic(&self) -> Result<(), Error> { + pub fn validate_basic( + &self, + ) -> Result<(), TendermintClientError> { self.header1.validate_basic::()?; self.header2.validate_basic::()?; if self.header1.signed_header.header.chain_id != self.header2.signed_header.header.chain_id { - return Err(Error::InvalidRawMisbehaviour { - reason: "headers must have identical chain_ids".to_owned(), + return Err(TendermintClientError::InvalidRawMisbehaviour { + description: "headers must have identical chain_ids".to_owned(), }); } if self.header1.height() < self.header2.height() { - return Err(Error::InvalidRawMisbehaviour { - reason: format!( + return Err(TendermintClientError::InvalidRawMisbehaviour { + description: format!( "header1 height is less than header2 height ({} < {})", self.header1.height(), self.header2.height() @@ -72,22 +74,22 @@ impl Misbehaviour { impl Protobuf for Misbehaviour {} impl TryFrom for Misbehaviour { - type Error = Error; + type Error = TendermintClientError; #[allow(deprecated)] fn try_from(raw: RawMisbehaviour) -> Result { let client_id = raw.client_id.parse()?; let header1: Header = raw .header_1 - .ok_or_else(|| Error::InvalidRawMisbehaviour { - reason: "missing header1".into(), + .ok_or_else(|| TendermintClientError::InvalidRawMisbehaviour { + description: "missing header1".into(), })? .try_into()?; let header2: Header = raw .header_2 - .ok_or_else(|| Error::InvalidRawMisbehaviour { - reason: "missing header2".into(), + .ok_or_else(|| TendermintClientError::InvalidRawMisbehaviour { + description: "missing header2".into(), })? .try_into()?; @@ -121,9 +123,7 @@ impl TryFrom for Misbehaviour { } match raw.type_url.as_str() { TENDERMINT_MISBEHAVIOUR_TYPE_URL => decode_misbehaviour(&raw.value), - _ => Err(ClientError::UnknownMisbehaviourType { - misbehaviour_type: raw.type_url, - }), + _ => Err(ClientError::InvalidMisbehaviourType(raw.type_url)), } } } diff --git a/ibc-clients/ics07-tendermint/types/src/trust_threshold.rs b/ibc-clients/ics07-tendermint/types/src/trust_threshold.rs index 830bba74d..401390b5e 100644 --- a/ibc-clients/ics07-tendermint/types/src/trust_threshold.rs +++ b/ibc-clients/ics07-tendermint/types/src/trust_threshold.rs @@ -106,11 +106,9 @@ impl TryFrom for TrustThresholdFraction { type Error = ClientError; fn try_from(t: TrustThreshold) -> Result { - Self::new(t.numerator, t.denominator).map_err(|_| { - ClientError::FailedTrustThresholdConversion { - numerator: t.numerator, - denominator: t.denominator, - } + Self::new(t.numerator, t.denominator).map_err(|_| ClientError::InvalidTrustThreshold { + numerator: t.numerator, + denominator: t.denominator, }) } } diff --git a/ibc-clients/ics08-wasm/types/src/client_state.rs b/ibc-clients/ics08-wasm/types/src/client_state.rs index e9570b320..c8c4aca41 100644 --- a/ibc-clients/ics08-wasm/types/src/client_state.rs +++ b/ibc-clients/ics08-wasm/types/src/client_state.rs @@ -5,7 +5,7 @@ use ibc_primitives::prelude::*; use ibc_primitives::proto::{Any, Protobuf}; use ibc_proto::ibc::lightclients::wasm::v1::ClientState as RawClientState; -use crate::error::Error; +use crate::error::WasmClientError; #[cfg(feature = "serde")] use crate::serializer::Base64; use crate::Bytes; @@ -38,18 +38,14 @@ impl From for RawClientState { } impl TryFrom for ClientState { - type Error = Error; + type Error = WasmClientError; fn try_from(raw: RawClientState) -> Result { let latest_height = raw .latest_height - .ok_or(Error::InvalidLatestHeight { - reason: "missing latest height".to_string(), - })? + .ok_or(WasmClientError::MissingLatestHeight)? .try_into() - .map_err(|_| Error::InvalidLatestHeight { - reason: "invalid protobuf latest height".to_string(), - })?; + .map_err(|_| WasmClientError::InvalidLatestHeight)?; Ok(Self { data: raw.data, checksum: raw.checksum, @@ -70,22 +66,24 @@ impl From for Any { } impl TryFrom for ClientState { - type Error = Error; + type Error = WasmClientError; fn try_from(any: Any) -> Result { - fn decode_client_state(value: &[u8]) -> Result { - let client_state = - Protobuf::::decode(value).map_err(|e| Error::DecodeError { - reason: e.to_string(), - })?; + fn decode_client_state(value: &[u8]) -> Result { + let client_state = Protobuf::::decode(value).map_err(|e| { + WasmClientError::DecodingError { + description: e.to_string(), + } + })?; Ok(client_state) } match any.type_url.as_str() { WASM_CLIENT_STATE_TYPE_URL => decode_client_state(&any.value), - _ => Err(Error::DecodeError { - reason: "type_url does not match".into(), + other_type_url => Err(WasmClientError::MismatchedTypeUrls { + expected: WASM_CLIENT_STATE_TYPE_URL.to_string(), + actual: other_type_url.to_string(), }), } } diff --git a/ibc-clients/ics08-wasm/types/src/error.rs b/ibc-clients/ics08-wasm/types/src/error.rs index eb48a2855..ecb3e1984 100644 --- a/ibc-clients/ics08-wasm/types/src/error.rs +++ b/ibc-clients/ics08-wasm/types/src/error.rs @@ -6,26 +6,30 @@ use ibc_primitives::prelude::*; /// The main error type #[derive(Debug, Display)] -pub enum Error { +pub enum WasmClientError { /// invalid identifier: `{0}` InvalidIdentifier(IdentifierError), - /// decoding error: `{reason}` - DecodeError { reason: String }, - /// invalid client state latest height: `{reason}` - InvalidLatestHeight { reason: String }, + /// invalid client state latest height + InvalidLatestHeight, + /// missing latest height + MissingLatestHeight, + /// mismatched type URLs: expected `{expected}`, actual `{actual}` + MismatchedTypeUrls { expected: String, actual: String }, + /// decoding error: `{description}` + DecodingError { description: String }, } #[cfg(feature = "std")] -impl std::error::Error for Error { +impl std::error::Error for WasmClientError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { - Error::InvalidIdentifier(err) => Some(err), + Self::InvalidIdentifier(err) => Some(err), _ => None, } } } -impl From for Error { +impl From for WasmClientError { fn from(e: IdentifierError) -> Self { Self::InvalidIdentifier(e) } diff --git a/ibc-clients/ics08-wasm/types/src/msgs/migrate_contract.rs b/ibc-clients/ics08-wasm/types/src/msgs/migrate_contract.rs index 40c737457..cd0bd9d3d 100644 --- a/ibc-clients/ics08-wasm/types/src/msgs/migrate_contract.rs +++ b/ibc-clients/ics08-wasm/types/src/msgs/migrate_contract.rs @@ -6,7 +6,7 @@ use ibc_primitives::Signer; use ibc_proto::ibc::lightclients::wasm::v1::MsgMigrateContract as RawMsgMigrateContract; use ibc_proto::Protobuf; -use crate::error::Error; +use crate::error::WasmClientError; use crate::Bytes; pub const MIGRATE_CONTRACT_TYPE_URL: &str = "/ibc.lightclients.wasm.v1.MsgMigrateContract"; @@ -34,7 +34,7 @@ impl From for RawMsgMigrateContract { } impl TryFrom for MsgMigrateContract { - type Error = Error; + type Error = WasmClientError; fn try_from(value: RawMsgMigrateContract) -> Result { Ok(Self { diff --git a/ibc-core/ics02-client/src/handler/create_client.rs b/ibc-core/ics02-client/src/handler/create_client.rs index d6413596f..263f8ffa4 100644 --- a/ibc-core/ics02-client/src/handler/create_client.rs +++ b/ibc-core/ics02-client/src/handler/create_client.rs @@ -4,6 +4,7 @@ use ibc_core_client_context::prelude::*; use ibc_core_client_types::error::ClientError; use ibc_core_client_types::events::CreateClient; use ibc_core_client_types::msgs::MsgCreateClient; +use ibc_core_client_types::Status; use ibc_core_handler_types::error::ContextError; use ibc_core_handler_types::events::{IbcEvent, MessageEvent}; use ibc_core_host::{ClientStateMut, ClientStateRef, ExecutionContext, ValidationContext}; @@ -35,10 +36,7 @@ where let status = client_state.status(client_val_ctx, &client_id)?; if status.is_frozen() { - return Err(ClientError::ClientFrozen { - description: "the client is frozen".to_string(), - } - .into()); + return Err(ClientError::InvalidStatus(Status::Frozen).into()); }; let host_timestamp = ctx.host_timestamp()?; @@ -46,7 +44,7 @@ where client_state.verify_consensus_state(consensus_state, &host_timestamp)?; if client_val_ctx.client_state(&client_id).is_ok() { - return Err(ClientError::ClientStateAlreadyExists { client_id }.into()); + return Err(ClientError::AlreadyExistingClientState(client_id).into()); }; Ok(()) diff --git a/ibc-core/ics02-client/src/handler/recover_client.rs b/ibc-core/ics02-client/src/handler/recover_client.rs index 1676c6e80..7994edab3 100644 --- a/ibc-core/ics02-client/src/handler/recover_client.rs +++ b/ibc-core/ics02-client/src/handler/recover_client.rs @@ -30,7 +30,7 @@ where let substitute_height = substitute_client_state.latest_height(); if subject_height >= substitute_height { - return Err(ClientError::ClientRecoveryHeightMismatch { + return Err(ClientError::NotAllowedClientRecoveryHeights { subject_height, substitute_height, } diff --git a/ibc-core/ics02-client/src/handler/update_client.rs b/ibc-core/ics02-client/src/handler/update_client.rs index 81e140b8a..66ce95ce1 100644 --- a/ibc-core/ics02-client/src/handler/update_client.rs +++ b/ibc-core/ics02-client/src/handler/update_client.rs @@ -64,8 +64,8 @@ where ctx.emit_ibc_event(event)?; } else { if !matches!(update_kind, UpdateKind::UpdateClient) { - return Err(ClientError::MisbehaviourHandlingFailure { - reason: "misbehaviour submitted, but none found".to_string(), + return Err(ClientError::FailedMisbehaviourHandling { + description: "misbehaviour submitted, but none found".to_string(), } .into()); } diff --git a/ibc-core/ics02-client/src/handler/upgrade_client.rs b/ibc-core/ics02-client/src/handler/upgrade_client.rs index a072988a2..7dce4cdaa 100644 --- a/ibc-core/ics02-client/src/handler/upgrade_client.rs +++ b/ibc-core/ics02-client/src/handler/upgrade_client.rs @@ -38,7 +38,7 @@ where ); let old_consensus_state = client_val_ctx .consensus_state(&old_client_cons_state_path) - .map_err(|_| ClientError::ConsensusStateNotFound { + .map_err(|_| ClientError::MissingConsensusState { client_id, height: old_client_state.latest_height(), })?; diff --git a/ibc-core/ics02-client/types/src/error.rs b/ibc-core/ics02-client/types/src/error.rs index cda8b0f05..9b16557ad 100644 --- a/ibc-core/ics02-client/types/src/error.rs +++ b/ibc-core/ics02-client/types/src/error.rs @@ -5,7 +5,7 @@ use core::convert::Infallible; use displaydoc::Display; use ibc_core_commitment_types::error::CommitmentError; use ibc_core_host_types::error::IdentifierError; -use ibc_core_host_types::identifiers::{ClientId, ClientType}; +use ibc_core_host_types::identifiers::ClientId; use ibc_primitives::prelude::*; use ibc_primitives::Timestamp; @@ -17,96 +17,75 @@ use crate::height::Height; pub enum ClientError { /// upgrade client error: `{0}` Upgrade(UpgradeClientError), - /// client is frozen with description: `{description}` - ClientFrozen { description: String }, - /// client is not active. Status=`{status}` - ClientNotActive { status: Status }, - /// client is not frozen or expired. Status=`{status}` - ClientNotInactive { status: Status }, - /// client state not found: `{client_id}` - ClientStateNotFound { client_id: ClientId }, - /// client state already exists: `{client_id}` - ClientStateAlreadyExists { client_id: ClientId }, - /// Substitute client height `{substitute_height}` is not greater than subject client height `{subject_height}` during client recovery - ClientRecoveryHeightMismatch { - subject_height: Height, - substitute_height: Height, - }, - /// Subject and substitute client state mismatch during client recovery - ClientRecoveryStateMismatch, - /// consensus state not found at: `{client_id}` at height `{height}` - ConsensusStateNotFound { client_id: ClientId, height: Height }, - /// Processed time or height for the client `{client_id}` at height `{height}` not found - UpdateMetaDataNotFound { client_id: ClientId, height: Height }, - /// header verification failed with reason: `{reason}` - HeaderVerificationFailure { reason: String }, - /// failed to build trust threshold from fraction: `{numerator}`/`{denominator}` + /// invalid client status: `{0}` + InvalidStatus(Status), + /// invalid trust threshold: `{numerator}`/`{denominator}` InvalidTrustThreshold { numerator: u64, denominator: u64 }, - /// failed to build Tendermint domain type trust threshold from fraction: `{numerator}`/`{denominator}` - FailedTrustThresholdConversion { numerator: u64, denominator: u64 }, - /// unknown client state type: `{client_state_type}` - UnknownClientStateType { client_state_type: String }, - /// unknown client consensus state type: `{consensus_state_type}` - UnknownConsensusStateType { consensus_state_type: String }, - /// unknown header type: `{header_type}` - UnknownHeaderType { header_type: String }, - /// unknown misbehaviour type: `{misbehaviour_type}` - UnknownMisbehaviourType { misbehaviour_type: String }, + /// invalid client state type: `{0}` + InvalidClientStateType(String), + /// invalid client consensus state type: `{0}` + InvalidConsensusStateType(String), + /// invalid header type: `{0}` + InvalidHeaderType(String), + /// invalid update client message + InvalidUpdateClientMessage, + /// invalid client identifier: `{0}` + InvalidClientIdentifier(IdentifierError), + /// invalid raw header: `{description}` + InvalidRawHeader { description: String }, + /// invalid misbehaviour type: `{0}` + InvalidMisbehaviourType(String), + /// invalid height; cannot be zero or negative + InvalidHeight, + /// invalid proof height; expected `{actual}` >= `{expected}` + InvalidProofHeight { actual: Height, expected: Height }, + /// invalid consensus state timestamp: `{0}` + InvalidConsensusStateTimestamp(Timestamp), + /// invalid attribute key: `{0}` + InvalidAttributeKey(String), + /// invalid attribute value: `{0}` + InvalidAttributeValue(String), + /// missing client state for client: `{0}` + MissingClientState(ClientId), + /// missing consensus state for client `{client_id}` at height `{height}` + MissingConsensusState { client_id: ClientId, height: Height }, + /// missing update client metadata for client `{client_id}` at height `{height}` + MissingUpdateMetaData { client_id: ClientId, height: Height }, /// missing raw client state MissingRawClientState, /// missing raw client consensus state MissingRawConsensusState, - /// invalid client id in the update client message: `{0}` - InvalidMsgUpdateClientId(IdentifierError), - /// invalid client id in recover client message: `{0}` - InvalidMsgRecoverClientId(IdentifierError), - /// invalid client identifier error: `{0}` - InvalidClientIdentifier(IdentifierError), - /// invalid raw header error: `{reason}` - InvalidRawHeader { reason: String }, /// missing raw client message - MissingClientMessage, - /// invalid raw misbehaviour error: `{0}` - InvalidRawMisbehaviour(IdentifierError), + MissingRawClientMessage, /// missing raw misbehaviour MissingRawMisbehaviour, - /// revision height cannot be zero - InvalidHeight, - /// height cannot end up zero or negative - InvalidHeightResult, - /// the proof height is insufficient: latest_height=`{latest_height}` proof_height=`{proof_height}` - InvalidProofHeight { - latest_height: Height, - proof_height: Height, + /// missing local consensus state at `{0}` + MissingLocalConsensusState(Height), + /// missing attribute key + MissingAttributeKey, + /// missing attribute value + MissingAttributeValue, + /// client state already exists: `{0}` + AlreadyExistingClientState(ClientId), + /// mismatched client recovery states + MismatchedClientRecoveryStates, + /// client recovery heights not allowed: expected substitute client height `{substitute_height}` > subject client height `{subject_height}` + NotAllowedClientRecoveryHeights { + subject_height: Height, + substitute_height: Height, }, - /// invalid commitment proof bytes error: `{0}` - InvalidCommitmentProof(CommitmentError), - /// mismatch between client and arguments types - ClientArgsTypeMismatch { client_type: ClientType }, - /// timestamp is invalid or missing, timestamp=`{time1}`, now=`{time2}` - InvalidConsensusStateTimestamp { time1: Timestamp, time2: Timestamp }, - /// the local consensus state could not be retrieved for height `{height}` - MissingLocalConsensusState { height: Height }, - /// invalid signer error: `{reason}` - InvalidSigner { reason: String }, - /// ics23 verification failure error: `{0}` - Ics23Verification(CommitmentError), - /// misbehaviour handling failed with reason: `{reason}` - MisbehaviourHandlingFailure { reason: String }, + /// failed ICS23 verification: `{0}` + FailedICS23Verification(CommitmentError), + /// failed header verification: `{description}` + FailedHeaderVerification { description: String }, + /// failed misbehaviour handling: `{description}` + FailedMisbehaviourHandling { description: String }, /// client-specific error: `{description}` ClientSpecific { description: String }, - /// client counter overflow error - CounterOverflow, - /// update client message did not contain valid header or misbehaviour - InvalidUpdateClientMessage, + + // TODO(seanchen1991): Incorporate these errors into their own variants /// other error: `{description}` Other { description: String }, - /// invalid attribute key: `{attribute_key}` - InvalidAttributeKey { attribute_key: String }, - /// invalid attribute value: `{attribute_value}` - InvalidAttributeValue { attribute_value: String }, - /// Missing attribute key: `{attribute_key}` - MissingAttributeKey { attribute_key: String }, } impl From<&'static str> for ClientError { @@ -123,14 +102,18 @@ impl From for ClientError { } } +impl From for ClientError { + fn from(e: CommitmentError) -> Self { + Self::FailedICS23Verification(e) + } +} + #[cfg(feature = "std")] impl std::error::Error for ClientError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match &self { - Self::InvalidMsgUpdateClientId(e) - | Self::InvalidClientIdentifier(e) - | Self::InvalidRawMisbehaviour(e) => Some(e), - Self::InvalidCommitmentProof(e) | Self::Ics23Verification(e) => Some(e), + Self::InvalidClientIdentifier(e) => Some(e), + Self::FailedICS23Verification(e) => Some(e), _ => None, } } @@ -139,23 +122,35 @@ impl std::error::Error for ClientError { /// Encodes all the possible upgrade client errors #[derive(Debug, Display)] pub enum UpgradeClientError { - /// invalid proof for the upgraded client state error: `{0}` + /// invalid proof for the upgraded client state: `{0}` InvalidUpgradeClientProof(CommitmentError), - /// invalid proof for the upgraded consensus state error: `{0}` + /// invalid proof for the upgraded consensus state: `{0}` InvalidUpgradeConsensusStateProof(CommitmentError), - /// upgraded client height `{upgraded_height}` must be at greater than current client height `{client_height}` - LowUpgradeHeight { + /// invalid upgrade path: `{description}` + InvalidUpgradePath { description: String }, + /// invalid upgrade proposal: `{description}` + InvalidUpgradeProposal { description: String }, + /// invalid upgrade plan: `{description}` + InvalidUpgradePlan { description: String }, + /// mismatched type URLs: expected `{expected}`, actual `{actual}` + MismatchedTypeUrls { expected: String, actual: String }, + /// missing upgraded client state + MissingUpgradedClientState, + /// missing upgraded consensus state + MissingUpgradedConsensusState, + /// failed to decode raw upgrade plan: `{description}` + FailedToDecodeRawUpgradePlan { description: String }, + /// failed to store upgrade plan: `{description}` + FailedToStoreUpgradePlan { description: String }, + /// failed to store upgraded client state: `{description}` + FailedToStoreUpgradedClientState { description: String }, + /// failed to store upgraded consensus state: `{description}` + FailedToStoreUpgradedConsensusState { description: String }, + /// insufficient upgrade client height `{upgraded_height}`; must be greater than current client height `{client_height}` + InsufficientUpgradeHeight { upgraded_height: Height, client_height: Height, }, - /// Invalid upgrade path: `{reason}` - InvalidUpgradePath { reason: String }, - /// invalid upgrade proposal: `{reason}` - InvalidUpgradeProposal { reason: String }, - /// invalid upgrade plan: `{reason}` - InvalidUpgradePlan { reason: String }, - /// other upgrade client error: `{reason}` - Other { reason: String }, } impl From for ClientError { diff --git a/ibc-core/ics02-client/types/src/events.rs b/ibc-core/ics02-client/types/src/events.rs index a91e1b413..56585036a 100644 --- a/ibc-core/ics02-client/types/src/events.rs +++ b/ibc-core/ics02-client/types/src/events.rs @@ -58,29 +58,21 @@ impl TryFrom for ClientIdAttribute { fn try_from(value: abci::EventAttribute) -> Result { if let Ok(key_str) = value.key_str() { if key_str != CLIENT_ID_ATTRIBUTE_KEY { - return Err(ClientError::InvalidAttributeKey { - attribute_key: key_str.to_string(), - }); + return Err(ClientError::InvalidAttributeKey(key_str.to_string())); } } else { - return Err(ClientError::InvalidAttributeKey { - attribute_key: String::new(), - }); + return Err(ClientError::MissingAttributeKey); } value .value_str() .map(|value| { - let client_id = - ClientId::from_str(value).map_err(|_| ClientError::InvalidAttributeValue { - attribute_value: value.to_string(), - })?; + let client_id = ClientId::from_str(value) + .map_err(|_| ClientError::InvalidAttributeValue(value.to_string()))?; Ok(ClientIdAttribute { client_id }) }) - .map_err(|_| ClientError::InvalidAttributeValue { - attribute_value: String::new(), - })? + .map_err(|_| ClientError::MissingAttributeValue)? } } @@ -114,30 +106,21 @@ impl TryFrom for ClientTypeAttribute { fn try_from(value: abci::EventAttribute) -> Result { if let Ok(key_str) = value.key_str() { if key_str != CLIENT_TYPE_ATTRIBUTE_KEY { - return Err(ClientError::InvalidAttributeKey { - attribute_key: key_str.to_string(), - }); + return Err(ClientError::InvalidAttributeKey(key_str.to_string())); } } else { - return Err(ClientError::InvalidAttributeKey { - attribute_key: String::new(), - }); + return Err(ClientError::MissingAttributeKey); } value .value_str() .map(|value| { - let client_type = ClientType::from_str(value).map_err(|_| { - ClientError::InvalidAttributeValue { - attribute_value: value.to_string(), - } - })?; + let client_type = ClientType::from_str(value) + .map_err(|_| ClientError::InvalidAttributeValue(value.to_string()))?; Ok(ClientTypeAttribute { client_type }) }) - .map_err(|_| ClientError::InvalidAttributeValue { - attribute_value: String::new(), - })? + .map_err(|_| ClientError::MissingAttributeValue)? } } @@ -171,30 +154,23 @@ impl TryFrom for ConsensusHeightAttribute { fn try_from(value: abci::EventAttribute) -> Result { if let Ok(key_str) = value.key_str() { if key_str != CONSENSUS_HEIGHT_ATTRIBUTE_KEY { - return Err(ClientError::InvalidAttributeKey { - attribute_key: key_str.to_string(), - }); + return Err(ClientError::InvalidAttributeKey(key_str.to_string())); } } else { - return Err(ClientError::InvalidAttributeKey { - attribute_key: String::new(), - }); + return Err(ClientError::MissingAttributeKey); } value .value_str() .map(|value| { - let consensus_height = - Height::from_str(value).map_err(|_| ClientError::InvalidAttributeValue { - attribute_value: value.to_string(), - })?; + let consensus_height = Height::from_str(value) + .map_err(|_| ClientError::InvalidAttributeValue(value.to_string()))?; Ok(ConsensusHeightAttribute { consensus_height }) }) - .map_err(|_| ClientError::InvalidAttributeKey { - attribute_key: String::new(), - })? + .map_err(|_| ClientError::MissingAttributeValue)? } } + #[cfg_attr( feature = "parity-scale-codec", derive( @@ -230,14 +206,10 @@ impl TryFrom for ConsensusHeightsAttribute { fn try_from(value: abci::EventAttribute) -> Result { if let Ok(key_str) = value.key_str() { if key_str != CONSENSUS_HEIGHTS_ATTRIBUTE_KEY { - return Err(ClientError::InvalidAttributeKey { - attribute_key: key_str.to_string(), - }); + return Err(ClientError::InvalidAttributeKey(key_str.to_string())); } } else { - return Err(ClientError::InvalidAttributeKey { - attribute_key: String::new(), - }); + return Err(ClientError::MissingAttributeKey); } value @@ -246,19 +218,14 @@ impl TryFrom for ConsensusHeightsAttribute { let consensus_heights: Vec = value .split(',') .map(|height_str| { - Height::from_str(height_str).map_err(|_| { - ClientError::InvalidAttributeValue { - attribute_value: height_str.to_string(), - } - }) + Height::from_str(height_str) + .map_err(|_| ClientError::InvalidAttributeValue(height_str.to_string())) }) .collect::, ClientError>>()?; Ok(ConsensusHeightsAttribute { consensus_heights }) }) - .map_err(|_| ClientError::InvalidAttributeValue { - attribute_value: String::new(), - })? + .map_err(|_| ClientError::MissingAttributeValue)? } } @@ -298,29 +265,21 @@ impl TryFrom for HeaderAttribute { fn try_from(value: abci::EventAttribute) -> Result { if let Ok(key_str) = value.key_str() { if key_str != HEADER_ATTRIBUTE_KEY { - return Err(ClientError::InvalidAttributeKey { - attribute_key: key_str.to_string(), - }); + return Err(ClientError::InvalidAttributeKey(key_str.to_string())); } } else { - return Err(ClientError::InvalidAttributeKey { - attribute_key: String::new(), - }); + return Err(ClientError::MissingAttributeKey); } value .value_str() .map(|value| { - let header = - hex::decode(value).map_err(|_| ClientError::InvalidAttributeValue { - attribute_value: value.to_string(), - })?; + let header = hex::decode(value) + .map_err(|_| ClientError::InvalidAttributeValue(value.to_string()))?; Ok(HeaderAttribute { header }) }) - .map_err(|_| ClientError::InvalidAttributeValue { - attribute_value: String::new(), - })? + .map_err(|_| ClientError::MissingAttributeValue)? } } @@ -405,12 +364,9 @@ impl TryFrom for CreateClient { Option, ), attribute| { - let key = - attribute - .key_str() - .map_err(|_| ClientError::InvalidAttributeKey { - attribute_key: String::new(), - })?; + let key = attribute + .key_str() + .map_err(|_| ClientError::MissingAttributeKey)?; match key { CLIENT_ID_ATTRIBUTE_KEY => Ok(( @@ -436,17 +392,10 @@ impl TryFrom for CreateClient { Option, Option, )| { - let client_id = client_id.ok_or_else(|| ClientError::MissingAttributeKey { - attribute_key: CLIENT_ID_ATTRIBUTE_KEY.to_string(), - })?; - let client_type = - client_type.ok_or_else(|| ClientError::MissingAttributeKey { - attribute_key: CLIENT_TYPE_ATTRIBUTE_KEY.to_string(), - })?; + let client_id = client_id.ok_or(ClientError::MissingAttributeKey)?; + let client_type = client_type.ok_or(ClientError::MissingAttributeKey)?; let consensus_height = - consensus_height.ok_or_else(|| ClientError::MissingAttributeKey { - attribute_key: CONSENSUS_HEIGHT_ATTRIBUTE_KEY.to_string(), - })?; + consensus_height.ok_or(ClientError::MissingAttributeKey)?; Ok(CreateClient::new( client_id.client_id, @@ -566,12 +515,9 @@ impl TryFrom for UpdateClient { .try_fold( (None, None, None, None, None), |acc: UpdateClientAttributes, attribute| { - let key = - attribute - .key_str() - .map_err(|_| ClientError::InvalidAttributeKey { - attribute_key: String::new(), - })?; + let key = attribute + .key_str() + .map_err(|_| ClientError::MissingAttributeKey)?; match key { CLIENT_ID_ATTRIBUTE_KEY => Ok(( @@ -615,31 +561,17 @@ impl TryFrom for UpdateClient { ) .and_then( |(client_id, client_type, consensus_height, consensus_heights, header)| { - let client_id = client_id - .ok_or_else(|| ClientError::MissingAttributeKey { - attribute_key: CLIENT_ID_ATTRIBUTE_KEY.to_string(), - })? - .client_id; + let client_id = client_id.ok_or(ClientError::MissingAttributeKey)?.client_id; let client_type = client_type - .ok_or_else(|| ClientError::MissingAttributeKey { - attribute_key: CLIENT_TYPE_ATTRIBUTE_KEY.to_string(), - })? + .ok_or(ClientError::MissingAttributeKey)? .client_type; let consensus_height = consensus_height - .ok_or_else(|| ClientError::MissingAttributeKey { - attribute_key: CONSENSUS_HEIGHT_ATTRIBUTE_KEY.to_string(), - })? + .ok_or(ClientError::MissingAttributeKey)? .consensus_height; let consensus_heights = consensus_heights - .ok_or_else(|| ClientError::MissingAttributeKey { - attribute_key: CONSENSUS_HEIGHTS_ATTRIBUTE_KEY.to_string(), - })? + .ok_or(ClientError::MissingAttributeKey)? .consensus_heights; - let header = header - .ok_or_else(|| ClientError::MissingAttributeKey { - attribute_key: HEADER_ATTRIBUTE_KEY.to_string(), - })? - .header; + let header = header.ok_or(ClientError::MissingAttributeKey)?.header; Ok(UpdateClient::new( client_id, @@ -808,9 +740,7 @@ mod tests { abci::EventAttribute::from(("consensus_height", "1-10")), ], }, - Err(ClientError::MissingAttributeKey { - attribute_key: CLIENT_ID_ATTRIBUTE_KEY.to_string(), - }), + Err(ClientError::MissingAttributeKey), )] fn test_create_client_try_from( #[case] event: abci::Event, @@ -872,9 +802,7 @@ mod tests { abci::EventAttribute::from(("header", "1234")), ], }, - Err(ClientError::MissingAttributeKey { - attribute_key: CLIENT_ID_ATTRIBUTE_KEY.to_string(), - }), + Err(ClientError::MissingAttributeKey), )] fn test_update_client_try_from( #[case] event: abci::Event, diff --git a/ibc-core/ics02-client/types/src/height.rs b/ibc-core/ics02-client/types/src/height.rs index 96eda4c92..8785a89d8 100644 --- a/ibc-core/ics02-client/types/src/height.rs +++ b/ibc-core/ics02-client/types/src/height.rs @@ -77,7 +77,7 @@ impl Height { pub fn sub(&self, delta: u64) -> Result { if self.revision_height <= delta { - return Err(ClientError::InvalidHeightResult); + return Err(ClientError::InvalidHeight); } Ok(Height { diff --git a/ibc-core/ics02-client/types/src/msgs/misbehaviour.rs b/ibc-core/ics02-client/types/src/msgs/misbehaviour.rs index 0b4031fe5..7d14f133f 100644 --- a/ibc-core/ics02-client/types/src/msgs/misbehaviour.rs +++ b/ibc-core/ics02-client/types/src/msgs/misbehaviour.rs @@ -48,7 +48,7 @@ impl TryFrom for MsgSubmitMisbehaviour { client_id: raw .client_id .parse() - .map_err(ClientError::InvalidRawMisbehaviour)?, + .map_err(ClientError::InvalidClientIdentifier)?, misbehaviour: raw_misbehaviour, signer: raw.signer.into(), }) diff --git a/ibc-core/ics02-client/types/src/msgs/recover_client.rs b/ibc-core/ics02-client/types/src/msgs/recover_client.rs index 5bc6c0594..62fc738fe 100644 --- a/ibc-core/ics02-client/types/src/msgs/recover_client.rs +++ b/ibc-core/ics02-client/types/src/msgs/recover_client.rs @@ -45,11 +45,11 @@ impl TryFrom for MsgRecoverClient { subject_client_id: raw .subject_client_id .parse() - .map_err(ClientError::InvalidMsgRecoverClientId)?, + .map_err(ClientError::InvalidClientIdentifier)?, substitute_client_id: raw .substitute_client_id .parse() - .map_err(ClientError::InvalidMsgRecoverClientId)?, + .map_err(ClientError::InvalidClientIdentifier)?, signer: raw.signer.into(), }) } diff --git a/ibc-core/ics02-client/types/src/msgs/update_client.rs b/ibc-core/ics02-client/types/src/msgs/update_client.rs index df79446ef..9eb088017 100644 --- a/ibc-core/ics02-client/types/src/msgs/update_client.rs +++ b/ibc-core/ics02-client/types/src/msgs/update_client.rs @@ -37,10 +37,10 @@ impl TryFrom for MsgUpdateClient { client_id: raw .client_id .parse() - .map_err(ClientError::InvalidMsgUpdateClientId)?, + .map_err(ClientError::InvalidClientIdentifier)?, client_message: raw .client_message - .ok_or(ClientError::MissingClientMessage)?, + .ok_or(ClientError::MissingRawClientMessage)?, signer: raw.signer.into(), }) } diff --git a/ibc-core/ics02-client/types/src/status.rs b/ibc-core/ics02-client/types/src/status.rs index d77a7bbe6..5fe9b9e0a 100644 --- a/ibc-core/ics02-client/types/src/status.rs +++ b/ibc-core/ics02-client/types/src/status.rs @@ -50,7 +50,7 @@ impl Status { pub fn verify_is_active(&self) -> Result<(), ClientError> { match self { Self::Active => Ok(()), - &status => Err(ClientError::ClientNotActive { status }), + &status => Err(ClientError::InvalidStatus(status)), } } @@ -58,7 +58,7 @@ impl Status { pub fn verify_is_inactive(&self) -> Result<(), ClientError> { match self { Self::Frozen | Self::Expired => Ok(()), - &status => Err(ClientError::ClientNotInactive { status }), + &status => Err(ClientError::InvalidStatus(status)), } } } diff --git a/ibc-core/ics03-connection/src/delay.rs b/ibc-core/ics03-connection/src/delay.rs index 06fb03098..82c162e4f 100644 --- a/ibc-core/ics03-connection/src/delay.rs +++ b/ibc-core/ics03-connection/src/delay.rs @@ -29,10 +29,10 @@ where // Verify that the current host chain time is later than the last client update time let earliest_valid_time = (last_client_update.0 + conn_delay_time_period) - .map_err(ConnectionError::TimestampOverflow)?; + .map_err(ConnectionError::OverflowedTimestamp)?; if current_host_time < earliest_valid_time { return Err(ContextError::ConnectionError( - ConnectionError::NotEnoughTimeElapsed { + ConnectionError::InsufficientTimeElapsed { current_host_time, earliest_valid_time, }, @@ -43,7 +43,7 @@ where let earliest_valid_height = last_client_update.1.add(conn_delay_height_period); if current_host_height < earliest_valid_height { return Err(ContextError::ConnectionError( - ConnectionError::NotEnoughBlocksElapsed { + ConnectionError::InsufficientBlocksElapsed { current_host_height, earliest_valid_height, }, diff --git a/ibc-core/ics03-connection/src/handler/conn_open_ack.rs b/ibc-core/ics03-connection/src/handler/conn_open_ack.rs index 443d32877..a061b3f06 100644 --- a/ibc-core/ics03-connection/src/handler/conn_open_ack.rs +++ b/ibc-core/ics03-connection/src/handler/conn_open_ack.rs @@ -37,11 +37,10 @@ where { ctx_a.validate_message_signer(&msg.signer)?; - let host_height = ctx_a.host_height().map_err(|_| ConnectionError::Other { - description: "failed to get host height".to_string(), - })?; + let host_height = ctx_a.host_height()?; + if msg.consensus_height_of_a_on_b > host_height { - return Err(ConnectionError::InvalidConsensusHeight { + return Err(ConnectionError::InsufficientConsensusHeight { target_height: msg.consensus_height_of_a_on_b, current_height: host_height, } @@ -104,7 +103,7 @@ where Path::Connection(ConnectionPath::new(&msg.conn_id_on_b)), expected_conn_end_on_b.encode_vec(), ) - .map_err(ConnectionError::VerifyConnectionState)?; + .map_err(ConnectionError::FailedToVerifyConnectionState)?; } client_state_of_b_on_a @@ -115,10 +114,7 @@ where Path::ClientState(ClientStatePath::new(vars.client_id_on_b().clone())), msg.client_state_of_a_on_b.to_vec(), ) - .map_err(|e| ConnectionError::ClientStateVerificationFailure { - client_id: vars.client_id_on_b().clone(), - client_error: e, - })?; + .map_err(ConnectionError::FailedToVerifyClientState)?; let expected_consensus_state_of_a_on_b = ctx_a.host_consensus_state(&msg.consensus_height_of_a_on_b)?; @@ -140,10 +136,7 @@ where Path::ClientConsensusState(client_cons_state_path_on_b), stored_consensus_state_of_a_on_b.to_vec(), ) - .map_err(|e| ConnectionError::ConsensusStateVerificationFailure { - height: msg.proofs_height_on_b, - client_error: e, - })?; + .map_err(ConnectionError::FailedToVerifyConsensusState)?; } Ok(()) diff --git a/ibc-core/ics03-connection/src/handler/conn_open_confirm.rs b/ibc-core/ics03-connection/src/handler/conn_open_confirm.rs index f9ef4eb61..e1751cfc9 100644 --- a/ibc-core/ics03-connection/src/handler/conn_open_confirm.rs +++ b/ibc-core/ics03-connection/src/handler/conn_open_confirm.rs @@ -81,7 +81,7 @@ where Path::Connection(ConnectionPath::new(conn_id_on_a)), expected_conn_end_on_a.encode_vec(), ) - .map_err(ConnectionError::VerifyConnectionState)?; + .map_err(ConnectionError::FailedToVerifyConnectionState)?; } Ok(()) diff --git a/ibc-core/ics03-connection/src/handler/conn_open_try.rs b/ibc-core/ics03-connection/src/handler/conn_open_try.rs index 4b59913e7..34bb18ae0 100644 --- a/ibc-core/ics03-connection/src/handler/conn_open_try.rs +++ b/ibc-core/ics03-connection/src/handler/conn_open_try.rs @@ -47,12 +47,11 @@ where ctx_b.validate_self_client(client_state_of_b_on_a)?; - let host_height = ctx_b.host_height().map_err(|_| ConnectionError::Other { - description: "failed to get host height".to_string(), - })?; + let host_height = ctx_b.host_height()?; + if msg.consensus_height_of_b_on_a > host_height { // Fail if the consensus height is too advanced. - return Err(ConnectionError::InvalidConsensusHeight { + return Err(ConnectionError::InsufficientConsensusHeight { target_height: msg.consensus_height_of_b_on_a, current_height: host_height, } @@ -100,7 +99,7 @@ where Path::Connection(ConnectionPath::new(&vars.conn_id_on_a)), expected_conn_end_on_a.encode_vec(), ) - .map_err(ConnectionError::VerifyConnectionState)?; + .map_err(ConnectionError::FailedToVerifyConnectionState)?; } client_state_of_a_on_b @@ -111,10 +110,7 @@ where Path::ClientState(ClientStatePath::new(client_id_on_a.clone())), msg.client_state_of_b_on_a.to_vec(), ) - .map_err(|e| ConnectionError::ClientStateVerificationFailure { - client_id: msg.client_id_on_b.clone(), - client_error: e, - })?; + .map_err(ConnectionError::FailedToVerifyClientState)?; let expected_consensus_state_of_b_on_a = ctx_b.host_consensus_state(&msg.consensus_height_of_b_on_a)?; @@ -136,10 +132,7 @@ where Path::ClientConsensusState(client_cons_state_path_on_a), stored_consensus_state_of_b_on_a.to_vec(), ) - .map_err(|e| ConnectionError::ConsensusStateVerificationFailure { - height: msg.proofs_height_on_a, - client_error: e, - })?; + .map_err(ConnectionError::FailedToVerifyConsensusState)?; } Ok(()) diff --git a/ibc-core/ics03-connection/src/handler/mod.rs b/ibc-core/ics03-connection/src/handler/mod.rs index 241136411..effc57fda 100644 --- a/ibc-core/ics03-connection/src/handler/mod.rs +++ b/ibc-core/ics03-connection/src/handler/mod.rs @@ -30,14 +30,14 @@ where let wasm_client_state = WasmClientState::try_from(value).map_err(|e| { ContextError::ConnectionError(ConnectionError::InvalidClientState { - reason: e.to_string(), + description: e.to_string(), }) })?; let any_client_state = ::decode(wasm_client_state.data.as_slice()) .map_err(|e| { ContextError::ConnectionError(ConnectionError::InvalidClientState { - reason: e.to_string(), + description: e.to_string(), }) })?; diff --git a/ibc-core/ics03-connection/types/src/connection.rs b/ibc-core/ics03-connection/types/src/connection.rs index dbfa87b01..2602c456d 100644 --- a/ibc-core/ics03-connection/types/src/connection.rs +++ b/ibc-core/ics03-connection/types/src/connection.rs @@ -257,7 +257,7 @@ impl ConnectionEnd { // + Init: contains the set of compatible versions, // + TryOpen/Open: contains the single version chosen by the handshake protocol. if state != State::Init && versions.len() != 1 { - return Err(ConnectionError::InvalidVersionLength); + return Err(ConnectionError::InvalidStateForConnectionEndInit); } Ok(Self { @@ -312,7 +312,7 @@ impl ConnectionEnd { /// Checks if the state of this connection end matches with an expected state. pub fn verify_state_matches(&self, expected: &State) -> Result<(), ConnectionError> { if !self.state.eq(expected) { - return Err(ConnectionError::InvalidState { + return Err(ConnectionError::MismatchedConnectionStates { expected: expected.to_string(), actual: self.state.to_string(), }); @@ -488,8 +488,8 @@ impl State { 1 => Ok(Self::Init), 2 => Ok(Self::TryOpen), 3 => Ok(Self::Open), - _ => Err(ConnectionError::InvalidState { - expected: "Must be one of: 0, 1, 2, 3".to_string(), + _ => Err(ConnectionError::MismatchedConnectionStates { + expected: "0, 1, 2, or 3".to_string(), actual: s.to_string(), }), } @@ -528,8 +528,8 @@ impl TryFrom for State { 1 => Ok(Self::Init), 2 => Ok(Self::TryOpen), 3 => Ok(Self::Open), - _ => Err(ConnectionError::InvalidState { - expected: "Must be one of: 0, 1, 2, 3".to_string(), + _ => Err(ConnectionError::MismatchedConnectionStates { + expected: "0, 1, 2, or 3".to_string(), actual: value.to_string(), }), } diff --git a/ibc-core/ics03-connection/types/src/error.rs b/ibc-core/ics03-connection/types/src/error.rs index e71d5426c..6062dfae6 100644 --- a/ibc-core/ics03-connection/types/src/error.rs +++ b/ibc-core/ics03-connection/types/src/error.rs @@ -1,9 +1,10 @@ //! Defines the connection error type use displaydoc::Display; -use ibc_core_client_types::{error as client_error, Height}; +use ibc_core_client_types::error::ClientError; +use ibc_core_client_types::Height; use ibc_core_host_types::error::IdentifierError; -use ibc_core_host_types::identifiers::{ClientId, ConnectionId}; +use ibc_core_host_types::identifiers::ConnectionId; use ibc_primitives::prelude::*; use ibc_primitives::{Timestamp, TimestampError}; @@ -11,96 +12,84 @@ use crate::version::Version; #[derive(Debug, Display)] pub enum ConnectionError { - /// client error: `{0}` - Client(client_error::ClientError), - /// invalid connection state: expected `{expected}`, actual `{actual}` - InvalidState { expected: String, actual: String }, - /// consensus height claimed by the client on the other party is too advanced: `{target_height}` (host chain current height: `{current_height}`) - InvalidConsensusHeight { - target_height: Height, - current_height: Height, - }, - /// identifier error: `{0}` + /// invalid identifier: `{0}` InvalidIdentifier(IdentifierError), - /// ConnectionEnd domain object could not be constructed out of empty proto object + /// invalid state for initializing new ConnectionEnd; expected `Init` connection state and a single version + InvalidStateForConnectionEndInit, + /// invalid connection proof + InvalidProof, + /// invalid counterparty + InvalidCounterparty, + /// invalid client state: `{description}` + InvalidClientState { description: String }, + /// mismatched connection states: expected `{expected}`, actual `{actual}` + MismatchedConnectionStates { expected: String, actual: String }, + /// empty proto connection end; failed to construct ConnectionEnd domain object EmptyProtoConnectionEnd, /// empty supported versions EmptyVersions, - /// single version must be negotiated on connection before opening channel - InvalidVersionLength, - /// version \"`{version}`\" not supported - VersionNotSupported { version: Version }, - /// no common version - NoCommonVersion, /// empty supported features EmptyFeatures, - /// feature \"`{feature}`\" not supported - FeatureNotSupported { feature: String }, - /// no common features - NoCommonFeatures, + /// unsupported version \"`{0}`\" + UnsupportedVersion(Version), + /// unsupported feature \"`{0}`\" + UnsupportedFeature(String), + /// missing common version + MissingCommonVersion, + /// missing common features + MissingCommonFeatures, /// missing proof height MissingProofHeight, /// missing consensus height MissingConsensusHeight, - /// invalid connection proof error - InvalidProof, - /// verifying connection state error: `{0}` - VerifyConnectionState(client_error::ClientError), - /// invalid signer error: `{reason}` - InvalidSigner { reason: String }, - /// no connection was found for the previous connection id provided `{connection_id}` - ConnectionNotFound { connection_id: ConnectionId }, - /// invalid counterparty - InvalidCounterparty, + /// missing connection `{0}` + MissingConnection(ConnectionId), + /// missing connection counter + MissingConnectionCounter, /// missing counterparty MissingCounterparty, /// missing client state MissingClientState, - /// the consensus proof verification failed (height: `{height}`), client error: `{client_error}` - ConsensusStateVerificationFailure { - height: Height, - client_error: client_error::ClientError, - }, - /// the client state proof verification failed for client id `{client_id}`, client error: `{client_error}` - ClientStateVerificationFailure { - // TODO: use more specific error source - client_id: ClientId, - client_error: client_error::ClientError, + /// insufficient consensus height `{current_height}` for host chain; needs to meet counterparty's height `{target_height}` + InsufficientConsensusHeight { + target_height: Height, + current_height: Height, }, - /// invalid client state: `{reason}` - InvalidClientState { reason: String }, - /// not enough blocks elapsed, current height `{current_host_height}` is still less than the earliest acceptable height `{earliest_valid_height}` - NotEnoughBlocksElapsed { + /// insufficient blocks elapsed: current height `{current_host_height}` needs to meet `{earliest_valid_height}` + InsufficientBlocksElapsed { current_host_height: Height, earliest_valid_height: Height, }, - /// not enough time elapsed, current timestamp `{current_host_time}` is still less than the earliest acceptable timestamp `{earliest_valid_time}` - NotEnoughTimeElapsed { + /// insufficient time elapsed: current timestamp `{current_host_time}` needs to meet `{earliest_valid_time}` + InsufficientTimeElapsed { current_host_time: Timestamp, earliest_valid_time: Timestamp, }, - /// timestamp overflowed error: `{0}` - TimestampOverflow(TimestampError), - /// connection counter overflow error - CounterOverflow, - /// other error: `{description}` - Other { description: String }, + /// failed to verify connection state: `{0}` + FailedToVerifyConnectionState(ClientError), + /// failed to verify consensus state: `{0}` + FailedToVerifyConsensusState(ClientError), + /// failed to verify client state: `{0}` + FailedToVerifyClientState(ClientError), + /// failed to store connection IDs + FailedToStoreConnectionIds, + /// failed to store connection end + FailedToStoreConnectionEnd, + /// failed to update connection counter + FailedToUpdateConnectionCounter, + /// overflowed timestamp: `{0}` + OverflowedTimestamp(TimestampError), } #[cfg(feature = "std")] impl std::error::Error for ConnectionError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match &self { - Self::Client(e) - | Self::VerifyConnectionState(e) - | Self::ConsensusStateVerificationFailure { - client_error: e, .. - } - | Self::ClientStateVerificationFailure { - client_error: e, .. - } => Some(e), - Self::InvalidIdentifier(e) => Some(e), - Self::TimestampOverflow(e) => Some(e), + Self::FailedToVerifyConnectionState(e) + | Self::FailedToVerifyConsensusState(e) + | Self::FailedToVerifyClientState(e) => Some(e), + // Self::InvalidIdentifier(e) => Some(e), + // Self::OverflowedTimestamp(e) => Some(e), _ => None, } } diff --git a/ibc-core/ics03-connection/types/src/version.rs b/ibc-core/ics03-connection/types/src/version.rs index a4446d113..aa6f5316a 100644 --- a/ibc-core/ics03-connection/types/src/version.rs +++ b/ibc-core/ics03-connection/types/src/version.rs @@ -54,7 +54,7 @@ impl Version { /// Checks whether the given feature is supported in this version pub fn verify_feature_supported(&self, feature: String) -> Result<(), ConnectionError> { if !self.features.contains(&feature) { - return Err(ConnectionError::FeatureNotSupported { feature }); + return Err(ConnectionError::UnsupportedFeature(feature)); } Ok(()) } @@ -134,7 +134,7 @@ pub fn pick_version( } if intersection.is_empty() { - return Err(ConnectionError::NoCommonVersion); + return Err(ConnectionError::MissingCommonVersion); } intersection.sort_by(|a, b| a.identifier.cmp(&b.identifier)); @@ -150,9 +150,7 @@ fn find_supported_version( supported_versions .iter() .find(|sv| sv.identifier == version.identifier) - .ok_or(ConnectionError::VersionNotSupported { - version: version.clone(), - }) + .ok_or(ConnectionError::UnsupportedVersion(version.clone())) .cloned() } @@ -171,7 +169,7 @@ fn get_feature_set_intersection( .collect(); if feature_set_intersection.is_empty() { - return Err(ConnectionError::NoCommonFeatures); + return Err(ConnectionError::MissingCommonFeatures); } Ok(feature_set_intersection) @@ -368,7 +366,7 @@ mod tests { name: "Disjoint versions".to_string(), supported: disjoint().0, counterparty: disjoint().1, - picked: Err(ConnectionError::NoCommonVersion), + picked: Err(ConnectionError::MissingCommonVersion), want_pass: false, }, ]; diff --git a/ibc-core/ics04-channel/Cargo.toml b/ibc-core/ics04-channel/Cargo.toml index 1ad973cc1..f9dd35235 100644 --- a/ibc-core/ics04-channel/Cargo.toml +++ b/ibc-core/ics04-channel/Cargo.toml @@ -20,6 +20,7 @@ all-features = true [dependencies] ibc-core-client = { workspace = true } ibc-core-connection = { workspace = true } +ibc-core-connection-types = { workspace = true } ibc-core-channel-types = { workspace = true } ibc-core-commitment-types = { workspace = true } ibc-core-host = { workspace = true } diff --git a/ibc-core/ics04-channel/src/handler/acknowledgement.rs b/ibc-core/ics04-channel/src/handler/acknowledgement.rs index c58edc74a..68bc43128 100644 --- a/ibc-core/ics04-channel/src/handler/acknowledgement.rs +++ b/ibc-core/ics04-channel/src/handler/acknowledgement.rs @@ -139,15 +139,17 @@ where return Ok(()); }; - if commitment_on_a - != compute_packet_commitment( - &packet.data, - &packet.timeout_height_on_b, - &packet.timeout_timestamp_on_b, - ) - { - return Err(PacketError::IncorrectPacketCommitment { + let expected_commitment_on_a = compute_packet_commitment( + &packet.data, + &packet.timeout_height_on_b, + &packet.timeout_timestamp_on_b, + ); + + if commitment_on_a != expected_commitment_on_a { + return Err(PacketError::MismatchedPacketCommitments { sequence: packet.seq_on_a, + actual: commitment_on_a, + expected: expected_commitment_on_a, } .into()); } @@ -156,9 +158,9 @@ where let seq_ack_path_on_a = SeqAckPath::new(&packet.port_id_on_a, &packet.chan_id_on_a); let next_seq_ack = ctx_a.get_next_sequence_ack(&seq_ack_path_on_a)?; if packet.seq_on_a != next_seq_ack { - return Err(PacketError::InvalidPacketSequence { - given_sequence: packet.seq_on_a, - next_sequence: next_seq_ack, + return Err(PacketError::MismatchedPacketSequences { + actual: packet.seq_on_a, + expected: next_seq_ack, } .into()); } @@ -199,11 +201,7 @@ where Path::Ack(ack_path_on_b), ack_commitment.into_vec(), ) - .map_err(|e| ChannelError::PacketVerificationFailed { - sequence: packet.seq_on_a, - client_error: e, - }) - .map_err(PacketError::Channel)?; + .map_err(ChannelError::FailedProofVerification)?; } Ok(()) diff --git a/ibc-core/ics04-channel/src/handler/chan_close_confirm.rs b/ibc-core/ics04-channel/src/handler/chan_close_confirm.rs index 5385d18c3..4f2dc0293 100644 --- a/ibc-core/ics04-channel/src/handler/chan_close_confirm.rs +++ b/ibc-core/ics04-channel/src/handler/chan_close_confirm.rs @@ -6,6 +6,7 @@ use ibc_core_channel_types::events::CloseConfirm; use ibc_core_channel_types::msgs::MsgChannelCloseConfirm; use ibc_core_client::context::prelude::*; use ibc_core_connection::types::State as ConnectionState; +use ibc_core_connection_types::error::ConnectionError; use ibc_core_handler_types::error::ContextError; use ibc_core_handler_types::events::{IbcEvent, MessageEvent}; use ibc_core_host::types::path::{ChannelEndPath, ClientConsensusStatePath, Path}; @@ -57,15 +58,9 @@ where let core_event = { let port_id_on_a = chan_end_on_b.counterparty().port_id.clone(); - let chan_id_on_a = chan_end_on_b - .counterparty() - .channel_id - .clone() - .ok_or(ContextError::ChannelError(ChannelError::Other { - description: - "internal error: ChannelEnd doesn't have a counterparty channel id in CloseInit" - .to_string(), - }))?; + let chan_id_on_a = chan_end_on_b.counterparty().channel_id.clone().ok_or( + ContextError::ChannelError(ChannelError::MissingCounterparty), + )?; let conn_id_on_b = chan_end_on_b.connection_hops[0].clone(); IbcEvent::CloseConfirmChannel(CloseConfirm::new( @@ -134,11 +129,10 @@ where .counterparty() .channel_id() .ok_or(ChannelError::MissingCounterparty)?; - let conn_id_on_a = conn_end_on_b.counterparty().connection_id().ok_or( - ChannelError::UndefinedConnectionCounterparty { - connection_id: chan_end_on_b.connection_hops()[0].clone(), - }, - )?; + let conn_id_on_a = conn_end_on_b + .counterparty() + .connection_id() + .ok_or(ConnectionError::MissingCounterparty)?; let expected_chan_end_on_a = ChannelEnd::new( ChannelState::Closed, @@ -159,7 +153,7 @@ where Path::ChannelEnd(chan_end_path_on_a), expected_chan_end_on_a.encode_vec(), ) - .map_err(ChannelError::VerifyChannelFailed)?; + .map_err(ChannelError::FailedProofVerification)?; } Ok(()) diff --git a/ibc-core/ics04-channel/src/handler/chan_close_init.rs b/ibc-core/ics04-channel/src/handler/chan_close_init.rs index 15c38c9f5..6bdab73f6 100644 --- a/ibc-core/ics04-channel/src/handler/chan_close_init.rs +++ b/ibc-core/ics04-channel/src/handler/chan_close_init.rs @@ -56,15 +56,9 @@ where let core_event = { let port_id_on_b = chan_end_on_a.counterparty().port_id.clone(); - let chan_id_on_b = chan_end_on_a - .counterparty() - .channel_id - .clone() - .ok_or(ContextError::ChannelError(ChannelError::Other { - description: - "internal error: ChannelEnd doesn't have a counterparty channel id in CloseInit" - .to_string(), - }))?; + let chan_id_on_b = chan_end_on_a.counterparty().channel_id.clone().ok_or( + ContextError::ChannelError(ChannelError::MissingCounterparty), + )?; let conn_id_on_a = chan_end_on_a.connection_hops[0].clone(); IbcEvent::CloseInitChannel(CloseInit::new( diff --git a/ibc-core/ics04-channel/src/handler/chan_open_ack.rs b/ibc-core/ics04-channel/src/handler/chan_open_ack.rs index 7e5b6b240..9369733a0 100644 --- a/ibc-core/ics04-channel/src/handler/chan_open_ack.rs +++ b/ibc-core/ics04-channel/src/handler/chan_open_ack.rs @@ -5,6 +5,7 @@ use ibc_core_channel_types::events::OpenAck; use ibc_core_channel_types::msgs::MsgChannelOpenAck; use ibc_core_client::context::prelude::*; use ibc_core_connection::types::State as ConnectionState; +use ibc_core_connection_types::error::ConnectionError; use ibc_core_handler_types::error::ContextError; use ibc_core_handler_types::events::{IbcEvent, MessageEvent}; use ibc_core_host::types::path::{ChannelEndPath, ClientConsensusStatePath, Path}; @@ -125,11 +126,10 @@ where client_val_ctx_a.consensus_state(&client_cons_state_path_on_a)?; let prefix_on_b = conn_end_on_a.counterparty().prefix(); let port_id_on_b = &chan_end_on_a.counterparty().port_id; - let conn_id_on_b = conn_end_on_a.counterparty().connection_id().ok_or( - ChannelError::UndefinedConnectionCounterparty { - connection_id: chan_end_on_a.connection_hops()[0].clone(), - }, - )?; + let conn_id_on_b = conn_end_on_a + .counterparty() + .connection_id() + .ok_or(ConnectionError::MissingCounterparty)?; let expected_chan_end_on_b = ChannelEnd::new( ChannelState::TryOpen, @@ -152,7 +152,7 @@ where Path::ChannelEnd(chan_end_path_on_b), expected_chan_end_on_b.encode_vec(), ) - .map_err(ChannelError::VerifyChannelFailed)?; + .map_err(ChannelError::FailedProofVerification)?; } Ok(()) diff --git a/ibc-core/ics04-channel/src/handler/chan_open_confirm.rs b/ibc-core/ics04-channel/src/handler/chan_open_confirm.rs index dfbc777c1..9d375a295 100644 --- a/ibc-core/ics04-channel/src/handler/chan_open_confirm.rs +++ b/ibc-core/ics04-channel/src/handler/chan_open_confirm.rs @@ -6,6 +6,7 @@ use ibc_core_channel_types::events::OpenConfirm; use ibc_core_channel_types::msgs::MsgChannelOpenConfirm; use ibc_core_client::context::prelude::*; use ibc_core_connection::types::State as ConnectionState; +use ibc_core_connection_types::error::ConnectionError; use ibc_core_handler_types::error::ContextError; use ibc_core_handler_types::events::{IbcEvent, MessageEvent}; use ibc_core_host::types::path::{ChannelEndPath, ClientConsensusStatePath, Path}; @@ -58,15 +59,14 @@ where let conn_id_on_b = chan_end_on_b.connection_hops[0].clone(); let port_id_on_a = chan_end_on_b.counterparty().port_id.clone(); - let chan_id_on_a = chan_end_on_b - .counterparty() - .channel_id - .clone() - .ok_or(ContextError::ChannelError(ChannelError::Other { - description: - "internal error: ChannelEnd doesn't have a counterparty channel id in OpenConfirm" - .to_string(), - }))?; + let chan_id_on_a = + chan_end_on_b + .counterparty() + .channel_id + .clone() + .ok_or(ContextError::ChannelError( + ChannelError::MissingCounterparty, + ))?; let core_event = IbcEvent::OpenConfirmChannel(OpenConfirm::new( msg.port_id_on_b.clone(), @@ -134,11 +134,10 @@ where .counterparty() .channel_id() .ok_or(ChannelError::MissingCounterparty)?; - let conn_id_on_a = conn_end_on_b.counterparty().connection_id().ok_or( - ChannelError::UndefinedConnectionCounterparty { - connection_id: chan_end_on_b.connection_hops()[0].clone(), - }, - )?; + let conn_id_on_a = conn_end_on_b + .counterparty() + .connection_id() + .ok_or(ConnectionError::MissingCounterparty)?; let expected_chan_end_on_a = ChannelEnd::new( ChannelState::Open, @@ -159,7 +158,7 @@ where Path::ChannelEnd(chan_end_path_on_a), expected_chan_end_on_a.encode_vec(), ) - .map_err(ChannelError::VerifyChannelFailed)?; + .map_err(ChannelError::FailedProofVerification)?; } Ok(()) diff --git a/ibc-core/ics04-channel/src/handler/chan_open_try.rs b/ibc-core/ics04-channel/src/handler/chan_open_try.rs index 08c82deac..48138ce69 100644 --- a/ibc-core/ics04-channel/src/handler/chan_open_try.rs +++ b/ibc-core/ics04-channel/src/handler/chan_open_try.rs @@ -6,6 +6,7 @@ use ibc_core_channel_types::events::OpenTry; use ibc_core_channel_types::msgs::MsgChannelOpenTry; use ibc_core_client::context::prelude::*; use ibc_core_connection::types::State as ConnectionState; +use ibc_core_connection_types::error::ConnectionError; use ibc_core_handler_types::error::ContextError; use ibc_core_handler_types::events::{IbcEvent, MessageEvent}; use ibc_core_host::types::identifiers::ChannelId; @@ -153,11 +154,10 @@ where let prefix_on_a = conn_end_on_b.counterparty().prefix(); let port_id_on_a = msg.port_id_on_a.clone(); let chan_id_on_a = msg.chan_id_on_a.clone(); - let conn_id_on_a = conn_end_on_b.counterparty().connection_id().ok_or( - ChannelError::UndefinedConnectionCounterparty { - connection_id: msg.connection_hops_on_b[0].clone(), - }, - )?; + let conn_id_on_a = conn_end_on_b + .counterparty() + .connection_id() + .ok_or(ConnectionError::MissingCounterparty)?; let expected_chan_end_on_a = ChannelEnd::new( ChannelState::Init, @@ -178,7 +178,7 @@ where Path::ChannelEnd(chan_end_path_on_a), expected_chan_end_on_a.encode_vec(), ) - .map_err(ChannelError::VerifyChannelFailed)?; + .map_err(ChannelError::FailedProofVerification)?; } Ok(()) diff --git a/ibc-core/ics04-channel/src/handler/recv_packet.rs b/ibc-core/ics04-channel/src/handler/recv_packet.rs index aa22d3ec9..f3229f493 100644 --- a/ibc-core/ics04-channel/src/handler/recv_packet.rs +++ b/ibc-core/ics04-channel/src/handler/recv_packet.rs @@ -162,7 +162,7 @@ where let latest_height = ctx_b.host_height()?; if msg.packet.timeout_height_on_b.has_expired(latest_height) { - return Err(PacketError::LowPacketHeight { + return Err(PacketError::InsufficientPacketHeight { chain_height: latest_height, timeout_height: msg.packet.timeout_height_on_b, } @@ -175,7 +175,7 @@ where .timeout_timestamp_on_b .has_expired(&latest_timestamp) { - return Err(PacketError::LowPacketTimestamp.into()); + return Err(PacketError::InsufficientPacketTimestamp.into()); } // Verify proofs @@ -221,11 +221,7 @@ where Path::Commitment(commitment_path_on_a), expected_commitment_on_a.into_vec(), ) - .map_err(|e| ChannelError::PacketVerificationFailed { - sequence: msg.packet.seq_on_a, - client_error: e, - }) - .map_err(PacketError::Channel)?; + .map_err(ChannelError::FailedProofVerification)?; } match chan_end_on_b.ordering { @@ -234,9 +230,9 @@ where SeqRecvPath::new(&msg.packet.port_id_on_b, &msg.packet.chan_id_on_b); let next_seq_recv = ctx_b.get_next_sequence_recv(&seq_recv_path_on_b)?; if msg.packet.seq_on_a > next_seq_recv { - return Err(PacketError::InvalidPacketSequence { - given_sequence: msg.packet.seq_on_a, - next_sequence: next_seq_recv, + return Err(PacketError::MismatchedPacketSequences { + actual: msg.packet.seq_on_a, + expected: next_seq_recv, } .into()); } @@ -256,7 +252,7 @@ where let packet_rec = ctx_b.get_packet_receipt(&receipt_path_on_b); match packet_rec { Ok(_receipt) => {} - Err(ContextError::PacketError(PacketError::PacketReceiptNotFound { sequence })) + Err(ContextError::PacketError(PacketError::MissingPacketReceipt(sequence))) if sequence == msg.packet.seq_on_a => {} Err(e) => return Err(e), } @@ -282,10 +278,7 @@ where let packet = msg.packet.clone(); let ack_path_on_b = AckPath::new(&packet.port_id_on_b, &packet.chan_id_on_b, packet.seq_on_a); if ctx_b.get_packet_acknowledgement(&ack_path_on_b).is_ok() { - return Err(PacketError::AcknowledgementExists { - sequence: msg.packet.seq_on_a, - } - .into()); + return Err(PacketError::DuplicateAcknowledgment(msg.packet.seq_on_a).into()); } Ok(()) diff --git a/ibc-core/ics04-channel/src/handler/send_packet.rs b/ibc-core/ics04-channel/src/handler/send_packet.rs index 514ffb89d..6680bf6fb 100644 --- a/ibc-core/ics04-channel/src/handler/send_packet.rs +++ b/ibc-core/ics04-channel/src/handler/send_packet.rs @@ -64,7 +64,7 @@ pub fn send_packet_validate( let latest_height_on_a = client_state_of_b_on_a.latest_height(); if packet.timeout_height_on_b.has_expired(latest_height_on_a) { - return Err(PacketError::LowPacketHeight { + return Err(PacketError::InsufficientPacketHeight { chain_height: latest_height_on_a, timeout_height: packet.timeout_height_on_b, } @@ -81,16 +81,16 @@ pub fn send_packet_validate( let latest_timestamp = consensus_state_of_b_on_a.timestamp(); let packet_timestamp = packet.timeout_timestamp_on_b; if packet_timestamp.has_expired(&latest_timestamp) { - return Err(PacketError::LowPacketTimestamp.into()); + return Err(PacketError::InsufficientPacketTimestamp.into()); } let seq_send_path_on_a = SeqSendPath::new(&packet.port_id_on_a, &packet.chan_id_on_a); let next_seq_send_on_a = ctx_a.get_next_sequence_send(&seq_send_path_on_a)?; if packet.seq_on_a != next_seq_send_on_a { - return Err(PacketError::InvalidPacketSequence { - given_sequence: packet.seq_on_a, - next_sequence: next_seq_send_on_a, + return Err(PacketError::MismatchedPacketSequences { + actual: packet.seq_on_a, + expected: next_seq_send_on_a, } .into()); } diff --git a/ibc-core/ics04-channel/src/handler/timeout.rs b/ibc-core/ics04-channel/src/handler/timeout.rs index ef478ed4e..9ce98bc0a 100644 --- a/ibc-core/ics04-channel/src/handler/timeout.rs +++ b/ibc-core/ics04-channel/src/handler/timeout.rs @@ -168,9 +168,12 @@ where &msg.packet.timeout_height_on_b, &msg.packet.timeout_timestamp_on_b, ); + if commitment_on_a != expected_commitment_on_a { - return Err(PacketError::IncorrectPacketCommitment { + return Err(PacketError::MismatchedPacketCommitments { sequence: msg.packet.seq_on_a, + expected: expected_commitment_on_a, + actual: commitment_on_a, } .into()); } @@ -212,9 +215,9 @@ where let next_seq_recv_verification_result = match chan_end_on_a.ordering { Order::Ordered => { if msg.packet.seq_on_a < msg.next_seq_recv_on_b { - return Err(PacketError::InvalidPacketSequence { - given_sequence: msg.packet.seq_on_a, - next_sequence: msg.next_seq_recv_on_b, + return Err(PacketError::MismatchedPacketSequences { + actual: msg.packet.seq_on_a, + expected: msg.next_seq_recv_on_b, } .into()); } @@ -251,12 +254,10 @@ where } }; - next_seq_recv_verification_result - .map_err(|e| ChannelError::PacketVerificationFailed { - sequence: msg.next_seq_recv_on_b, - client_error: e, - }) - .map_err(PacketError::Channel)?; + next_seq_recv_verification_result.map_err(|e| ChannelError::FailedPacketVerification { + sequence: msg.next_seq_recv_on_b, + client_error: e, + })?; } Ok(()) diff --git a/ibc-core/ics04-channel/src/handler/timeout_on_close.rs b/ibc-core/ics04-channel/src/handler/timeout_on_close.rs index 8366a5d26..c52225f81 100644 --- a/ibc-core/ics04-channel/src/handler/timeout_on_close.rs +++ b/ibc-core/ics04-channel/src/handler/timeout_on_close.rs @@ -4,6 +4,7 @@ use ibc_core_channel_types::error::{ChannelError, PacketError}; use ibc_core_channel_types::msgs::MsgTimeoutOnClose; use ibc_core_client::context::prelude::*; use ibc_core_connection::delay::verify_conn_delay_passed; +use ibc_core_connection::types::error::ConnectionError; use ibc_core_handler_types::error::ContextError; use ibc_core_host::types::path::{ ChannelEndPath, ClientConsensusStatePath, CommitmentPath, Path, ReceiptPath, SeqRecvPath, @@ -50,8 +51,10 @@ where &packet.timeout_timestamp_on_b, ); if commitment_on_a != expected_commitment_on_a { - return Err(PacketError::IncorrectPacketCommitment { + return Err(PacketError::MismatchedPacketCommitments { sequence: packet.seq_on_a, + expected: expected_commitment_on_a, + actual: commitment_on_a, } .into()); } @@ -84,11 +87,10 @@ where .counterparty() .channel_id() .ok_or(PacketError::Channel(ChannelError::MissingCounterparty))?; - let conn_id_on_b = conn_end_on_a.counterparty().connection_id().ok_or( - PacketError::UndefinedConnectionCounterparty { - connection_id: chan_end_on_a.connection_hops()[0].clone(), - }, - )?; + let conn_id_on_b = conn_end_on_a + .counterparty() + .connection_id() + .ok_or(ConnectionError::MissingCounterparty)?; let expected_conn_hops_on_b = vec![conn_id_on_b.clone()]; let expected_counterparty = Counterparty::new( packet.port_id_on_a.clone(), @@ -114,17 +116,16 @@ where Path::ChannelEnd(chan_end_path_on_b), expected_chan_end_on_b.encode_vec(), ) - .map_err(ChannelError::VerifyChannelFailed) - .map_err(PacketError::Channel)?; + .map_err(ChannelError::FailedProofVerification)?; verify_conn_delay_passed(ctx_a, msg.proof_height_on_b, &conn_end_on_a)?; let next_seq_recv_verification_result = match chan_end_on_a.ordering { Order::Ordered => { if packet.seq_on_a < msg.next_seq_recv_on_b { - return Err(PacketError::InvalidPacketSequence { - given_sequence: packet.seq_on_a, - next_sequence: msg.next_seq_recv_on_b, + return Err(PacketError::MismatchedPacketSequences { + actual: packet.seq_on_a, + expected: msg.next_seq_recv_on_b, } .into()); } @@ -161,12 +162,10 @@ where } }; - next_seq_recv_verification_result - .map_err(|e| ChannelError::PacketVerificationFailed { - sequence: msg.next_seq_recv_on_b, - client_error: e, - }) - .map_err(PacketError::Channel)?; + next_seq_recv_verification_result.map_err(|e| ChannelError::FailedPacketVerification { + sequence: msg.next_seq_recv_on_b, + client_error: e, + })?; }; Ok(()) diff --git a/ibc-core/ics04-channel/types/src/acknowledgement.rs b/ibc-core/ics04-channel/types/src/acknowledgement.rs index 7364857d1..0e983b0ce 100644 --- a/ibc-core/ics04-channel/types/src/acknowledgement.rs +++ b/ibc-core/ics04-channel/types/src/acknowledgement.rs @@ -45,7 +45,7 @@ impl TryFrom> for Acknowledgement { fn try_from(bytes: Vec) -> Result { if bytes.is_empty() { - Err(PacketError::InvalidAcknowledgement) + Err(PacketError::EmptyAcknowledgment) } else { Ok(Self(bytes)) } @@ -81,7 +81,7 @@ impl StatusValue { let value = value.to_string(); if value.is_empty() { - return Err(PacketError::EmptyAcknowledgementStatus); + return Err(PacketError::EmptyAcknowledgmentStatus); } Ok(Self(value)) diff --git a/ibc-core/ics04-channel/types/src/error.rs b/ibc-core/ics04-channel/types/src/error.rs index 7042411d3..56cdadcd5 100644 --- a/ibc-core/ics04-channel/types/src/error.rs +++ b/ibc-core/ics04-channel/types/src/error.rs @@ -3,173 +3,132 @@ use displaydoc::Display; use ibc_core_client_types::error::ClientError; use ibc_core_client_types::Height; -use ibc_core_connection_types::error as connection_error; use ibc_core_host_types::error::IdentifierError; -use ibc_core_host_types::identifiers::{ChannelId, ConnectionId, PortId, Sequence}; +use ibc_core_host_types::identifiers::{ChannelId, PortId, Sequence}; use ibc_primitives::prelude::*; use ibc_primitives::{Timestamp, TimestampError}; use super::channel::Counterparty; use super::timeout::TimeoutHeight; -use crate::channel::State; +use crate::commitment::PacketCommitment; use crate::timeout::TimeoutTimestamp; use crate::Version; #[derive(Debug, Display)] pub enum ChannelError { - /// invalid channel end: `{channel_end}` - InvalidChannelEnd { channel_end: String }, + /// application module error: `{description}` + AppModule { description: String }, + /// identifier error: `{0}` + InvalidIdentifier(IdentifierError), /// invalid channel id: expected `{expected}`, actual `{actual}` InvalidChannelId { expected: String, actual: String }, /// invalid channel state: expected `{expected}`, actual `{actual}` InvalidState { expected: String, actual: String }, /// invalid channel order type: expected `{expected}`, actual `{actual}` InvalidOrderType { expected: String, actual: String }, - /// invalid connection hops length: expected `{expected}`; actual `{actual}` + /// invalid connection hops length: expected `{expected}`, actual `{actual}` InvalidConnectionHopsLength { expected: u64, actual: u64 }, - /// invalid signer error: `{reason}` - InvalidSigner { reason: String }, - /// invalid proof: missing height - MissingHeight, - /// packet data bytes must be valid UTF-8 (this restriction will be lifted in the future) - NonUtf8PacketData, + /// invalid counterparty: expected `{expected}`, actual `{actual}` + InvalidCounterparty { + expected: Counterparty, + actual: Counterparty, + }, + /// missing proof + MissingProof, + /// missing proof height + MissingProofHeight, /// missing counterparty MissingCounterparty, + /// missing channel end in raw message + MissingRawChannelEnd, + /// missing channel counter + MissingCounter, /// unsupported channel upgrade sequence UnsupportedChannelUpgradeSequence, - /// version not supported: expected `{expected}`, actual `{actual}` - VersionNotSupported { expected: Version, actual: Version }, - /// missing channel end - MissingChannel, - /// the channel end (`{port_id}`, `{channel_id}`) does not exist - ChannelNotFound { + /// unsupported version: expected `{expected}`, actual `{actual}` + UnsupportedVersion { expected: Version, actual: Version }, + /// non-existent channel end: (`{port_id}`, `{channel_id}`) + NonexistentChannel { port_id: PortId, channel_id: ChannelId, }, - /// Verification fails for the packet with the sequence number `{sequence}`, error: `{client_error}` - PacketVerificationFailed { + /// packet data bytes must be valid UTF-8 + NonUtf8PacketData, + /// failed packet verification for packet with sequence `{sequence}`: `{client_error}` + FailedPacketVerification { sequence: Sequence, client_error: ClientError, }, - /// Error verifying channel state error: `{0}` - VerifyChannelFailed(ClientError), - /// String `{value}` cannot be converted to packet sequence, error: `{error}` - InvalidStringAsSequence { - value: String, - error: core::num::ParseIntError, - }, - /// invalid channel counterparty: expected `{expected}`, actual `{actual}` - InvalidCounterparty { - expected: Counterparty, - actual: Counterparty, - }, - /// application module error: `{description}` - AppModule { description: String }, - /// Undefined counterparty connection for `{connection_id}` - UndefinedConnectionCounterparty { connection_id: ConnectionId }, - /// invalid proof: empty proof - InvalidProof, - /// identifier error: `{0}` - InvalidIdentifier(IdentifierError), - /// channel counter overflow error - CounterOverflow, - /// other error: `{description}` - Other { description: String }, + /// failed proof verification: `{0}` + FailedProofVerification(ClientError), + + // TODO(seanchen1991): These two variants should be encoded by host-relevant error types + // once those have been defined. + /// failed to update counter: `{description}` + FailedToUpdateCounter { description: String }, + /// failed to store channel: `{description}` + FailedToStoreChannel { description: String }, } #[derive(Debug, Display)] pub enum PacketError { - /// connection error: `{0}` - Connection(connection_error::ConnectionError), + /// application module error: `{description}` + AppModule { description: String }, /// channel error: `{0}` Channel(ChannelError), - /// Receiving chain block height `{chain_height}` >= packet timeout height `{timeout_height}` - LowPacketHeight { + /// insufficient packet timeout height: should have `{timeout_height}` > `{chain_height}` + InsufficientPacketHeight { chain_height: Height, timeout_height: TimeoutHeight, }, - /// Receiving chain block timestamp >= packet timeout timestamp - LowPacketTimestamp, - /// Invalid packet sequence `{given_sequence}` ≠ next send sequence `{next_sequence}` - InvalidPacketSequence { - given_sequence: Sequence, - next_sequence: Sequence, + /// insufficient packet timestamp: should be greater than chain block timestamp + InsufficientPacketTimestamp, + /// mismatched packet sequences: expected `{expected}`, actual `{actual}` + MismatchedPacketSequences { + expected: Sequence, + actual: Sequence, }, - /// Channel `{channel_id}` should not be state `{state}` - InvalidChannelState { channel_id: ChannelId, state: State }, - /// the associated connection `{connection_id}` is not OPEN - ConnectionNotOpen { connection_id: ConnectionId }, - /// Receipt for the packet `{sequence}` not found - PacketReceiptNotFound { sequence: Sequence }, - /// The stored commitment of the packet `{sequence}` is incorrect - IncorrectPacketCommitment { sequence: Sequence }, - /// implementation-specific error - ImplementationSpecific, - /// Undefined counterparty connection for `{connection_id}` - UndefinedConnectionCounterparty { connection_id: ConnectionId }, - /// invalid proof: empty proof - InvalidProof, - /// Packet timeout height `{timeout_height}` > chain height `{chain_height} and timeout timestamp `{timeout_timestamp}` > chain timestamp `{chain_timestamp}` + /// mismatched commitments for packet `{sequence}`: expected `{expected:?}`, actual `{actual:?}` + MismatchedPacketCommitments { + sequence: Sequence, + expected: PacketCommitment, + actual: PacketCommitment, + }, + /// missing packet receipt for packet `{0}` + MissingPacketReceipt(Sequence), + /// missing proof + MissingProof, + /// missing acknowledgment for packet `{0}` + MissingPacketAcknowledgment(Sequence), + /// missing proof height + MissingProofHeight, + /// missing timeout + MissingTimeout, + /// invalid timeout height: `{0}` + InvalidTimeoutHeight(ClientError), + /// invalid timeout timestamp: `{0}` + InvalidTimeoutTimestamp(TimestampError), + /// invalid identifier: `{0}` + InvalidIdentifier(IdentifierError), + /// empty acknowledgment not allowed + EmptyAcknowledgment, + /// empty acknowledgment status not allowed + EmptyAcknowledgmentStatus, + /// packet data bytes cannot be empty + EmptyPacketData, + /// packet acknowledgment for sequence `{0}` already exists + DuplicateAcknowledgment(Sequence), + /// packet sequence cannot be 0 + ZeroPacketSequence, + /// packet timeout height `{timeout_height}` > chain height `{chain_height} and timeout timestamp `{timeout_timestamp}` > chain timestamp `{chain_timestamp}` PacketTimeoutNotReached { timeout_height: TimeoutHeight, chain_height: Height, timeout_timestamp: TimeoutTimestamp, chain_timestamp: Timestamp, }, - /// Packet acknowledgement exists for the packet with the sequence `{sequence}` - AcknowledgementExists { sequence: Sequence }, - /// Acknowledgment cannot be empty - InvalidAcknowledgement, - /// Acknowledgment status cannot be empty - EmptyAcknowledgementStatus, - /// Acknowledgment for the packet `{sequence}` not found - PacketAcknowledgementNotFound { sequence: Sequence }, - /// invalid proof: missing height - MissingHeight, - /// there is no packet in this message - MissingPacket, - /// invalid signer error: `{reason}` - InvalidSigner { reason: String }, - /// application module error: `{description}` - AppModule { description: String }, - /// route not found - RouteNotFound, - /// packet sequence cannot be 0 - ZeroPacketSequence, - /// packet data bytes cannot be empty - ZeroPacketData, - /// invalid timeout height with error: `{0}` - InvalidTimeoutHeight(ClientError), - /// Invalid timeout timestamp with error: `{0}` - InvalidTimeoutTimestamp(TimestampError), - /// missing timeout - MissingTimeout, - /// invalid identifier error: `{0}` - InvalidIdentifier(IdentifierError), - /// Missing sequence number for sending packets on port `{port_id}` and channel `{channel_id}` - MissingNextSendSeq { - port_id: PortId, - channel_id: ChannelId, - }, - /// the channel end (`{port_id}`, `{channel_id}`) does not exist - ChannelNotFound { - port_id: PortId, - channel_id: ChannelId, - }, - /// Commitment for the packet `{sequence}` not found - PacketCommitmentNotFound { sequence: Sequence }, - /// Missing sequence number for receiving packets on port `{port_id}` and channel `{channel_id}` - MissingNextRecvSeq { - port_id: PortId, - channel_id: ChannelId, - }, - /// Missing sequence number for ack packets on port `{port_id}` and channel `{channel_id}` - MissingNextAckSeq { - port_id: PortId, - channel_id: ChannelId, - }, - /// other error: `{description}` - Other { description: String }, + /// implementation-specific error + ImplementationSpecific, } impl From for ChannelError { @@ -194,7 +153,6 @@ impl From for PacketError { impl std::error::Error for PacketError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match &self { - Self::Connection(e) => Some(e), Self::Channel(e) => Some(e), Self::InvalidIdentifier(e) => Some(e), _ => None, @@ -207,10 +165,9 @@ impl std::error::Error for ChannelError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match &self { Self::InvalidIdentifier(e) => Some(e), - Self::PacketVerificationFailed { + Self::FailedPacketVerification { client_error: e, .. } => Some(e), - Self::InvalidStringAsSequence { error: e, .. } => Some(e), _ => None, } } diff --git a/ibc-core/ics04-channel/types/src/msgs/acknowledgement.rs b/ibc-core/ics04-channel/types/src/msgs/acknowledgement.rs index a93f7a622..2446395d7 100644 --- a/ibc-core/ics04-channel/types/src/msgs/acknowledgement.rs +++ b/ibc-core/ics04-channel/types/src/msgs/acknowledgement.rs @@ -39,17 +39,17 @@ impl TryFrom for MsgAcknowledgement { Ok(MsgAcknowledgement { packet: raw_msg .packet - .ok_or(PacketError::MissingPacket)? + .ok_or(PacketError::EmptyPacketData)? .try_into()?, acknowledgement: raw_msg.acknowledgement.try_into()?, proof_acked_on_b: raw_msg .proof_acked .try_into() - .map_err(|_| PacketError::InvalidProof)?, + .map_err(|_| PacketError::MissingProof)?, proof_height_on_b: raw_msg .proof_height .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or(PacketError::MissingHeight)?, + .ok_or(PacketError::MissingProofHeight)?, signer: raw_msg.signer.into(), }) } diff --git a/ibc-core/ics04-channel/types/src/msgs/chan_close_confirm.rs b/ibc-core/ics04-channel/types/src/msgs/chan_close_confirm.rs index c533fc59c..5074f552a 100644 --- a/ibc-core/ics04-channel/types/src/msgs/chan_close_confirm.rs +++ b/ibc-core/ics04-channel/types/src/msgs/chan_close_confirm.rs @@ -45,11 +45,11 @@ impl TryFrom for MsgChannelCloseConfirm { proof_chan_end_on_a: raw_msg .proof_init .try_into() - .map_err(|_| ChannelError::InvalidProof)?, + .map_err(|_| ChannelError::MissingProof)?, proof_height_on_a: raw_msg .proof_height .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or(ChannelError::MissingHeight)?, + .ok_or(ChannelError::MissingProofHeight)?, signer: raw_msg.signer.into(), }) } diff --git a/ibc-core/ics04-channel/types/src/msgs/chan_open_ack.rs b/ibc-core/ics04-channel/types/src/msgs/chan_open_ack.rs index 432ca92fe..8634eb3e8 100644 --- a/ibc-core/ics04-channel/types/src/msgs/chan_open_ack.rs +++ b/ibc-core/ics04-channel/types/src/msgs/chan_open_ack.rs @@ -44,11 +44,11 @@ impl TryFrom for MsgChannelOpenAck { proof_chan_end_on_b: raw_msg .proof_try .try_into() - .map_err(|_| ChannelError::InvalidProof)?, + .map_err(|_| ChannelError::MissingProof)?, proof_height_on_b: raw_msg .proof_height .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or(ChannelError::MissingHeight)?, + .ok_or(ChannelError::MissingProofHeight)?, signer: raw_msg.signer.into(), }) } diff --git a/ibc-core/ics04-channel/types/src/msgs/chan_open_confirm.rs b/ibc-core/ics04-channel/types/src/msgs/chan_open_confirm.rs index 6070eb1a2..9b6de510a 100644 --- a/ibc-core/ics04-channel/types/src/msgs/chan_open_confirm.rs +++ b/ibc-core/ics04-channel/types/src/msgs/chan_open_confirm.rs @@ -41,11 +41,11 @@ impl TryFrom for MsgChannelOpenConfirm { proof_chan_end_on_a: raw_msg .proof_ack .try_into() - .map_err(|_| ChannelError::InvalidProof)?, + .map_err(|_| ChannelError::MissingProof)?, proof_height_on_a: raw_msg .proof_height .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or(ChannelError::MissingHeight)?, + .ok_or(ChannelError::MissingProofHeight)?, signer: raw_msg.signer.into(), }) } diff --git a/ibc-core/ics04-channel/types/src/msgs/chan_open_init.rs b/ibc-core/ics04-channel/types/src/msgs/chan_open_init.rs index 7ab3f1598..afac87752 100644 --- a/ibc-core/ics04-channel/types/src/msgs/chan_open_init.rs +++ b/ibc-core/ics04-channel/types/src/msgs/chan_open_init.rs @@ -47,7 +47,7 @@ impl TryFrom for MsgChannelOpenInit { fn try_from(raw_msg: RawMsgChannelOpenInit) -> Result { let chan_end_on_a: ChannelEnd = raw_msg .channel - .ok_or(ChannelError::MissingChannel)? + .ok_or(ChannelError::MissingRawChannelEnd)? .try_into()?; chan_end_on_a.verify_state_matches(&State::Init)?; chan_end_on_a.counterparty().verify_empty_channel_id()?; diff --git a/ibc-core/ics04-channel/types/src/msgs/chan_open_try.rs b/ibc-core/ics04-channel/types/src/msgs/chan_open_try.rs index c6cbd918b..8d85d1fa3 100644 --- a/ibc-core/ics04-channel/types/src/msgs/chan_open_try.rs +++ b/ibc-core/ics04-channel/types/src/msgs/chan_open_try.rs @@ -55,7 +55,7 @@ impl TryFrom for MsgChannelOpenTry { fn try_from(raw_msg: RawMsgChannelOpenTry) -> Result { let chan_end_on_b: ChannelEnd = raw_msg .channel - .ok_or(ChannelError::MissingChannel)? + .ok_or(ChannelError::MissingRawChannelEnd)? .try_into()?; chan_end_on_b.verify_state_matches(&State::TryOpen)?; @@ -82,11 +82,11 @@ impl TryFrom for MsgChannelOpenTry { proof_chan_end_on_a: raw_msg .proof_init .try_into() - .map_err(|_| ChannelError::InvalidProof)?, + .map_err(|_| ChannelError::MissingProof)?, proof_height_on_a: raw_msg .proof_height .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or(ChannelError::MissingHeight)?, + .ok_or(ChannelError::MissingProofHeight)?, signer: raw_msg.signer.into(), version_proposal: chan_end_on_b.version, }; diff --git a/ibc-core/ics04-channel/types/src/msgs/recv_packet.rs b/ibc-core/ics04-channel/types/src/msgs/recv_packet.rs index 5295e1807..53e4a6716 100644 --- a/ibc-core/ics04-channel/types/src/msgs/recv_packet.rs +++ b/ibc-core/ics04-channel/types/src/msgs/recv_packet.rs @@ -39,16 +39,16 @@ impl TryFrom for MsgRecvPacket { Ok(MsgRecvPacket { packet: raw_msg .packet - .ok_or(PacketError::MissingPacket)? + .ok_or(PacketError::EmptyPacketData)? .try_into()?, proof_commitment_on_a: raw_msg .proof_commitment .try_into() - .map_err(|_| PacketError::InvalidProof)?, + .map_err(|_| PacketError::MissingProof)?, proof_height_on_a: raw_msg .proof_height .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or(PacketError::MissingHeight)?, + .ok_or(PacketError::MissingProofHeight)?, signer: raw_msg.signer.into(), }) } diff --git a/ibc-core/ics04-channel/types/src/msgs/timeout.rs b/ibc-core/ics04-channel/types/src/msgs/timeout.rs index 362630ff0..657e8233e 100644 --- a/ibc-core/ics04-channel/types/src/msgs/timeout.rs +++ b/ibc-core/ics04-channel/types/src/msgs/timeout.rs @@ -41,17 +41,17 @@ impl TryFrom for MsgTimeout { Ok(MsgTimeout { packet: raw_msg .packet - .ok_or(PacketError::MissingPacket)? + .ok_or(PacketError::EmptyPacketData)? .try_into()?, next_seq_recv_on_b: Sequence::from(raw_msg.next_sequence_recv), proof_unreceived_on_b: raw_msg .proof_unreceived .try_into() - .map_err(|_| PacketError::InvalidProof)?, + .map_err(|_| PacketError::MissingProof)?, proof_height_on_b: raw_msg .proof_height .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or(PacketError::MissingHeight)?, + .ok_or(PacketError::MissingProofHeight)?, signer: raw_msg.signer.into(), }) } diff --git a/ibc-core/ics04-channel/types/src/msgs/timeout_on_close.rs b/ibc-core/ics04-channel/types/src/msgs/timeout_on_close.rs index 7673d5f33..670205716 100644 --- a/ibc-core/ics04-channel/types/src/msgs/timeout_on_close.rs +++ b/ibc-core/ics04-channel/types/src/msgs/timeout_on_close.rs @@ -48,21 +48,21 @@ impl TryFrom for MsgTimeoutOnClose { Ok(MsgTimeoutOnClose { packet: raw_msg .packet - .ok_or(PacketError::MissingPacket)? + .ok_or(PacketError::EmptyPacketData)? .try_into()?, next_seq_recv_on_b: Sequence::from(raw_msg.next_sequence_recv), proof_unreceived_on_b: raw_msg .proof_unreceived .try_into() - .map_err(|_| PacketError::InvalidProof)?, + .map_err(|_| PacketError::MissingProof)?, proof_close_on_b: raw_msg .proof_close .try_into() - .map_err(|_| PacketError::InvalidProof)?, + .map_err(|_| PacketError::MissingProof)?, proof_height_on_b: raw_msg .proof_height .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or(PacketError::MissingHeight)?, + .ok_or(PacketError::MissingProofHeight)?, signer: raw_msg.signer.into(), }) } diff --git a/ibc-core/ics04-channel/types/src/packet.rs b/ibc-core/ics04-channel/types/src/packet.rs index d07746b77..dfd6f0358 100644 --- a/ibc-core/ics04-channel/types/src/packet.rs +++ b/ibc-core/ics04-channel/types/src/packet.rs @@ -172,7 +172,7 @@ impl TryFrom for Packet { } if raw_pkt.data.is_empty() { - return Err(PacketError::ZeroPacketData); + return Err(PacketError::EmptyPacketData); } // Note: ibc-go currently (July 2022) incorrectly treats the timeout @@ -282,7 +282,7 @@ impl TryFrom for PacketState { } if raw_pkt.data.is_empty() { - return Err(PacketError::ZeroPacketData); + return Err(PacketError::EmptyPacketData); } Ok(PacketState { diff --git a/ibc-core/ics04-channel/types/src/version.rs b/ibc-core/ics04-channel/types/src/version.rs index 2cfc1e46c..1cf5c894a 100644 --- a/ibc-core/ics04-channel/types/src/version.rs +++ b/ibc-core/ics04-channel/types/src/version.rs @@ -51,7 +51,7 @@ impl Version { pub fn verify_is_expected(&self, expected: Version) -> Result<(), ChannelError> { if self != &expected { - return Err(ChannelError::VersionNotSupported { + return Err(ChannelError::UnsupportedVersion { expected, actual: self.clone(), }); diff --git a/ibc-core/ics23-commitment/types/src/commitment.rs b/ibc-core/ics23-commitment/types/src/commitment.rs index 2555f89ac..43506a798 100644 --- a/ibc-core/ics23-commitment/types/src/commitment.rs +++ b/ibc-core/ics23-commitment/types/src/commitment.rs @@ -122,7 +122,7 @@ impl<'a> TryFrom<&'a CommitmentProofBytes> for MerkleProof { fn try_from(value: &'a CommitmentProofBytes) -> Result { Protobuf::::decode(value.as_ref()) - .map_err(|e| CommitmentError::DecodingFailure(e.to_string())) + .map_err(|e| CommitmentError::FailedDecoding(e.to_string())) } } diff --git a/ibc-core/ics23-commitment/types/src/error.rs b/ibc-core/ics23-commitment/types/src/error.rs index fdbdd24c7..81ee26e06 100644 --- a/ibc-core/ics23-commitment/types/src/error.rs +++ b/ibc-core/ics23-commitment/types/src/error.rs @@ -7,6 +7,8 @@ use ibc_primitives::prelude::*; pub enum CommitmentError { /// empty commitment prefix EmptyCommitmentPrefix, + /// empty commitment root + EmptyCommitmentRoot, /// empty merkle proof EmptyMerkleProof, /// empty merkle root @@ -15,28 +17,24 @@ pub enum CommitmentError { EmptyVerifiedValue, /// empty proof specs EmptyProofSpecs, - /// invalid depth range: [{0}, {1}] - InvalidDepthRange(i32, i32), - /// mismatch between the number of proofs with that of specs - NumberOfSpecsMismatch, - /// mismatch between the number of proofs with that of keys - NumberOfKeysMismatch, + /// mismatched number of proofs: expected `{expected}`, actual `{actual}` + MismatchedNumberOfProofs { expected: usize, actual: usize }, + /// mismatched proofs: expected `{expected}`, actual `{actual}` + MismatchedProofs { expected: String, actual: String }, + /// invalid range: [`{min}`, `{max}`] + InvalidRange { min: i32, max: i32 }, /// invalid merkle proof InvalidMerkleProof, - /// proof verification failed - VerificationFailure, - /// encoded commitment prefix is not a valid hex string: `{0}` - EncodingFailure(String), - /// decoding commitment proof bytes failed: `{0}` - DecodingFailure(String), - /// invalid prefix length range: `[{0}, {1}]` - InvalidPrefixLengthRange(i32, i32), /// invalid child size: `{0}` InvalidChildSize(i32), /// invalid hash operation: `{0}` InvalidHashOp(i32), /// invalid length operation: `{0}` InvalidLengthOp(i32), + /// failed decoding commitment proof: `{0}` + FailedDecoding(String), + /// failed to verify membership + FailedToVerifyMembership, } #[cfg(feature = "std")] diff --git a/ibc-core/ics23-commitment/types/src/merkle.rs b/ibc-core/ics23-commitment/types/src/merkle.rs index d597ef1c8..69aad45e5 100644 --- a/ibc-core/ics23-commitment/types/src/merkle.rs +++ b/ibc-core/ics23-commitment/types/src/merkle.rs @@ -106,10 +106,16 @@ impl MerkleProof { let num = self.proofs.len(); let ics23_specs = Vec::::from(specs.clone()); if ics23_specs.len() != num { - return Err(CommitmentError::NumberOfSpecsMismatch); + return Err(CommitmentError::MismatchedNumberOfProofs { + expected: ics23_specs.len(), + actual: num, + }); } if keys.key_path.len() != num { - return Err(CommitmentError::NumberOfKeysMismatch); + return Err(CommitmentError::MismatchedNumberOfProofs { + expected: keys.key_path.len(), + actual: num, + }); } if value.is_empty() { return Err(CommitmentError::EmptyVerifiedValue); @@ -135,7 +141,7 @@ impl MerkleProof { .map_err(|_| CommitmentError::InvalidMerkleProof)?; if !verify_membership::(proof, spec, &subroot, key.as_ref(), &value) { - return Err(CommitmentError::VerificationFailure); + return Err(CommitmentError::FailedToVerifyMembership); } value.clone_from(&subroot); } @@ -144,7 +150,7 @@ impl MerkleProof { } if root.hash != subroot { - return Err(CommitmentError::VerificationFailure); + return Err(CommitmentError::FailedToVerifyMembership); } Ok(()) @@ -166,10 +172,16 @@ impl MerkleProof { let num = self.proofs.len(); let ics23_specs = Vec::::from(specs.clone()); if ics23_specs.len() != num { - return Err(CommitmentError::NumberOfSpecsMismatch); + return Err(CommitmentError::MismatchedNumberOfProofs { + actual: num, + expected: ics23_specs.len(), + }); } if keys.key_path.len() != num { - return Err(CommitmentError::NumberOfKeysMismatch); + return Err(CommitmentError::MismatchedNumberOfProofs { + actual: num, + expected: keys.key_path.len(), + }); } // verify the absence of key in lowest subtree @@ -190,7 +202,7 @@ impl MerkleProof { let subroot = calculate_non_existence_root::(non_existence_proof)?; if !verify_non_membership::(proof, spec, &subroot, key.as_ref()) { - return Err(CommitmentError::VerificationFailure); + return Err(CommitmentError::FailedToVerifyMembership); } // verify membership proofs starting from index 1 with value = subroot diff --git a/ibc-core/ics23-commitment/types/src/specs.rs b/ibc-core/ics23-commitment/types/src/specs.rs index 46bf79a95..a37abf6cc 100644 --- a/ibc-core/ics23-commitment/types/src/specs.rs +++ b/ibc-core/ics23-commitment/types/src/specs.rs @@ -43,10 +43,10 @@ impl ProofSpecs { && 0 < proof_spec.0.max_depth && proof_spec.0.max_depth < proof_spec.0.min_depth) { - return Err(CommitmentError::InvalidDepthRange( - proof_spec.0.min_depth, - proof_spec.0.max_depth, - )); + return Err(CommitmentError::InvalidRange { + min: proof_spec.0.min_depth, + max: proof_spec.0.max_depth, + }); } } Ok(()) @@ -90,10 +90,10 @@ impl TryFrom for ProofSpec { || spec.min_depth < 0 || (0 < spec.min_depth && 0 < spec.max_depth && spec.max_depth < spec.min_depth) { - return Err(CommitmentError::InvalidDepthRange( - spec.min_depth, - spec.max_depth, - )); + return Err(CommitmentError::InvalidRange { + min: spec.min_depth, + max: spec.max_depth, + }); } let leaf_spec = spec @@ -166,10 +166,10 @@ impl TryFrom for InnerSpec { || inner_spec.max_prefix_length < 0 || inner_spec.max_prefix_length < inner_spec.min_prefix_length { - return Err(CommitmentError::InvalidPrefixLengthRange( - inner_spec.min_prefix_length, - inner_spec.max_prefix_length, - )); + return Err(CommitmentError::InvalidRange { + min: inner_spec.min_prefix_length, + max: inner_spec.max_prefix_length, + }); } Ok(Self(RawInnerSpec { @@ -200,15 +200,15 @@ mod tests { #[case(0, 0)] #[case(2, 2)] #[case(5, 6)] - #[should_panic(expected = "InvalidDepthRange")] + #[should_panic(expected = "InvalidRange")] #[case(-3,3)] - #[should_panic(expected = "InvalidDepthRange")] + #[should_panic(expected = "InvalidRange")] #[case(2,-6)] - #[should_panic(expected = "InvalidDepthRange")] + #[should_panic(expected = "InvalidRange")] #[case(-2,-6)] - #[should_panic(expected = "InvalidDepthRange")] + #[should_panic(expected = "InvalidRange")] #[case(-6,-2)] - #[should_panic(expected = "InvalidDepthRange")] + #[should_panic(expected = "InvalidRange")] #[case(5, 3)] fn test_proof_specs_try_from(#[case] min_depth: i32, #[case] max_depth: i32) { let raw_proof_spec = RawProofSpec { @@ -225,15 +225,15 @@ mod tests { #[case(0, 0)] #[case(1, 2)] #[case(2, 2)] - #[should_panic(expected = "InvalidPrefixLengthRange")] + #[should_panic(expected = "InvalidRange")] #[case(2, 1)] - #[should_panic(expected = "InvalidPrefixLengthRange")] + #[should_panic(expected = "InvalidRange")] #[case(-2,1)] - #[should_panic(expected = "InvalidPrefixLengthRange")] + #[should_panic(expected = "InvalidRange")] #[case(2,-1)] - #[should_panic(expected = "InvalidPrefixLengthRange")] + #[should_panic(expected = "InvalidRange")] #[case(-2,-1)] - #[should_panic(expected = "InvalidPrefixLengthRange")] + #[should_panic(expected = "InvalidRange")] #[case(-1,-2)] fn test_inner_specs_try_from(#[case] min_prefix_length: i32, #[case] max_prefix_length: i32) { let raw_inner_spec = RawInnerSpec { diff --git a/ibc-core/ics24-host/cosmos/src/upgrade_proposal/handler.rs b/ibc-core/ics24-host/cosmos/src/upgrade_proposal/handler.rs index 3f27bad12..419a1e46c 100644 --- a/ibc-core/ics24-host/cosmos/src/upgrade_proposal/handler.rs +++ b/ibc-core/ics24-host/cosmos/src/upgrade_proposal/handler.rs @@ -29,7 +29,7 @@ where let mut client_state = TmClientState::try_from(proposal.upgraded_client_state).map_err(|e| { UpgradeClientError::InvalidUpgradeProposal { - reason: e.to_string(), + description: e.to_string(), } })?; diff --git a/ibc-core/ics24-host/cosmos/src/upgrade_proposal/plan.rs b/ibc-core/ics24-host/cosmos/src/upgrade_proposal/plan.rs index d1a25adcc..f66487cc7 100644 --- a/ibc-core/ics24-host/cosmos/src/upgrade_proposal/plan.rs +++ b/ibc-core/ics24-host/cosmos/src/upgrade_proposal/plan.rs @@ -33,21 +33,21 @@ impl TryFrom for Plan { fn try_from(raw: RawPlan) -> Result { if raw.name.is_empty() { return Err(UpgradeClientError::InvalidUpgradePlan { - reason: "name field cannot be empty".to_string(), + description: "name field cannot be empty".to_string(), }); } #[allow(deprecated)] if raw.time.is_some() { return Err(UpgradeClientError::InvalidUpgradePlan { - reason: "time field must be empty".to_string(), + description: "time field must be empty".to_string(), }); } #[allow(deprecated)] if raw.upgraded_client_state.is_some() { return Err(UpgradeClientError::InvalidUpgradePlan { - reason: "upgraded_client_state field must be empty".to_string(), + description: "upgraded_client_state field must be empty".to_string(), }); } @@ -55,7 +55,7 @@ impl TryFrom for Plan { name: raw.name, height: u64::try_from(raw.height).map_err(|_| { UpgradeClientError::InvalidUpgradePlan { - reason: "height plan overflow".to_string(), + description: "height plan overflow".to_string(), } })?, info: raw.info, @@ -83,17 +83,15 @@ impl TryFrom for Plan { fn try_from(any: Any) -> Result { if any.type_url != TYPE_URL { - return Err(UpgradeClientError::InvalidUpgradePlan { - reason: format!( - "type_url do not match: expected {}, got {}", - TYPE_URL, any.type_url - ), + return Err(UpgradeClientError::MismatchedTypeUrls { + expected: TYPE_URL.to_string(), + actual: any.type_url, }); } let plan = Protobuf::::decode_vec(&any.value).map_err(|e| { - UpgradeClientError::InvalidUpgradePlan { - reason: format!("raw plan decode error: {}", e), + UpgradeClientError::FailedToDecodeRawUpgradePlan { + description: e.to_string(), } })?; diff --git a/ibc-core/ics24-host/cosmos/src/upgrade_proposal/proposal.rs b/ibc-core/ics24-host/cosmos/src/upgrade_proposal/proposal.rs index a12f4fab4..4a72af753 100644 --- a/ibc-core/ics24-host/cosmos/src/upgrade_proposal/proposal.rs +++ b/ibc-core/ics24-host/cosmos/src/upgrade_proposal/proposal.rs @@ -33,13 +33,13 @@ impl TryFrom for UpgradeProposal { fn try_from(raw: RawUpgradeProposal) -> Result { if raw.title.is_empty() { return Err(UpgradeClientError::InvalidUpgradeProposal { - reason: "title field cannot be empty".to_string(), + description: "title field cannot be empty".to_string(), }); } if raw.description.is_empty() { return Err(UpgradeClientError::InvalidUpgradeProposal { - reason: "description field cannot be empty".to_string(), + description: "description field cannot be empty".to_string(), }); } @@ -47,13 +47,13 @@ impl TryFrom for UpgradeProposal { plan.try_into()? } else { return Err(UpgradeClientError::InvalidUpgradeProposal { - reason: "plan field cannot be empty".to_string(), + description: "plan field cannot be empty".to_string(), }); }; let upgraded_client_state = raw.upgraded_client_state.ok_or_else(|| { UpgradeClientError::InvalidUpgradeProposal { - reason: "upgraded client state cannot be empty".to_string(), + description: "upgraded client state cannot be empty".to_string(), } })?; diff --git a/ibc-core/ics24-host/cosmos/src/validate_self_client.rs b/ibc-core/ics24-host/cosmos/src/validate_self_client.rs index 7064fdcd8..d2f2d2eba 100644 --- a/ibc-core/ics24-host/cosmos/src/validate_self_client.rs +++ b/ibc-core/ics24-host/cosmos/src/validate_self_client.rs @@ -2,7 +2,7 @@ use core::time::Duration; use ibc_client_tendermint::types::ClientState as TmClientState; use ibc_core_client_types::error::ClientError; -use ibc_core_client_types::Height; +use ibc_core_client_types::{Height, Status}; use ibc_core_commitment_types::specs::ProofSpecs; use ibc_core_connection_types::error::ConnectionError; use ibc_core_handler_types::error::ContextError; @@ -25,17 +25,14 @@ pub trait ValidateSelfClientContext { .map_err(ClientError::from)?; if client_state_of_host_on_counterparty.is_frozen() { - return Err(ClientError::ClientFrozen { - description: String::new(), - } - .into()); + return Err(ClientError::InvalidStatus(Status::Frozen).into()); } let self_chain_id = self.chain_id(); if self_chain_id != &client_state_of_host_on_counterparty.chain_id { return Err(ContextError::ConnectionError( ConnectionError::InvalidClientState { - reason: format!( + description: format!( "invalid chain-id. expected: {}, got: {}", self_chain_id, client_state_of_host_on_counterparty.chain_id ), @@ -48,7 +45,7 @@ pub trait ValidateSelfClientContext { if self_revision_number != latest_height.revision_number() { return Err(ContextError::ConnectionError( ConnectionError::InvalidClientState { - reason: format!( + description: format!( "client is not in the same revision as the chain. expected: {}, got: {}", self_revision_number, latest_height.revision_number() @@ -60,7 +57,7 @@ pub trait ValidateSelfClientContext { if latest_height >= self.host_current_height() { return Err(ContextError::ConnectionError( ConnectionError::InvalidClientState { - reason: format!( + description: format!( "client has latest height {} greater than or equal to chain height {}", latest_height, self.host_current_height() @@ -72,7 +69,7 @@ pub trait ValidateSelfClientContext { if self.proof_specs() != &client_state_of_host_on_counterparty.proof_specs { return Err(ContextError::ConnectionError( ConnectionError::InvalidClientState { - reason: format!( + description: format!( "client has invalid proof specs. expected: {:?}, got: {:?}", self.proof_specs(), client_state_of_host_on_counterparty.proof_specs @@ -89,14 +86,14 @@ pub trait ValidateSelfClientContext { trust_level.denominator(), ) .map_err(|_| ConnectionError::InvalidClientState { - reason: "invalid trust level".to_string(), + description: "invalid trust level".to_string(), })? }; if self.unbonding_period() != client_state_of_host_on_counterparty.unbonding_period { return Err(ContextError::ConnectionError( ConnectionError::InvalidClientState { - reason: format!( + description: format!( "invalid unbonding period. expected: {:?}, got: {:?}", self.unbonding_period(), client_state_of_host_on_counterparty.unbonding_period, @@ -108,7 +105,7 @@ pub trait ValidateSelfClientContext { if client_state_of_host_on_counterparty.unbonding_period < client_state_of_host_on_counterparty.trusting_period { - return Err(ContextError::ConnectionError(ConnectionError::InvalidClientState{ reason: format!( + return Err(ContextError::ConnectionError(ConnectionError::InvalidClientState{ description: format!( "unbonding period must be greater than trusting period. unbonding period ({:?}) < trusting period ({:?})", client_state_of_host_on_counterparty.unbonding_period, client_state_of_host_on_counterparty.trusting_period @@ -120,7 +117,7 @@ pub trait ValidateSelfClientContext { { return Err(ContextError::ConnectionError( ConnectionError::InvalidClientState { - reason: format!( + description: format!( "invalid upgrade path. expected: {:?}, got: {:?}", self.upgrade_path(), client_state_of_host_on_counterparty.upgrade_path diff --git a/ibc-core/ics24-host/types/src/error.rs b/ibc-core/ics24-host/types/src/error.rs index 4df4c4dd9..72d8535c2 100644 --- a/ibc-core/ics24-host/types/src/error.rs +++ b/ibc-core/ics24-host/types/src/error.rs @@ -1,21 +1,20 @@ use displaydoc::Display; use ibc_primitives::prelude::*; +/// Errors that arise when parsing identifiers. #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[derive(Debug, Display)] pub enum IdentifierError { - /// identifier `{id}` has invalid length; must be between `{min}` and `{max}` characters - InvalidLength { id: String, min: u64, max: u64 }, - /// identifier `{id}` must only contain alphanumeric characters or `.`, `_`, `+`, `-`, `#`, - `[`, `]`, `<`, `>` - InvalidCharacter { id: String }, - /// identifier prefix `{prefix}` is invalid - InvalidPrefix { prefix: String }, - /// chain identifier is not formatted with revision number - UnformattedRevisionNumber { chain_id: String }, - /// revision number overflowed - RevisionNumberOverflow, - /// String `{value}` cannot be converted to packet sequence, error: `{reason}` - InvalidStringAsSequence { value: String, reason: String }, + /// id `{actual}` has invalid length; must be between [`{min}`,`{max}`) + InvalidLength { actual: String, min: u64, max: u64 }, + /// id `{0}` can only contain alphanumeric characters or `.`, `_`, `+`, `-`, `#`, - `[`, `]`, `<`, `>` + InvalidCharacter(String), + /// invalid prefix: `{0}` + InvalidPrefix(String), + /// failed to parse `{value}`: `{description}` + FailedToParse { value: String, description: String }, + /// overflowed revision number + OverflowedRevisionNumber, } #[cfg(feature = "std")] diff --git a/ibc-core/ics24-host/types/src/identifiers/chain_id.rs b/ibc-core/ics24-host/types/src/identifiers/chain_id.rs index d37ad4e1f..eeae43a6a 100644 --- a/ibc-core/ics24-host/types/src/identifiers/chain_id.rs +++ b/ibc-core/ics24-host/types/src/identifiers/chain_id.rs @@ -98,7 +98,7 @@ impl ChainId { let inc_revision_number = self .revision_number .checked_add(1) - .ok_or(IdentifierError::RevisionNumberOverflow)?; + .ok_or(IdentifierError::OverflowedRevisionNumber)?; self.id = format!("{}-{}", chain_name, inc_revision_number); self.revision_number = inc_revision_number; Ok(()) @@ -312,8 +312,9 @@ fn parse_chain_id_string(chain_id_str: &str) -> Result<(&str, u64), IdentifierEr .ok() .map(|revision_number| (chain_name, revision_number)) }) - .ok_or(IdentifierError::UnformattedRevisionNumber { - chain_id: chain_id_str.to_string(), + .ok_or(IdentifierError::FailedToParse { + value: chain_id_str.to_string(), + description: "invalid revision number".to_string(), }) } diff --git a/ibc-core/ics24-host/types/src/identifiers/sequence.rs b/ibc-core/ics24-host/types/src/identifiers/sequence.rs index 9e8b2ccbc..146a978d7 100644 --- a/ibc-core/ics24-host/types/src/identifiers/sequence.rs +++ b/ibc-core/ics24-host/types/src/identifiers/sequence.rs @@ -25,9 +25,9 @@ impl core::str::FromStr for Sequence { fn from_str(s: &str) -> Result { Ok(Self::from(s.parse::().map_err(|e| { - IdentifierError::InvalidStringAsSequence { + IdentifierError::FailedToParse { value: s.to_string(), - reason: e.to_string(), + description: e.to_string(), } })?)) } diff --git a/ibc-core/ics24-host/types/src/validate.rs b/ibc-core/ics24-host/types/src/validate.rs index 75e3494ef..3c1557d6a 100644 --- a/ibc-core/ics24-host/types/src/validate.rs +++ b/ibc-core/ics24-host/types/src/validate.rs @@ -17,7 +17,7 @@ pub fn validate_identifier_chars(id: &str) -> Result<(), Error> { .chars() .all(|c| c.is_alphanumeric() || VALID_SPECIAL_CHARS.contains(c)) { - return Err(Error::InvalidCharacter { id: id.into() }); + return Err(Error::InvalidCharacter(id.into())); } // All good! @@ -35,7 +35,7 @@ pub fn validate_identifier_length(id: &str, min: u64, max: u64) -> Result<(), Er Ok(()) } else { Err(Error::InvalidLength { - id: id.into(), + actual: id.into(), min, max, }) @@ -67,17 +67,18 @@ pub fn validate_prefix_length( pub fn validate_named_u64_index(id: &str, name: &str) -> Result<(), Error> { let number_s = id .strip_prefix(name) - .ok_or_else(|| Error::InvalidPrefix { prefix: id.into() })? + .ok_or_else(|| Error::InvalidPrefix(id.into()))? .strip_prefix('-') - .ok_or_else(|| Error::InvalidPrefix { prefix: id.into() })?; + .ok_or_else(|| Error::InvalidPrefix(id.into()))?; if number_s.starts_with('0') && number_s.len() > 1 { - return Err(Error::InvalidPrefix { prefix: id.into() }); + return Err(Error::InvalidPrefix(id.into())); } - _ = number_s - .parse::() - .map_err(|_| Error::InvalidPrefix { prefix: id.into() })?; + _ = number_s.parse::().map_err(|_| Error::FailedToParse { + value: id.into(), + description: "invalid prefix".to_string(), + })?; Ok(()) } diff --git a/ibc-core/ics25-handler/src/entrypoint.rs b/ibc-core/ics25-handler/src/entrypoint.rs index 3c271a9bb..575e5e096 100644 --- a/ibc-core/ics25-handler/src/entrypoint.rs +++ b/ibc-core/ics25-handler/src/entrypoint.rs @@ -80,12 +80,10 @@ where let port_id = channel_msg_to_port_id(&msg); let module_id = router .lookup_module(port_id) - .ok_or(RouterError::UnknownPort { - port_id: port_id.clone(), - })?; + .ok_or(RouterError::UnknownPort(port_id.clone()))?; let module = router .get_route(&module_id) - .ok_or(RouterError::ModuleNotFound)?; + .ok_or(RouterError::MissingModule)?; match msg { ChannelMsg::OpenInit(msg) => chan_open_init_validate(ctx, module, msg), @@ -100,12 +98,10 @@ where let port_id = packet_msg_to_port_id(&msg); let module_id = router .lookup_module(port_id) - .ok_or(RouterError::UnknownPort { - port_id: port_id.clone(), - })?; + .ok_or(RouterError::UnknownPort(port_id.clone()))?; let module = router .get_route(&module_id) - .ok_or(RouterError::ModuleNotFound)?; + .ok_or(RouterError::MissingModule)?; match msg { PacketMsg::Recv(msg) => recv_packet_validate(ctx, msg), @@ -157,12 +153,10 @@ where let port_id = channel_msg_to_port_id(&msg); let module_id = router .lookup_module(port_id) - .ok_or(RouterError::UnknownPort { - port_id: port_id.clone(), - })?; + .ok_or(RouterError::UnknownPort(port_id.clone()))?; let module = router .get_route_mut(&module_id) - .ok_or(RouterError::ModuleNotFound)?; + .ok_or(RouterError::MissingModule)?; match msg { ChannelMsg::OpenInit(msg) => chan_open_init_execute(ctx, module, msg), @@ -177,12 +171,10 @@ where let port_id = packet_msg_to_port_id(&msg); let module_id = router .lookup_module(port_id) - .ok_or(RouterError::UnknownPort { - port_id: port_id.clone(), - })?; + .ok_or(RouterError::UnknownPort(port_id.clone()))?; let module = router .get_route_mut(&module_id) - .ok_or(RouterError::ModuleNotFound)?; + .ok_or(RouterError::MissingModule)?; match msg { PacketMsg::Recv(msg) => recv_packet_execute(ctx, module, msg), diff --git a/ibc-core/ics25-handler/types/src/events.rs b/ibc-core/ics25-handler/types/src/events.rs index aa62ea813..28480c375 100644 --- a/ibc-core/ics25-handler/types/src/events.rs +++ b/ibc-core/ics25-handler/types/src/events.rs @@ -1,51 +1,13 @@ //! Defines events emitted during handling of IBC messages -use displaydoc::Display; -use ibc_core_channel_types::{error as channel_error, events as ChannelEvents}; -use ibc_core_client_types::error as client_error; +use ibc_core_channel_types::error::ChannelError; +use ibc_core_channel_types::events as ChannelEvents; use ibc_core_client_types::events::{self as ClientEvents}; -use ibc_core_connection_types::{error as connection_error, events as ConnectionEvents}; -use ibc_core_host_types::error::IdentifierError; +use ibc_core_connection_types::events as ConnectionEvents; use ibc_core_router_types::event::ModuleEvent; use ibc_primitives::prelude::*; -use ibc_primitives::TimestampError; use tendermint::abci; -/// All error variants related to IBC events -#[derive(Debug, Display)] -pub enum Error { - /// error parsing height - Height, - /// parse error: `{0}` - Parse(IdentifierError), - /// client error: `{0}` - Client(client_error::ClientError), - /// connection error: `{0}` - Connection(connection_error::ConnectionError), - /// channel error: `{0}` - Channel(channel_error::ChannelError), - /// parsing timestamp error: `{0}` - Timestamp(TimestampError), - /// incorrect event type: `{event}` - IncorrectEventType { event: String }, - /// module event cannot use core event types: `{event:?}` - MalformedModuleEvent { event: ModuleEvent }, -} - -#[cfg(feature = "std")] -impl std::error::Error for Error { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match &self { - Self::Parse(e) => Some(e), - Self::Client(e) => Some(e), - Self::Connection(e) => Some(e), - Self::Channel(e) => Some(e), - Self::Timestamp(e) => Some(e), - _ => None, - } - } -} - const MESSAGE_EVENT: &str = "message"; /// Events created by the IBC component of a chain, destined for a relayer. @@ -93,7 +55,7 @@ pub enum IbcEvent { } impl TryFrom for abci::Event { - type Error = Error; + type Error = ChannelError; fn try_from(event: IbcEvent) -> Result { Ok(match event { @@ -111,11 +73,11 @@ impl TryFrom for abci::Event { IbcEvent::OpenConfirmChannel(event) => event.into(), IbcEvent::CloseInitChannel(event) => event.into(), IbcEvent::CloseConfirmChannel(event) => event.into(), - IbcEvent::SendPacket(event) => event.try_into().map_err(Error::Channel)?, - IbcEvent::ReceivePacket(event) => event.try_into().map_err(Error::Channel)?, - IbcEvent::WriteAcknowledgement(event) => event.try_into().map_err(Error::Channel)?, - IbcEvent::AcknowledgePacket(event) => event.try_into().map_err(Error::Channel)?, - IbcEvent::TimeoutPacket(event) => event.try_into().map_err(Error::Channel)?, + IbcEvent::SendPacket(event) => event.try_into()?, + IbcEvent::ReceivePacket(event) => event.try_into()?, + IbcEvent::WriteAcknowledgement(event) => event.try_into()?, + IbcEvent::AcknowledgePacket(event) => event.try_into()?, + IbcEvent::TimeoutPacket(event) => event.try_into()?, IbcEvent::ChannelClosed(event) => event.into(), IbcEvent::Module(event) => event.into(), IbcEvent::Message(event) => abci::Event { diff --git a/ibc-core/ics25-handler/types/src/msgs.rs b/ibc-core/ics25-handler/types/src/msgs.rs index 1272c0aff..e3276b951 100644 --- a/ibc-core/ics25-handler/types/src/msgs.rs +++ b/ibc-core/ics25-handler/types/src/msgs.rs @@ -47,7 +47,7 @@ impl TryFrom for MsgEnvelope { // Pop out the message and then wrap it in the corresponding type. let domain_msg = MsgCreateClient::decode_vec(&any_msg.value).map_err(|e| { RouterError::MalformedMessageBytes { - reason: e.to_string(), + description: e.to_string(), } })?; Ok(MsgEnvelope::Client(ClientMsg::CreateClient(domain_msg))) @@ -55,7 +55,7 @@ impl TryFrom for MsgEnvelope { UPDATE_CLIENT_TYPE_URL => { let domain_msg = MsgUpdateClient::decode_vec(&any_msg.value).map_err(|e| { RouterError::MalformedMessageBytes { - reason: e.to_string(), + description: e.to_string(), } })?; Ok(MsgEnvelope::Client(ClientMsg::UpdateClient(domain_msg))) @@ -63,7 +63,7 @@ impl TryFrom for MsgEnvelope { UPGRADE_CLIENT_TYPE_URL => { let domain_msg = MsgUpgradeClient::decode_vec(&any_msg.value).map_err(|e| { RouterError::MalformedMessageBytes { - reason: e.to_string(), + description: e.to_string(), } })?; Ok(MsgEnvelope::Client(ClientMsg::UpgradeClient(domain_msg))) @@ -72,7 +72,7 @@ impl TryFrom for MsgEnvelope { let domain_msg = MsgSubmitMisbehaviour::decode_vec(&any_msg.value).map_err(|e| { RouterError::MalformedMessageBytes { - reason: e.to_string(), + description: e.to_string(), } })?; Ok(MsgEnvelope::Client(ClientMsg::Misbehaviour(domain_msg))) @@ -83,7 +83,7 @@ impl TryFrom for MsgEnvelope { let domain_msg = MsgConnectionOpenInit::decode_vec(&any_msg.value).map_err(|e| { RouterError::MalformedMessageBytes { - reason: e.to_string(), + description: e.to_string(), } })?; Ok(MsgEnvelope::Connection(ConnectionMsg::OpenInit(domain_msg))) @@ -91,7 +91,7 @@ impl TryFrom for MsgEnvelope { CONN_OPEN_TRY_TYPE_URL => { let domain_msg = MsgConnectionOpenTry::decode_vec(&any_msg.value).map_err(|e| { RouterError::MalformedMessageBytes { - reason: e.to_string(), + description: e.to_string(), } })?; Ok(MsgEnvelope::Connection(ConnectionMsg::OpenTry(domain_msg))) @@ -99,7 +99,7 @@ impl TryFrom for MsgEnvelope { CONN_OPEN_ACK_TYPE_URL => { let domain_msg = MsgConnectionOpenAck::decode_vec(&any_msg.value).map_err(|e| { RouterError::MalformedMessageBytes { - reason: e.to_string(), + description: e.to_string(), } })?; Ok(MsgEnvelope::Connection(ConnectionMsg::OpenAck(domain_msg))) @@ -108,7 +108,7 @@ impl TryFrom for MsgEnvelope { let domain_msg = MsgConnectionOpenConfirm::decode_vec(&any_msg.value).map_err(|e| { RouterError::MalformedMessageBytes { - reason: e.to_string(), + description: e.to_string(), } })?; Ok(MsgEnvelope::Connection(ConnectionMsg::OpenConfirm( @@ -120,7 +120,7 @@ impl TryFrom for MsgEnvelope { CHAN_OPEN_INIT_TYPE_URL => { let domain_msg = MsgChannelOpenInit::decode_vec(&any_msg.value).map_err(|e| { RouterError::MalformedMessageBytes { - reason: e.to_string(), + description: e.to_string(), } })?; Ok(MsgEnvelope::Channel(ChannelMsg::OpenInit(domain_msg))) @@ -128,7 +128,7 @@ impl TryFrom for MsgEnvelope { CHAN_OPEN_TRY_TYPE_URL => { let domain_msg = MsgChannelOpenTry::decode_vec(&any_msg.value).map_err(|e| { RouterError::MalformedMessageBytes { - reason: e.to_string(), + description: e.to_string(), } })?; Ok(MsgEnvelope::Channel(ChannelMsg::OpenTry(domain_msg))) @@ -136,7 +136,7 @@ impl TryFrom for MsgEnvelope { CHAN_OPEN_ACK_TYPE_URL => { let domain_msg = MsgChannelOpenAck::decode_vec(&any_msg.value).map_err(|e| { RouterError::MalformedMessageBytes { - reason: e.to_string(), + description: e.to_string(), } })?; Ok(MsgEnvelope::Channel(ChannelMsg::OpenAck(domain_msg))) @@ -145,7 +145,7 @@ impl TryFrom for MsgEnvelope { let domain_msg = MsgChannelOpenConfirm::decode_vec(&any_msg.value).map_err(|e| { RouterError::MalformedMessageBytes { - reason: e.to_string(), + description: e.to_string(), } })?; Ok(MsgEnvelope::Channel(ChannelMsg::OpenConfirm(domain_msg))) @@ -153,7 +153,7 @@ impl TryFrom for MsgEnvelope { CHAN_CLOSE_INIT_TYPE_URL => { let domain_msg = MsgChannelCloseInit::decode_vec(&any_msg.value).map_err(|e| { RouterError::MalformedMessageBytes { - reason: e.to_string(), + description: e.to_string(), } })?; Ok(MsgEnvelope::Channel(ChannelMsg::CloseInit(domain_msg))) @@ -162,7 +162,7 @@ impl TryFrom for MsgEnvelope { let domain_msg = MsgChannelCloseConfirm::decode_vec(&any_msg.value).map_err(|e| { RouterError::MalformedMessageBytes { - reason: e.to_string(), + description: e.to_string(), } })?; Ok(MsgEnvelope::Channel(ChannelMsg::CloseConfirm(domain_msg))) @@ -171,7 +171,7 @@ impl TryFrom for MsgEnvelope { RECV_PACKET_TYPE_URL => { let domain_msg = MsgRecvPacket::decode_vec(&any_msg.value).map_err(|e| { RouterError::MalformedMessageBytes { - reason: e.to_string(), + description: e.to_string(), } })?; Ok(MsgEnvelope::Packet(PacketMsg::Recv(domain_msg))) @@ -179,7 +179,7 @@ impl TryFrom for MsgEnvelope { ACKNOWLEDGEMENT_TYPE_URL => { let domain_msg = MsgAcknowledgement::decode_vec(&any_msg.value).map_err(|e| { RouterError::MalformedMessageBytes { - reason: e.to_string(), + description: e.to_string(), } })?; Ok(MsgEnvelope::Packet(PacketMsg::Ack(domain_msg))) @@ -187,7 +187,7 @@ impl TryFrom for MsgEnvelope { TIMEOUT_TYPE_URL => { let domain_msg = MsgTimeout::decode_vec(&any_msg.value).map_err(|e| { RouterError::MalformedMessageBytes { - reason: e.to_string(), + description: e.to_string(), } })?; Ok(MsgEnvelope::Packet(PacketMsg::Timeout(domain_msg))) @@ -195,14 +195,12 @@ impl TryFrom for MsgEnvelope { TIMEOUT_ON_CLOSE_TYPE_URL => { let domain_msg = MsgTimeoutOnClose::decode_vec(&any_msg.value).map_err(|e| { RouterError::MalformedMessageBytes { - reason: e.to_string(), + description: e.to_string(), } })?; Ok(MsgEnvelope::Packet(PacketMsg::TimeoutOnClose(domain_msg))) } - _ => Err(RouterError::UnknownMessageTypeUrl { - url: any_msg.type_url, - }), + _ => Err(RouterError::UnknownMessageTypeUrl(any_msg.type_url)), } } } diff --git a/ibc-core/ics26-routing/types/src/error.rs b/ibc-core/ics26-routing/types/src/error.rs index 01fb3c6c6..663cbcc6a 100644 --- a/ibc-core/ics26-routing/types/src/error.rs +++ b/ibc-core/ics26-routing/types/src/error.rs @@ -5,14 +5,14 @@ use ibc_primitives::prelude::*; /// Error type for the router module. #[derive(Debug, Display)] pub enum RouterError { - /// unknown type URL `{url}` - UnknownMessageTypeUrl { url: String }, - /// the message is malformed and cannot be decoded error: `{reason}` - MalformedMessageBytes { reason: String }, - /// port `{port_id}` is unknown - UnknownPort { port_id: PortId }, - /// module not found - ModuleNotFound, + /// malformed message that could not be decoded: `{description}` + MalformedMessageBytes { description: String }, + /// missing module + MissingModule, + /// unknown message type URL `{0}` + UnknownMessageTypeUrl(String), + /// unknown port `{0}` + UnknownPort(PortId), } #[cfg(feature = "std")] diff --git a/ibc-testkit/src/fixtures/clients/tendermint.rs b/ibc-testkit/src/fixtures/clients/tendermint.rs index 89d870f72..74c5a14d9 100644 --- a/ibc-testkit/src/fixtures/clients/tendermint.rs +++ b/ibc-testkit/src/fixtures/clients/tendermint.rs @@ -3,7 +3,7 @@ use core::time::Duration; use basecoin_store::avl::get_proof_spec as basecoin_proof_spec; use ibc::clients::tendermint::client_state::ClientState as TmClientState; -use ibc::clients::tendermint::types::error::{Error as ClientError, Error}; +use ibc::clients::tendermint::types::error::TendermintClientError; use ibc::clients::tendermint::types::proto::v1::{ClientState as RawTmClientState, Fraction}; #[cfg(feature = "serde")] use ibc::clients::tendermint::types::Header; @@ -19,7 +19,9 @@ use tendermint::block::Header as TmHeader; use typed_builder::TypedBuilder; /// Returns a dummy tendermint `ClientState` by given `frozen_height`, for testing purposes only! -pub fn dummy_tm_client_state_from_raw(frozen_height: RawHeight) -> Result { +pub fn dummy_tm_client_state_from_raw( + frozen_height: RawHeight, +) -> Result { ClientStateType::try_from(dummy_raw_tm_client_state(frozen_height)).map(TmClientState::from) } @@ -95,7 +97,7 @@ impl ClientStateConfig { self, chain_id: ChainId, latest_height: Height, - ) -> Result { + ) -> Result { Ok(ClientStateType::new( chain_id, self.trust_level, diff --git a/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs b/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs index c10a14210..780392577 100644 --- a/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs +++ b/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs @@ -136,9 +136,7 @@ impl TryFrom for MockClientState { } match raw.type_url.as_str() { MOCK_CLIENT_STATE_TYPE_URL => decode_client_state(&raw.value), - _ => Err(ClientError::UnknownClientStateType { - client_state_type: raw.type_url, - }), + _ => Err(ClientError::InvalidClientStateType(raw.type_url)), } } } @@ -171,9 +169,7 @@ impl ClientStateCommon for MockClientState { if consensus_state_status(&mock_consensus_state, host_timestamp, self.trusting_period)? .is_expired() { - return Err(ClientError::ClientNotActive { - status: Status::Expired, - }); + return Err(ClientError::InvalidStatus(Status::Expired)); } Ok(()) @@ -190,8 +186,8 @@ impl ClientStateCommon for MockClientState { fn validate_proof_height(&self, proof_height: Height) -> Result<(), ClientError> { if self.latest_height() < proof_height { return Err(ClientError::InvalidProofHeight { - latest_height: self.latest_height(), - proof_height, + actual: self.latest_height(), + expected: proof_height, }); } Ok(()) @@ -212,7 +208,7 @@ impl ClientStateCommon for MockClientState { let upgraded_mock_client_state = Self::try_from(upgraded_client_state)?; MockConsensusState::try_from(upgraded_consensus_state)?; if self.latest_height() >= upgraded_mock_client_state.latest_height() { - return Err(UpgradeClientError::LowUpgradeHeight { + return Err(UpgradeClientError::InsufficientUpgradeHeight { upgraded_height: self.latest_height(), client_height: upgraded_mock_client_state.latest_height(), })?; @@ -285,9 +281,7 @@ where Ok(header_heights_equal && headers_are_in_future) } - header_type => Err(ClientError::UnknownHeaderType { - header_type: header_type.to_owned(), - }), + header_type => Err(ClientError::InvalidHeaderType(header_type.to_owned())), } } diff --git a/ibc-testkit/src/testapp/ibc/clients/mock/consensus_state.rs b/ibc-testkit/src/testapp/ibc/clients/mock/consensus_state.rs index 82536d2d6..96273f780 100644 --- a/ibc-testkit/src/testapp/ibc/clients/mock/consensus_state.rs +++ b/ibc-testkit/src/testapp/ibc/clients/mock/consensus_state.rs @@ -75,9 +75,7 @@ impl TryFrom for MockConsensusState { } match raw.type_url.as_str() { MOCK_CONSENSUS_STATE_TYPE_URL => decode_consensus_state(&raw.value), - _ => Err(ClientError::UnknownConsensusStateType { - consensus_state_type: raw.type_url, - }), + _ => Err(ClientError::InvalidConsensusStateType(raw.type_url)), } } } diff --git a/ibc-testkit/src/testapp/ibc/clients/mock/header.rs b/ibc-testkit/src/testapp/ibc/clients/mock/header.rs index d45e11827..fac7bf00c 100644 --- a/ibc-testkit/src/testapp/ibc/clients/mock/header.rs +++ b/ibc-testkit/src/testapp/ibc/clients/mock/header.rs @@ -97,12 +97,10 @@ impl TryFrom for MockHeader { match raw.type_url.as_str() { MOCK_HEADER_TYPE_URL => Ok(Protobuf::::decode_vec(&raw.value).map_err( |e| ClientError::InvalidRawHeader { - reason: e.to_string(), + description: e.to_string(), }, )?), - _ => Err(ClientError::UnknownHeaderType { - header_type: raw.type_url, - }), + _ => Err(ClientError::InvalidHeaderType(raw.type_url)), } } } diff --git a/ibc-testkit/src/testapp/ibc/clients/mock/misbehaviour.rs b/ibc-testkit/src/testapp/ibc/clients/mock/misbehaviour.rs index ff4664da4..e07c310a1 100644 --- a/ibc-testkit/src/testapp/ibc/clients/mock/misbehaviour.rs +++ b/ibc-testkit/src/testapp/ibc/clients/mock/misbehaviour.rs @@ -61,9 +61,7 @@ impl TryFrom for Misbehaviour { } match raw.type_url.as_str() { MOCK_MISBEHAVIOUR_TYPE_URL => decode_misbehaviour(&raw.value), - _ => Err(ClientError::UnknownMisbehaviourType { - misbehaviour_type: raw.type_url, - }), + _ => Err(ClientError::InvalidMisbehaviourType(raw.type_url)), } } } diff --git a/ibc-testkit/src/testapp/ibc/core/client_ctx.rs b/ibc-testkit/src/testapp/ibc/core/client_ctx.rs index 8c7bd8dd2..a2c9d59d7 100644 --- a/ibc-testkit/src/testapp/ibc/core/client_ctx.rs +++ b/ibc-testkit/src/testapp/ibc/core/client_ctx.rs @@ -108,7 +108,7 @@ where .map(|path| { self.consensus_state_store .get(StoreHeight::Pending, &path) - .ok_or_else(|| ClientError::ConsensusStateNotFound { + .ok_or_else(|| ClientError::MissingConsensusState { client_id: client_id.clone(), height: *height, }) @@ -141,7 +141,7 @@ where .map(|path| { self.consensus_state_store .get(StoreHeight::Pending, &path) - .ok_or_else(|| ClientError::ConsensusStateNotFound { + .ok_or_else(|| ClientError::MissingConsensusState { client_id: client_id.clone(), height: *height, }) @@ -163,9 +163,7 @@ where Ok(self .client_state_store .get(StoreHeight::Pending, &ClientStatePath(client_id.clone())) - .ok_or(ClientError::ClientStateNotFound { - client_id: client_id.clone(), - })?) + .ok_or(ClientError::MissingClientState(client_id.clone()))?) } fn consensus_state( @@ -180,7 +178,7 @@ where let consensus_state = self .consensus_state_store .get(StoreHeight::Pending, client_cons_state_path) - .ok_or(ClientError::ConsensusStateNotFound { + .ok_or(ClientError::MissingConsensusState { client_id: client_cons_state_path.client_id.clone(), height, })?; @@ -203,7 +201,7 @@ where let processed_timestamp = self .client_processed_times .get(StoreHeight::Pending, &client_update_time_path) - .ok_or(ClientError::UpdateMetaDataNotFound { + .ok_or(ClientError::MissingUpdateMetaData { client_id: client_id.clone(), height: *height, })?; @@ -215,7 +213,7 @@ where let processed_height = self .client_processed_heights .get(StoreHeight::Pending, &client_update_height_path) - .ok_or(ClientError::UpdateMetaDataNotFound { + .ok_or(ClientError::MissingUpdateMetaData { client_id: client_id.clone(), height: *height, })?; diff --git a/ibc-testkit/src/testapp/ibc/core/core_ctx.rs b/ibc-testkit/src/testapp/ibc/core/core_ctx.rs index a475f90d7..5f572cf6b 100644 --- a/ibc-testkit/src/testapp/ibc/core/core_ctx.rs +++ b/ibc-testkit/src/testapp/ibc/core/core_ctx.rs @@ -11,7 +11,7 @@ use ibc::core::channel::types::error::{ChannelError, PacketError}; use ibc::core::channel::types::packet::{PacketState, Receipt}; use ibc::core::client::context::consensus_state::ConsensusState; use ibc::core::client::types::error::ClientError; -use ibc::core::client::types::Height; +use ibc::core::client::types::{Height, Status}; use ibc::core::commitment_types::commitment::CommitmentPrefix; use ibc::core::commitment_types::merkle::MerkleProof; use ibc::core::connection::types::error::ConnectionError; @@ -72,7 +72,7 @@ where Ok(consensus_states_binding .get(&height.revision_height()) .cloned() - .ok_or(ClientError::MissingLocalConsensusState { height: *height })?) + .ok_or(ClientError::MissingLocalConsensusState(*height))?) } fn validate_self_client( @@ -80,10 +80,7 @@ where client_state_of_host_on_counterparty: Self::HostClientState, ) -> Result<(), ContextError> { if client_state_of_host_on_counterparty.is_frozen() { - return Err(ClientError::ClientFrozen { - description: String::new(), - } - .into()); + return Err(ClientError::InvalidStatus(Status::Frozen).into()); } let latest_height = self.host_height()?; @@ -96,7 +93,7 @@ where { return Err(ContextError::ConnectionError( ConnectionError::InvalidClientState { - reason: format!( + description: format!( "client is not in the same revision as the chain. expected: {}, got: {}", self_revision_number, client_state_of_host_on_counterparty @@ -111,7 +108,7 @@ where if client_state_of_host_on_counterparty.latest_height() >= host_current_height { return Err(ContextError::ConnectionError( ConnectionError::InvalidClientState { - reason: format!( + description: format!( "client has latest height {} greater than or equal to chain height {}", client_state_of_host_on_counterparty.latest_height(), host_current_height @@ -127,9 +124,7 @@ where Ok(self .connection_end_store .get(StoreHeight::Pending, &ConnectionPath::new(conn_id)) - .ok_or(ConnectionError::ConnectionNotFound { - connection_id: conn_id.clone(), - })?) + .ok_or(ConnectionError::MissingConnection(conn_id.clone()))?) } fn commitment_prefix(&self) -> CommitmentPrefix { @@ -142,9 +137,7 @@ where Ok(self .conn_counter .get(StoreHeight::Pending, &NextConnectionSequencePath) - .ok_or(ConnectionError::Other { - description: "connection counter not found".into(), - })?) + .ok_or(ConnectionError::MissingConnectionCounter)?) } fn channel_end(&self, channel_end_path: &ChannelEndPath) -> Result { @@ -154,7 +147,10 @@ where StoreHeight::Pending, &ChannelEndPath::new(&channel_end_path.0, &channel_end_path.1), ) - .ok_or(ChannelError::MissingChannel)?) + .ok_or(ChannelError::NonexistentChannel { + port_id: channel_end_path.0.clone(), + channel_id: channel_end_path.1.clone(), + })?) } fn get_next_sequence_send( @@ -222,9 +218,7 @@ where ), ) .then_some(Receipt::Ok) - .ok_or(PacketError::PacketReceiptNotFound { - sequence: receipt_path.sequence, - })?) + .ok_or(PacketError::MissingPacketReceipt(receipt_path.sequence))?) } fn get_packet_acknowledgement( @@ -237,9 +231,7 @@ where StoreHeight::Pending, &AckPath::new(&ack_path.port_id, &ack_path.channel_id, ack_path.sequence), ) - .ok_or(PacketError::PacketAcknowledgementNotFound { - sequence: ack_path.sequence, - })?) + .ok_or(PacketError::MissingPacketAcknowledgment(ack_path.sequence))?) } /// Returns a counter of the number of channel ids that have been created thus far. @@ -249,9 +241,7 @@ where Ok(self .channel_counter .get(StoreHeight::Pending, &NextChannelSequencePath) - .ok_or(ChannelError::Other { - description: "channel counter not found".into(), - })?) + .ok_or(ChannelError::MissingCounter)?) } /// Returns the maximum expected time per block @@ -316,9 +306,7 @@ where let client_state = self .client_state_store .get(StoreHeight::Pending, &client_state_path) - .ok_or_else(|| ClientError::ClientStateNotFound { - client_id: client_state_path.0.clone(), - })?; + .ok_or_else(|| ClientError::MissingClientState(client_state_path.0.clone()))?; Ok((client_state_path.0, client_state)) }) .collect() @@ -354,7 +342,7 @@ where .consensus_state_store .get(StoreHeight::Pending, &consensus_path) .ok_or({ - ClientError::ConsensusStateNotFound { + ClientError::MissingConsensusState { client_id: consensus_path.client_id, height, } @@ -409,9 +397,7 @@ where let connection_end = self .connection_end_store .get(StoreHeight::Pending, &connection_path) - .ok_or_else(|| ConnectionError::ConnectionNotFound { - connection_id: connection_path.0.clone(), - })?; + .ok_or_else(|| ConnectionError::MissingConnection(connection_path.0.clone()))?; Ok(IdentifiedConnectionEnd { connection_id: connection_path.0, connection_end, @@ -451,7 +437,7 @@ where let channel_end = self .channel_end_store .get(StoreHeight::Pending, &channel_path) - .ok_or_else(|| ChannelError::ChannelNotFound { + .ok_or_else(|| ChannelError::NonexistentChannel { port_id: channel_path.0.clone(), channel_id: channel_path.1.clone(), })?; @@ -473,10 +459,7 @@ where "commitments/ports/{}/channels/{}/sequences", channel_end_path.0, channel_end_path.1 ) - .try_into() - .map_err(|_| PacketError::Other { - description: "Invalid commitment path".into(), - })?; + .into(); self.packet_commitment_store .get_keys(&path) @@ -520,10 +503,7 @@ where "acks/ports/{}/channels/{}/sequences", channel_end_path.0, channel_end_path.1 ) - .try_into() - .map_err(|_| PacketError::Other { - description: "Invalid ack path".into(), - })?; + .into(); self.packet_ack_store .get_keys(&ack_path_prefix) @@ -601,10 +581,7 @@ where "commitments/ports/{}/channels/{}/sequences", channel_end_path.0, channel_end_path.1 ) - .try_into() - .map_err(|_| PacketError::Other { - description: "Invalid commitment path".into(), - })?; + .into(); self.packet_commitment_store .get_keys(&commitment_path_prefix) @@ -673,9 +650,7 @@ where ) -> Result<(), ContextError> { self.connection_end_store .set(connection_path.clone(), connection_end) - .map_err(|_| ConnectionError::Other { - description: "Connection end store error".to_string(), - })?; + .map_err(|_| ConnectionError::FailedToStoreConnectionEnd)?; Ok(()) } @@ -692,9 +667,7 @@ where conn_ids.push(conn_id); self.connection_ids_store .set(client_connection_path.clone(), conn_ids) - .map_err(|_| ConnectionError::Other { - description: "Connection ids store error".to_string(), - })?; + .map_err(|_| ConnectionError::FailedToStoreConnectionIds)?; Ok(()) } @@ -704,15 +677,11 @@ where let current_sequence = self .conn_counter .get(StoreHeight::Pending, &NextConnectionSequencePath) - .ok_or(ConnectionError::Other { - description: "connection counter not found".into(), - })?; + .ok_or(ConnectionError::MissingConnectionCounter)?; self.conn_counter .set(NextConnectionSequencePath, current_sequence + 1) - .map_err(|e| ConnectionError::Other { - description: format!("connection counter update failed: {e:?}"), - })?; + .map_err(|_| ConnectionError::FailedToUpdateConnectionCounter)?; Ok(()) } @@ -770,8 +739,8 @@ where ) -> Result<(), ContextError> { self.channel_end_store .set(channel_end_path.clone(), channel_end) - .map_err(|_| ChannelError::Other { - description: "Channel end store error".to_string(), + .map_err(|e| ChannelError::FailedToStoreChannel { + description: format!("{e:?}"), })?; Ok(()) } @@ -813,14 +782,12 @@ where let current_sequence = self .channel_counter .get(StoreHeight::Pending, &NextChannelSequencePath) - .ok_or(ChannelError::Other { - description: "channel counter not found".into(), - })?; + .ok_or(ChannelError::MissingCounter)?; self.channel_counter .set(NextChannelSequencePath, current_sequence + 1) - .map_err(|e| ChannelError::Other { - description: format!("channel counter update failed: {e:?}"), + .map_err(|e| ChannelError::FailedToUpdateCounter { + description: format!("{e:?}"), })?; Ok(()) diff --git a/tests-integration/tests/core/ics02_client/create_client.rs b/tests-integration/tests/core/ics02_client/create_client.rs index 71b40718c..78c4f8392 100644 --- a/tests-integration/tests/core/ics02_client/create_client.rs +++ b/tests-integration/tests/core/ics02_client/create_client.rs @@ -180,11 +180,9 @@ fn test_create_expired_mock_client() { let fxt = create_client_fixture(Ctx::Default, Msg::ExpiredMockHeader); create_client_validate( &fxt, - Expect::Failure(Some(ContextError::ClientError( - ClientError::ClientNotActive { - status: Status::Expired, - }, - ))), + Expect::Failure(Some(ContextError::ClientError(ClientError::InvalidStatus( + Status::Expired, + )))), ); } @@ -208,11 +206,9 @@ fn test_create_expired_tm_client() { let fxt = create_client_fixture(Ctx::Default, Msg::ExpiredTendermintHeader); create_client_validate( &fxt, - Expect::Failure(Some(ContextError::ClientError( - ClientError::ClientNotActive { - status: Status::Expired, - }, - ))), + Expect::Failure(Some(ContextError::ClientError(ClientError::InvalidStatus( + Status::Expired, + )))), ); } @@ -222,9 +218,9 @@ fn test_create_frozen_tm_client() { let fxt = create_client_fixture(Ctx::Default, Msg::FrozenTendermintHeader); create_client_validate( &fxt, - Expect::Failure(Some(ContextError::ClientError(ClientError::ClientFrozen { - description: "the client is frozen".to_string(), - }))), + Expect::Failure(Some(ContextError::ClientError(ClientError::InvalidStatus( + Status::Frozen, + )))), ); } @@ -306,6 +302,6 @@ fn test_tm_create_client_proof_verification_ok() { serde_json::to_vec(&(next_client_seq_value + 1)).expect("valid json serialization"), ) .expect_err("proof verification fails"), - ClientError::Ics23Verification(CommitmentError::VerificationFailure) + ClientError::FailedICS23Verification(CommitmentError::FailedToVerifyMembership) )); } diff --git a/tests-integration/tests/core/ics02_client/upgrade_client.rs b/tests-integration/tests/core/ics02_client/upgrade_client.rs index c59a7686a..153d69e42 100644 --- a/tests-integration/tests/core/ics02_client/upgrade_client.rs +++ b/tests-integration/tests/core/ics02_client/upgrade_client.rs @@ -142,9 +142,8 @@ fn msg_upgrade_client_healthy() { #[test] fn upgrade_client_fail_nonexisting_client() { let fxt = msg_upgrade_client_fixture(Ctx::Default, Msg::Default); - let expected_err = ContextError::ClientError(ClientError::ClientStateNotFound { - client_id: fxt.msg.client_id.clone(), - }); + let expected_err = + ContextError::ClientError(ClientError::MissingClientState(fxt.msg.client_id.clone())); upgrade_client_validate(&fxt, Expect::Failure(Some(expected_err))); } @@ -152,7 +151,7 @@ fn upgrade_client_fail_nonexisting_client() { fn upgrade_client_fail_low_upgrade_height() { let fxt: Fixture = msg_upgrade_client_fixture(Ctx::WithClient, Msg::LowUpgradeHeight); - let expected_err: ClientError = UpgradeClientError::LowUpgradeHeight { + let expected_err: ClientError = UpgradeClientError::InsufficientUpgradeHeight { upgraded_height: Height::new(0, 26).unwrap(), client_height: fxt.ctx.host_height().unwrap(), } @@ -166,8 +165,8 @@ fn upgrade_client_fail_low_upgrade_height() { #[test] fn upgrade_client_fail_unknown_upgraded_client_state() { let fxt = msg_upgrade_client_fixture(Ctx::WithClient, Msg::UnknownUpgradedClientStateType); - let expected_err = ContextError::ClientError(ClientError::UnknownClientStateType { - client_state_type: client_type().to_string(), - }); + let expected_err = ContextError::ClientError(ClientError::InvalidClientStateType( + client_type().to_string(), + )); upgrade_client_validate(&fxt, Expect::Failure(Some(expected_err))); } diff --git a/tests-integration/tests/core/ics03_connection/conn_open_ack.rs b/tests-integration/tests/core/ics03_connection/conn_open_ack.rs index 651ab378f..5d292b3f7 100644 --- a/tests-integration/tests/core/ics03_connection/conn_open_ack.rs +++ b/tests-integration/tests/core/ics03_connection/conn_open_ack.rs @@ -126,16 +126,16 @@ fn conn_open_ack_validate(fxt: &Fixture, expect: Expect) { let cons_state_height = fxt.msg.consensus_height_of_a_on_b; match res.unwrap_err() { - ContextError::ConnectionError(ConnectionError::ConnectionNotFound { connection_id }) => { + ContextError::ConnectionError(ConnectionError::MissingConnection(connection_id)) => { assert_eq!(connection_id, right_connection_id) } - ContextError::ConnectionError(ConnectionError::InvalidConsensusHeight { + ContextError::ConnectionError(ConnectionError::InsufficientConsensusHeight { target_height, current_height: _, }) => { assert_eq!(cons_state_height, target_height); } - ContextError::ConnectionError(ConnectionError::InvalidState { + ContextError::ConnectionError(ConnectionError::MismatchedConnectionStates { expected: _, actual: _, }) => {} @@ -187,26 +187,27 @@ fn conn_open_ack_healthy() { #[test] fn conn_open_ack_no_connection() { let fxt = conn_open_ack_fixture(Ctx::New); - let expected_err = ContextError::ConnectionError(ConnectionError::ConnectionNotFound { - connection_id: fxt.msg.conn_id_on_a.clone(), - }); + let expected_err = ContextError::ConnectionError(ConnectionError::MissingConnection( + fxt.msg.conn_id_on_a.clone(), + )); conn_open_ack_validate(&fxt, Expect::Failure(Some(expected_err))); } #[test] fn conn_open_ack_invalid_consensus_height() { let fxt = conn_open_ack_fixture(Ctx::DefaultWithConnection); - let expected_err = ContextError::ConnectionError(ConnectionError::InvalidConsensusHeight { - target_height: fxt.msg.consensus_height_of_a_on_b, - current_height: Height::new(0, 10).unwrap(), - }); + let expected_err = + ContextError::ConnectionError(ConnectionError::InsufficientConsensusHeight { + target_height: fxt.msg.consensus_height_of_a_on_b, + current_height: Height::new(0, 10).unwrap(), + }); conn_open_ack_validate(&fxt, Expect::Failure(Some(expected_err))); } #[test] fn conn_open_ack_connection_mismatch() { let fxt = conn_open_ack_fixture(Ctx::NewWithConnectionEndOpen); - let expected_err = ContextError::ConnectionError(ConnectionError::InvalidState { + let expected_err = ContextError::ConnectionError(ConnectionError::MismatchedConnectionStates { expected: State::Init.to_string(), actual: State::Open.to_string(), });