diff --git a/.changelog/unreleased/bug-fixes/ibc-relayer/4247-fix-chain-name-extraction.md b/.changelog/unreleased/bug-fixes/ibc-relayer/4247-fix-chain-name-extraction.md new file mode 100644 index 0000000000..6f538ac1d9 --- /dev/null +++ b/.changelog/unreleased/bug-fixes/ibc-relayer/4247-fix-chain-name-extraction.md @@ -0,0 +1,3 @@ +- Fix the extraction of the chain name from the chain ID to ensure + accurate identification. + ([\#4247](https://github.com/informalsystems/hermes/issues/4247)) \ No newline at end of file diff --git a/crates/relayer-types/src/core/ics24_host/identifier.rs b/crates/relayer-types/src/core/ics24_host/identifier.rs index 8c472d8947..dbe60ea337 100644 --- a/crates/relayer-types/src/core/ics24_host/identifier.rs +++ b/crates/relayer-types/src/core/ics24_host/identifier.rs @@ -68,8 +68,20 @@ impl ChainId { /// Extract the chain name from this chain identifier. The chain name /// consists of the first part of the identifier, before the dash. - pub fn name(&self) -> String { - self.id.split('-').take(1).collect::>().join("") + /// If the value following the dash is not an integer, or if there are no + /// dashes, the entire chain ID will be returned. + pub fn name(&self) -> &str { + self.id + .rsplit_once('-') + .and_then(|(chain_name, rev_number_str)| { + // Parses the revision number string into a `u64` and checks its validity. + if rev_number_str.parse::().is_ok() { + Some(chain_name) + } else { + None + } + }) + .unwrap_or_else(|| &self.id) } /// Extract the version from the given chain identifier. @@ -442,3 +454,82 @@ impl Display for PortChannelId { write!(f, "{}/{}", self.port_id, self.channel_id) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_chain_id_name() { + let chain_id = ChainId::from_str("ibc-5").unwrap(); + assert_eq!(chain_id.name(), "ibc".to_owned()); + } + + #[test] + fn test_chain_id_name_with_test() { + let chain_id = ChainId::from_str("ibc-test-5").unwrap(); + assert_eq!(chain_id.name(), "ibc-test".to_owned()); + } + + #[test] + fn test_chain_id_name_no_dash() { + let chain_id = ChainId::from_str("ibc5").unwrap(); + assert_eq!(chain_id.name(), "ibc5".to_owned()); + } + + #[test] + fn standard_chain_names() { + let test_cases = [ + ("ibc-0", "ibc"), + ("osmosis-1", "osmosis"), + ("cosmoshub-4", "cosmoshub"), + ("juno-mainnet-1", "juno-mainnet"), + ("akash-testnet-2", "akash-testnet"), + ("stargaze-123", "stargaze"), + ("crypto-org-chain-0", "crypto-org-chain"), + ("mars-hub-1", "mars-hub"), + ("evmos-9000-1", "evmos-9000"), + ]; + + for (input, expected) in test_cases { + let chain_id = ChainId::from_str(input).unwrap(); + assert_eq!(chain_id.name(), expected); + } + } + + #[test] + fn missing_or_invalid_revision_numbers() { + let test_cases = [ + ("osmosis", "osmosis"), + ("ibc-test", "ibc-test"), + ("cosmos-hub", "cosmos-hub"), + ("juno-", "juno-"), + ("akash-testnet-x", "akash-testnet-x"), + ("stargaze-abc", "stargaze-abc"), + ("chain-123-xyz", "chain-123-xyz"), + ]; + + for (input, expected) in test_cases { + let chain_id = ChainId::from_str(input).unwrap(); + assert_eq!(chain_id.name(), expected); + } + } + + #[test] + fn edge_cases() { + let test_cases = [ + ("", ""), + ("-", "-"), + ("chain-", "chain-"), + ( + "chain-9999999999999999999999", + "chain-9999999999999999999999", + ), // number too large for u64 + ]; + + for (input, expected) in test_cases { + let chain_id = ChainId::from_str(input).unwrap(); + assert_eq!(chain_id.name(), expected); + } + } +}