From ced74a754669ebf698decdae33ceeb2cc9a0839b Mon Sep 17 00:00:00 2001 From: "Tony Arcieri (iqlusion)" Date: Fri, 31 May 2024 12:19:46 -0600 Subject: [PATCH] cosmrs: match upstream Cosmos SDK's Denom validation logic (#470) Implements the same checks as the upstream Cosmos SDK's `MatchDenom`: https://github.com/cosmos/cosmos-sdk/blob/6a07568/types/coin.go#L885-L906 --- cosmrs/src/base/denom.rs | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/cosmrs/src/base/denom.rs b/cosmrs/src/base/denom.rs index bc4415f1..a5c13dc5 100644 --- a/cosmrs/src/base/denom.rs +++ b/cosmrs/src/base/denom.rs @@ -6,6 +6,14 @@ use std::{fmt, str::FromStr}; #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub struct Denom(String); +impl Denom { + /// Minimum length of a [`Denom`]. + pub const MIN_LENGTH: usize = 3; + + /// Maximum length of a [`Denom`]. + pub const MAX_LENGTH: usize = 128; +} + impl AsRef for Denom { fn as_ref(&self) -> &str { self.0.as_ref() @@ -21,15 +29,18 @@ impl fmt::Display for Denom { impl FromStr for Denom { type Err = ErrorReport; + /// NOTE: implements the same checks as the `MatchDenom` function from the upstream Cosmos SDK. + /// fn from_str(s: &str) -> Result { - // TODO(tarcieri): ensure this is the proper validation for a denom name - if s.chars() - .all(|c| matches!(c, 'A'..='Z' | 'a'..='z' | '0'..='9' | '/' | ':' | '.' | '_' | '-')) - { - Ok(Denom(s.to_owned())) - } else { - Err(Error::Denom { name: s.to_owned() }.into()) + if s.len() < Self::MIN_LENGTH || s.len() > Self::MAX_LENGTH { + return Err(Error::Denom { name: s.to_owned() }.into()); + } + + if !s.chars().all(is_valid_denom_char) { + return Err(Error::Denom { name: s.to_owned() }.into()); } + + Ok(Denom(s.to_owned())) } } @@ -47,6 +58,15 @@ impl Serialize for Denom { } } +/// Check if a given character is allowed in a `Denom` name. +/// +/// NOTE: implements the same checks as the `isValidRune` function from the upstream Cosmos SDK. +/// +#[inline] +fn is_valid_denom_char(c: char) -> bool { + matches!(c, 'A'..='Z' | 'a'..='z' | '0'..='9' | '/' | ':' | '.' | '_' | '-') +} + #[cfg(test)] mod tests { use super::Denom;