From 3e4896d238177c6ef02f4fea7b79c49ac6c816bc Mon Sep 17 00:00:00 2001 From: Chris Smith <1979423+chris13524@users.noreply.github.com> Date: Wed, 21 Feb 2024 18:09:23 -0500 Subject: [PATCH] chore: refactor account ID (#371) * chore: refactor account ID * chore: prefer match * chore: improve understanding of panic --- src/model/types/account_id.rs | 276 ------------------ src/model/types/account_id/caip10.rs | 58 ++++ src/model/types/account_id/eip155.rs | 159 ++++++++++ src/model/types/account_id/erc55.rs | 42 +++ src/model/types/account_id/mod.rs | 106 +++++++ .../handlers/subscribe_topic.rs | 4 +- tests/integration.rs | 4 +- tests/utils/mod.rs | 2 +- 8 files changed, 370 insertions(+), 281 deletions(-) delete mode 100644 src/model/types/account_id.rs create mode 100644 src/model/types/account_id/caip10.rs create mode 100644 src/model/types/account_id/eip155.rs create mode 100644 src/model/types/account_id/erc55.rs create mode 100644 src/model/types/account_id/mod.rs diff --git a/src/model/types/account_id.rs b/src/model/types/account_id.rs deleted file mode 100644 index 6ab4f364..00000000 --- a/src/model/types/account_id.rs +++ /dev/null @@ -1,276 +0,0 @@ -use { - once_cell::sync::OnceCell, - relay_rpc::auth::did::{combine_did_data, extract_did_data, DidError}, - serde::{Deserialize, Serialize}, - sha2::Digest, - sha3::Keccak256, - std::sync::Arc, - thiserror::Error, -}; - -#[derive( - Debug, - Hash, - Clone, - PartialEq, - Eq, - ::derive_more::Display, - ::derive_more::From, - ::derive_more::AsRef, -)] -#[doc = "A CAIP-10 account ID."] -#[as_ref(forward)] -pub struct AccountId(Arc); - -impl AccountId { - pub fn value(&self) -> &Arc { - &self.0 - } - - pub fn into_value(self) -> Arc { - self.0 - } -} - -#[derive(Debug, PartialEq, Eq, Error)] -pub enum AccountIdError { - #[error("Account ID is is not a valid CAIP-10 account ID or uses an unsupported namespace")] - Namespace, - - #[error("Account ID appears to be eip155 but: {0}")] - Eip155(#[from] Eip155Error), -} - -impl TryFrom for AccountId { - type Error = AccountIdError; - - fn try_from(s: String) -> Result { - Self::try_from(s.as_ref()) - } -} - -impl TryFrom<&str> for AccountId { - type Error = AccountIdError; - - fn try_from(s: &str) -> Result { - if s.starts_with("eip155:") { - is_valid_eip155_account(s)?; - Ok(Self(Arc::from(s))) - } else { - Err(AccountIdError::Namespace) - } - } -} - -impl Serialize for AccountId { - fn serialize(&self, serializer: S) -> Result { - serializer.serialize_str(self.as_ref()) - } -} - -impl<'a> Deserialize<'a> for AccountId { - fn deserialize>(deserializer: D) -> Result { - let s = String::deserialize(deserializer)?; - Self::try_from(s).map_err(serde::de::Error::custom) - } -} - -#[derive(Debug, PartialEq, Eq, Error)] -pub enum Eip155Error { - #[error("does not match regex")] - Regex, - - #[error("does not pass ERC-55 checksum")] - Erc55, -} - -fn is_valid_eip155_account(account_id: &str) -> Result<(), Eip155Error> { - static PATTERN_CELL: OnceCell = OnceCell::new(); - let pattern = PATTERN_CELL - .get_or_init(|| regex::Regex::new(r"^eip155:\d+:0x([0-9a-fA-F]{40})$").unwrap()); - - if let Some(caps) = pattern.captures(account_id) { - let (_, [address]) = &caps.extract(); - let erc55 = erc_55_checksum_encode(&address.to_ascii_lowercase()).collect::(); - if &erc55 == address { - Ok(()) - } else { - Err(Eip155Error::Erc55) - } - } else { - Err(Eip155Error::Regex) - } -} - -#[derive(Debug, thiserror::Error)] -pub enum AccountIdParseError { - #[error(transparent)] - Caip10Error(#[from] AccountIdError), - - #[error("DID error: {0}")] - Did(#[from] DidError), -} - -const DID_METHOD_PKH: &str = "pkh"; - -impl AccountId { - pub fn from_did_pkh(did: &str) -> Result { - extract_did_data(did, DID_METHOD_PKH) - .map_err(AccountIdParseError::Did)? - .try_into() - .map_err(AccountIdParseError::Caip10Error) - } - - pub fn to_did_pkh(&self) -> String { - combine_did_data(DID_METHOD_PKH, self.as_ref()) - } -} - -// Encodes a lowercase hex address without '0x' with ERC-55 checksum -// If a non-lowercase hex value, or a non-address is passed, the behavior is undefined -pub fn erc_55_checksum_encode(s: &str) -> impl Iterator + '_ { - let address_hash = hex::encode(Keccak256::default().chain_update(s).finalize()); - s.chars().enumerate().map(move |(i, c)| { - if !c.is_numeric() && address_hash.as_bytes()[i] > b'7' { - c.to_ascii_uppercase() - } else { - c - } - }) -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn test_eip155() { - // https://github.com/ChainAgnostic/namespaces/blob/main/eip155/caip10.md#test-cases - - // Ethereum mainnet (valid/checksummed) - let test = "eip155:1:0x22227A31dd842196A246d8f3b775998560eAa61d"; - assert!(is_valid_eip155_account(test).is_ok()); - - // Ethereum mainnet (will not validate in EIP155-conformant systems) - let test = "eip155:1:0x22227a31dd842196a246d8f3b775998560eaa61d"; - assert!(is_valid_eip155_account(test).is_err()); - - // Polygon mainnet (valid/checksummed) - let test = "eip155:137:0x0495766cD136138Fc492Dd499B8DC87A92D6685b"; - assert!(is_valid_eip155_account(test).is_ok()); - - // Polygon mainnet (will not validate in EIP155-conformant systems) - let test = "eip155:137:0x0495766CD136138FC492DD499B8DC87A92D6685B"; - assert!(is_valid_eip155_account(test).is_err()); - - // Not EIP155 - let junk = "jkF53jF"; - assert!(is_valid_eip155_account(junk).is_err()); - } - - #[test] - fn test_erc_55() { - // https://eips.ethereum.org/EIPS/eip-55 - - fn test(addr: &str) { - let ox = "0x"; - assert_eq!( - addr, - ox.chars() - .chain(erc_55_checksum_encode( - &addr[ox.len()..].to_ascii_lowercase() - )) - .collect::() - ); - } - - test("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"); - test("0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359"); - test("0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB"); - test("0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb"); - } - - #[test] - fn to_did_pkh() { - let address = "eip155:1:0x1234567890123456789012345678901234567890"; - let account_id = AccountId::try_from(address).unwrap(); - assert_eq!(account_id.to_did_pkh(), format!("did:pkh:{address}")); - } - - #[test] - fn from_did_pkh() { - let address = "eip155:1:0x1234567890123456789012345678901234567890"; - let account_id = AccountId::from_did_pkh(&format!("did:pkh:{address}")).unwrap(); - assert_eq!(account_id.as_ref(), address); - } - - #[test] - fn test_is_valid_eip155_account() { - assert!( - is_valid_eip155_account("eip155:1:0x62639418051006514eD5Bb5B20aa7aAD642cC2d0").is_ok() - ); - assert!( - is_valid_eip155_account("eip155:2:0x62639418051006514eD5Bb5B20aa7aAD642cC2d0").is_ok() - ); - assert!( - is_valid_eip155_account("eip155:12:0x62639418051006514eD5Bb5B20aa7aAD642cC2d0").is_ok() - ); - assert!( - is_valid_eip155_account("eip155:1:0x62639418051006514eD5Bb5B20aa7aAD642cC2d").is_err() - ); - assert!( - is_valid_eip155_account("eip156:1:0x62639418051006514eD5Bb5B20aa7aAD642cC2d0").is_err() - ); - assert!( - is_valid_eip155_account("eip15:1:0x62639418051006514eD5Bb5B20aa7aAD642cC2d0").is_err() - ); - assert!(is_valid_eip155_account("0x62639418051006514eD5Bb5B20aa7aAD642cC2d0").is_err()); - assert!( - is_valid_eip155_account("eip155:12:62639418051006514eD5Bb5B20aa7aAD642cC2d0").is_err() - ); - assert!(is_valid_eip155_account("62639418051006514eD5Bb5B20aa7aAD642cC2d0").is_err()); - assert!( - is_valid_eip155_account("eip155:1:0x62639418051006514eD5Bb5B20aa7aAD642cC2d00") - .is_err() - ); - assert!( - is_valid_eip155_account("eeip155:1:0x62639418051006514eD5Bb5B20aa7aAD642cC2d0") - .is_err() - ); - } - - #[test] - fn account_id_valid_namespaces() { - assert!(AccountId::try_from("eip155:1:0x62639418051006514eD5Bb5B20aa7aAD642cC2d0").is_ok()); - assert!(AccountId::try_from("solana:xxx").is_err()); - } - - #[test] - fn account_id_eip155_regex_fail() { - assert_eq!( - AccountId::try_from("eip155:1:0x62639418051006514eD5Bb5B20aa7aAD642cC2d"), - Err(AccountIdError::Eip155(Eip155Error::Regex)) - ); - assert_eq!( - AccountId::try_from("eip155:1:0x62639418051006514eD5Bb5B20aa7aAD642cC2d00"), - Err(AccountIdError::Eip155(Eip155Error::Regex)) - ); - assert_eq!( - AccountId::try_from("eip155:1:"), - Err(AccountIdError::Eip155(Eip155Error::Regex)) - ); - assert_eq!( - AccountId::try_from("eip155:f:0x62639418051006514eD5Bb5B20aa7aAD642cC2d0"), - Err(AccountIdError::Eip155(Eip155Error::Regex)) - ); - } - - #[test] - fn account_id_eip155_requires_erc_55() { - assert!(AccountId::try_from("eip155:1:0x62639418051006514eD5Bb5B20aa7aAD642cC2d0").is_ok()); - assert_eq!( - AccountId::try_from("eip155:1:0x62639418051006514eD5Bb5B20aa7aAD642cC2D0"), - Err(AccountIdError::Eip155(Eip155Error::Erc55)) - ); - } -} diff --git a/src/model/types/account_id/caip10.rs b/src/model/types/account_id/caip10.rs new file mode 100644 index 00000000..a28a89c2 --- /dev/null +++ b/src/model/types/account_id/caip10.rs @@ -0,0 +1,58 @@ +use { + super::eip155::{validate_eip155, Eip155Error, NAMESPACE_EIP155}, + once_cell::sync::Lazy, + regex::Regex, + thiserror::Error, +}; + +#[derive(Debug, PartialEq, Eq, Error)] +pub enum Caip10Error { + #[error("Account ID is is not a valid CAIP-10 account ID")] + Invalid, + + #[error("Account ID uses an unsupported namespace")] + UnsupportedNamespace, + + #[error("Account ID is eip155 namespace but: {0}")] + Eip155(#[from] Eip155Error), +} + +// https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-10.md#syntax +static PATTERN: Lazy = Lazy::new(|| { + Regex::new(r"^([-a-z0-9]{3,8}):([-_a-zA-Z0-9]{1,32}):([-.%a-zA-Z0-9]{1,128})$") + .expect("Safe unwrap: panics should be caught by test cases") +}); + +pub fn validate_caip_10(s: &str) -> Result<(), Caip10Error> { + if let Some(caps) = PATTERN.captures(s) { + let (_, [namespace, reference, address]) = caps.extract(); + + match namespace { + NAMESPACE_EIP155 => validate_eip155(reference, address).map_err(Into::into), + _ => Err(Caip10Error::UnsupportedNamespace), + } + } else { + Err(Caip10Error::Invalid) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test() { + assert!(validate_caip_10("eip155:1:0x9AfEaC202C837df470b5A145e0EfD6a574B21029").is_ok()); + assert_eq!(validate_caip_10("eip155:111111111111111111111111111111111:0x9AfEaC202C837df470b5A145e0EfD6a574B21029"), Err(Caip10Error::Invalid)); + assert_eq!(validate_caip_10("junk"), Err(Caip10Error::Invalid)); + } + + #[test] + fn account_id_valid_namespaces() { + assert!(validate_caip_10("eip155:1:0x9AfEaC202C837df470b5A145e0EfD6a574B21029").is_ok()); + assert_eq!( + validate_caip_10("junk:1:1"), + Err(Caip10Error::UnsupportedNamespace) + ); + } +} diff --git a/src/model/types/account_id/eip155.rs b/src/model/types/account_id/eip155.rs new file mode 100644 index 00000000..03b2629b --- /dev/null +++ b/src/model/types/account_id/eip155.rs @@ -0,0 +1,159 @@ +use { + crate::model::types::erc55::erc_55_checksum_encode, once_cell::sync::Lazy, regex::Regex, + thiserror::Error, +}; + +// https://github.com/ChainAgnostic/namespaces/blob/main/eip155/caip10.md +pub const NAMESPACE_EIP155: &str = "eip155"; + +// https://github.com/ChainAgnostic/namespaces/blob/main/eip155/caip2.md#syntax +static REFERENCE_PATTERN: Lazy = + Lazy::new(|| Regex::new(r"^\d+$").expect("Safe unwrap: panics should be caught by test cases")); + +static ADDRESS_PATTERN: Lazy = Lazy::new(|| { + Regex::new(r"^0x([0-9a-fA-F]{40})$") + .expect("Safe unwrap: panics should be caught by test cases") +}); + +#[derive(Debug, PartialEq, Eq, Error)] +pub enum Eip155Error { + #[error("reference does not validate: {0}")] + Reference(#[from] Eip155ReferenceError), + + #[error("address does not validate: {0}")] + Address(#[from] Eip155AddressError), +} + +pub fn validate_eip155(reference: &str, address: &str) -> Result<(), Eip155Error> { + validate_eip155_reference(reference)?; + validate_eip155_address(address)?; + Ok(()) +} + +#[derive(Debug, PartialEq, Eq, Error)] +pub enum Eip155ReferenceError { + #[error("does not match regex")] + Regex, +} + +fn validate_eip155_reference(reference: &str) -> Result<(), Eip155ReferenceError> { + if REFERENCE_PATTERN.is_match(reference) { + Ok(()) + } else { + Err(Eip155ReferenceError::Regex) + } +} + +#[derive(Debug, PartialEq, Eq, Error)] +pub enum Eip155AddressError { + #[error("does not match regex")] + Regex, + + #[error("does not pass ERC-55 checksum")] + Erc55, +} + +fn validate_eip155_address(address: &str) -> Result<(), Eip155AddressError> { + if let Some(caps) = ADDRESS_PATTERN.captures(address) { + let (_, [address_hex]) = caps.extract(); + let erc55: String = + erc_55_checksum_encode(&address_hex.to_ascii_lowercase()).collect::(); + if erc55 == address_hex { + Ok(()) + } else { + Err(Eip155AddressError::Erc55) + } + } else { + Err(Eip155AddressError::Regex) + } +} + +#[cfg(test)] +mod tests { + use {super::*, crate::model::types::account_id::caip10::validate_caip_10}; + + #[test] + fn test_eip155() { + // https://github.com/ChainAgnostic/namespaces/blob/main/eip155/caip10.md#test-cases + + // Ethereum mainnet (valid/checksummed) + let test = "eip155:1:0x22227A31dd842196A246d8f3b775998560eAa61d"; + assert!(validate_caip_10(test).is_ok()); + + // Ethereum mainnet (will not validate in EIP155-conformant systems) + let test = "eip155:1:0x22227a31dd842196a246d8f3b775998560eaa61d"; + assert!(validate_caip_10(test).is_err()); + + // Polygon mainnet (valid/checksummed) + let test = "eip155:137:0x0495766cD136138Fc492Dd499B8DC87A92D6685b"; + assert!(validate_caip_10(test).is_ok()); + + // Polygon mainnet (will not validate in EIP155-conformant systems) + let test = "eip155:137:0x0495766CD136138FC492DD499B8DC87A92D6685B"; + assert!(validate_caip_10(test).is_err()); + } + + #[test] + fn validate_fn_uses_address_check() { + let address = "123"; + let reference = "1"; + assert_eq!( + validate_eip155(reference, address), + Err(Eip155Error::Address(Eip155AddressError::Regex)) + ); + } + + #[test] + fn validate_fn_uses_reference_check() { + let address = "0x62639418051006514eD5Bb5B20aa7aAD642cC2d0"; + let reference = "abc"; + assert_eq!( + validate_eip155(reference, address), + Err(Eip155Error::Reference(Eip155ReferenceError::Regex)) + ); + } + + #[test] + fn test_is_valid_eip155_account() { + assert!(validate_caip_10("eip155:1:0x62639418051006514eD5Bb5B20aa7aAD642cC2d0").is_ok()); + assert!(validate_caip_10("eip155:2:0x62639418051006514eD5Bb5B20aa7aAD642cC2d0").is_ok()); + assert!(validate_caip_10("eip155:12:0x62639418051006514eD5Bb5B20aa7aAD642cC2d0").is_ok()); + assert!(validate_caip_10("eip155:1:0x62639418051006514eD5Bb5B20aa7aAD642cC2d").is_err()); + assert!(validate_caip_10("eip156:1:0x62639418051006514eD5Bb5B20aa7aAD642cC2d0").is_err()); + assert!(validate_caip_10("eip15:1:0x62639418051006514eD5Bb5B20aa7aAD642cC2d0").is_err()); + assert!(validate_caip_10("0x62639418051006514eD5Bb5B20aa7aAD642cC2d0").is_err()); + assert!(validate_caip_10("eip155:12:62639418051006514eD5Bb5B20aa7aAD642cC2d0").is_err()); + assert!(validate_caip_10("62639418051006514eD5Bb5B20aa7aAD642cC2d0").is_err()); + assert!(validate_caip_10("eip155:1:0x62639418051006514eD5Bb5B20aa7aAD642cC2d00").is_err()); + assert!(validate_caip_10("eeip155:1:0x62639418051006514eD5Bb5B20aa7aAD642cC2d0").is_err()); + } + + #[test] + fn reference() { + assert_eq!( + validate_eip155_reference("f"), + Err(Eip155ReferenceError::Regex) + ); + } + + #[test] + fn address_regex_fail() { + assert_eq!( + validate_eip155_address("0x62639418051006514eD5Bb5B20aa7aAD642cC2d"), + Err(Eip155AddressError::Regex) + ); + assert_eq!( + validate_eip155_address("0x62639418051006514eD5Bb5B20aa7aAD642cC2d00"), + Err(Eip155AddressError::Regex) + ); + } + + #[test] + fn requires_erc_55() { + assert!(validate_eip155_address("0x62639418051006514eD5Bb5B20aa7aAD642cC2d0").is_ok()); + assert_eq!( + validate_eip155_address("0x62639418051006514eD5Bb5B20aa7aAD642cC2D0"), + Err(Eip155AddressError::Erc55) + ); + } +} diff --git a/src/model/types/account_id/erc55.rs b/src/model/types/account_id/erc55.rs new file mode 100644 index 00000000..d8574839 --- /dev/null +++ b/src/model/types/account_id/erc55.rs @@ -0,0 +1,42 @@ +use {sha2::Digest, sha3::Keccak256}; + +// https://eips.ethereum.org/EIPS/eip-55 + +// Encodes a lowercase hex address without '0x' with ERC-55 checksum +// If a non-lowercase hex value, or a non-address is passed, the behavior is undefined +pub fn erc_55_checksum_encode(s: &str) -> impl Iterator + '_ { + let address_hash = hex::encode(Keccak256::default().chain_update(s).finalize()); + s.chars().enumerate().map(move |(i, c)| { + if !c.is_numeric() && address_hash.as_bytes()[i] > b'7' { + c.to_ascii_uppercase() + } else { + c + } + }) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test() { + fn test(addr: &str) { + let ox = "0x"; + assert_eq!( + addr, + ox.chars() + .chain(erc_55_checksum_encode( + &addr[ox.len()..].to_ascii_lowercase() + )) + .collect::() + ); + } + + test("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"); + test("0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359"); + test("0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB"); + test("0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb"); + test("0x9AfEaC202C837df470b5A145e0EfD6a574B21029"); + } +} diff --git a/src/model/types/account_id/mod.rs b/src/model/types/account_id/mod.rs new file mode 100644 index 00000000..64c9adaa --- /dev/null +++ b/src/model/types/account_id/mod.rs @@ -0,0 +1,106 @@ +use { + self::caip10::{validate_caip_10, Caip10Error}, + relay_rpc::auth::did::{combine_did_data, extract_did_data, DidError}, + serde::{Deserialize, Serialize}, + std::sync::Arc, +}; + +mod caip10; +mod eip155; +pub mod erc55; + +#[derive( + Debug, + Hash, + Clone, + PartialEq, + Eq, + ::derive_more::Display, + ::derive_more::From, + ::derive_more::AsRef, +)] +#[doc = "A CAIP-10 account ID."] +#[as_ref(forward)] +pub struct AccountId(Arc); + +impl AccountId { + pub fn value(&self) -> &Arc { + &self.0 + } + + pub fn into_value(self) -> Arc { + self.0 + } +} + +impl TryFrom for AccountId { + type Error = Caip10Error; + + fn try_from(s: String) -> Result { + Self::try_from(s.as_ref()) + } +} + +impl TryFrom<&str> for AccountId { + type Error = Caip10Error; + + fn try_from(s: &str) -> Result { + validate_caip_10(s)?; + Ok(Self(Arc::from(s))) + } +} + +impl Serialize for AccountId { + fn serialize(&self, serializer: S) -> Result { + serializer.serialize_str(self.as_ref()) + } +} + +impl<'a> Deserialize<'a> for AccountId { + fn deserialize>(deserializer: D) -> Result { + let s = String::deserialize(deserializer)?; + Self::try_from(s).map_err(serde::de::Error::custom) + } +} +#[derive(Debug, thiserror::Error)] +pub enum AccountIdParseError { + #[error(transparent)] + Caip10Error(#[from] Caip10Error), + + #[error("DID error: {0}")] + Did(#[from] DidError), +} + +const DID_METHOD_PKH: &str = "pkh"; + +impl AccountId { + pub fn from_did_pkh(did: &str) -> Result { + extract_did_data(did, DID_METHOD_PKH) + .map_err(AccountIdParseError::Did)? + .try_into() + .map_err(AccountIdParseError::Caip10Error) + } + + pub fn to_did_pkh(&self) -> String { + combine_did_data(DID_METHOD_PKH, self.as_ref()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn to_did_pkh() { + let address = "eip155:1:0x9AfEaC202C837df470b5A145e0EfD6a574B21029"; + let account_id = AccountId::try_from(address).unwrap(); + assert_eq!(account_id.to_did_pkh(), format!("did:pkh:{address}")); + } + + #[test] + fn from_did_pkh() { + let address = "eip155:1:0x9AfEaC202C837df470b5A145e0EfD6a574B21029"; + let account_id = AccountId::from_did_pkh(&format!("did:pkh:{address}")).unwrap(); + assert_eq!(account_id.as_ref(), address); + } +} diff --git a/src/services/public_http_server/handlers/subscribe_topic.rs b/src/services/public_http_server/handlers/subscribe_topic.rs index df2d856d..b6602bfc 100644 --- a/src/services/public_http_server/handlers/subscribe_topic.rs +++ b/src/services/public_http_server/handlers/subscribe_topic.rs @@ -131,7 +131,9 @@ pub async fn subscribe_topic_rate_limit( } fn is_domain(domain: &str) -> bool { - static DOMAIN_REGEX: Lazy = Lazy::new(|| Regex::new(r"^[a-z0-9-_\.]+$").unwrap()); + static DOMAIN_REGEX: Lazy = Lazy::new(|| { + Regex::new(r"^[a-z0-9-_\.]+$").expect("Safe unwrap: panics should be caught by test cases") + }); DOMAIN_REGEX.is_match(domain) } diff --git a/tests/integration.rs b/tests/integration.rs index 01e28be9..a111b400 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -2292,9 +2292,7 @@ async fn test_notify_invalid_account(notify_server: &NotifyServerContext) { assert_eq!(response.status(), StatusCode::UNPROCESSABLE_ENTITY); let response = response.text().await.unwrap(); assert!(response.contains("Failed to deserialize the JSON body into the target type")); - assert!(response.contains( - "Account ID is is not a valid CAIP-10 account ID or uses an unsupported namespace" - )); + assert!(response.contains("Account ID is is not a valid CAIP-10 account ID")); } #[test_context(NotifyServerContext)] diff --git a/tests/utils/mod.rs b/tests/utils/mod.rs index 64cf6e55..fb268283 100644 --- a/tests/utils/mod.rs +++ b/tests/utils/mod.rs @@ -5,7 +5,7 @@ use { notify_server::{ auth::{AuthError, DidWeb, GetSharedClaims, SharedClaims}, error::NotifyServerError, - model::types::{erc_55_checksum_encode, AccountId}, + model::types::{erc55::erc_55_checksum_encode, AccountId}, notify_message::NotifyMessage, relay_client_helpers::create_http_client, },