From 8dd22e640b58a4ae29668f99574f4b3d6db7d238 Mon Sep 17 00:00:00 2001 From: Stan Bondi Date: Thu, 5 Sep 2024 16:12:18 +0400 Subject: [PATCH] ensure that substate_id to_string and from_str impls are correct (test) --- dan_layer/engine_types/src/substate.rs | 37 +++++++++------ .../template_lib/src/models/non_fungible.rs | 46 +++++++++++-------- .../src/models/non_fungible_index.rs | 28 +++++++++-- .../src/resource/builder/confidential.rs | 4 +- .../src/resource/builder/fungible.rs | 4 +- .../src/resource/builder/non_fungible.rs | 4 +- 6 files changed, 82 insertions(+), 41 deletions(-) diff --git a/dan_layer/engine_types/src/substate.rs b/dan_layer/engine_types/src/substate.rs index 4d565bde4..ec1bfb7ba 100644 --- a/dan_layer/engine_types/src/substate.rs +++ b/dan_layer/engine_types/src/substate.rs @@ -362,17 +362,9 @@ impl FromStr for SubstateId { }, Some(("nftindex", rest)) => { // nftindex_{resource_id}_{index} - let (resource, idx) = rest - .split_once('_') - .ok_or_else(|| InvalidSubstateIdFormat(s.to_string()))?; - let resource_addr = - ResourceAddress::from_hex(resource).map_err(|_| InvalidSubstateIdFormat(s.to_string()))?; - let index = u64::from_str(idx).map_err(|_| InvalidSubstateIdFormat(s.to_string()))?; - - Ok(SubstateId::NonFungibleIndex(NonFungibleIndexAddress::new( - resource_addr, - index, - ))) + let addr = + NonFungibleIndexAddress::from_str(rest).map_err(|_| InvalidSubstateIdFormat(s.to_string()))?; + Ok(SubstateId::NonFungibleIndex(addr)) }, Some(("vault", addr)) => { let id = VaultId::from_hex(addr).map_err(|_| InvalidSubstateIdFormat(s.to_string()))?; @@ -723,11 +715,11 @@ impl SubstateDiff { mod tests { use super::*; - mod substate_address_parse { + mod substate_id_parse { use super::*; #[test] - fn it_parses_valid_substate_addresses() { + fn it_parses_valid_substate_ids() { SubstateId::from_str("component_7cbfe29101c24924b1b6ccefbfff98986d648622272ae24f7585dab5ffffffff") .unwrap() .as_component_address() @@ -755,5 +747,24 @@ mod tests { .as_non_fungible_index_address() .unwrap(); } + + #[test] + fn it_parses_a_display_string() { + fn check(s: &str) { + let id = SubstateId::from_str(s).unwrap(); + assert_eq!(id.to_string(), s); + } + check("component_7cbfe29101c24924b1b6ccefbfff98986d648622272ae24f7585dab5ffffffff"); + check("vault_7cbfe29101c24924b1b6ccefbfff98986d648622272ae24f7585dab5ffffffff"); + check("resource_7cbfe29101c24924b1b6ccefbfff98986d648622272ae24f7585dab5ffffffff"); + check("nft_7cbfe29101c24924b1b6ccefbfff98986d648622272ae24f7585dab5ffffffff_str_SpecialNft"); + check( + "nft_a7cf4fd18ada7f367b1c102a9c158abc3754491665033231c5eb907fffffffff_uuid_7f19c3fe5fa13ff66a0d379fe5f9e3508acbd338db6bedd7350d8d565b2c5d32", + ); + check("nftindex_7cbfe29101c24924b1b6ccefbfff98986d648622272ae24f7585dab5ffffffff_0"); + check("feeclaim_7cbfe29101c24924b1b6ccefbfff98986d648622272ae24f7585dab5ffffffff"); + check("txreceipt_7cbfe29101c24924b1b6ccefbfff98986d648622272ae24f7585dab5ffffffff"); + check("commitment_7cbfe29101c24924b1b6ccefbfff98986d648622272ae24f7585dab5ffffffff"); + } } } diff --git a/dan_layer/template_lib/src/models/non_fungible.rs b/dan_layer/template_lib/src/models/non_fungible.rs index 030312736..962c0305b 100644 --- a/dan_layer/template_lib/src/models/non_fungible.rs +++ b/dan_layer/template_lib/src/models/non_fungible.rs @@ -20,8 +20,6 @@ use crate::{ Hash, }; -const DELIM: char = ':'; - /// The unique identification of a non-fungible token inside it's parent resource #[serde_as] #[derive(Debug, Clone, Ord, PartialOrd, PartialEq, Eq, Serialize, Deserialize, Hash)] @@ -74,7 +72,7 @@ impl NonFungibleId { let type_name = self.type_name(); let mut s = String::with_capacity(type_name.len() + 1 + self.str_repr_len()); s.push_str(self.type_name()); - s.push(DELIM); + s.push('_'); match self { NonFungibleId::U256(uuid) => { @@ -193,10 +191,10 @@ fn validate_nft_id_str(s: &str) -> Result<(), ParseNonFungibleIdError> { impl Display for NonFungibleId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - NonFungibleId::U256(v) => write!(f, "uuid:{}", Hash::from(*v)), - NonFungibleId::String(s) => write!(f, "str:{}", s), - NonFungibleId::Uint32(v) => write!(f, "u32:{}", v), - NonFungibleId::Uint64(v) => write!(f, "u64:{}", v), + NonFungibleId::U256(v) => write!(f, "uuid_{}", Hash::from(*v)), + NonFungibleId::String(s) => write!(f, "str_{}", s), + NonFungibleId::Uint32(v) => write!(f, "u32_{}", v), + NonFungibleId::Uint64(v) => write!(f, "u64_{}", v), } } } @@ -287,7 +285,11 @@ impl From for NonFungibleAddress { impl Display for NonFungibleAddress { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{} nft_{}", self.0.resource_address, self.0.id) + write!(f, "nft_")?; + for byte in self.resource_address().as_bytes() { + write!(f, "{:02x}", byte)?; + } + write!(f, "_{}", self.id()) } } @@ -437,21 +439,21 @@ mod tests { #[test] fn it_generates_correct_canonical_string() { // u32 - assert_eq!(NonFungibleId::from_u32(0).to_canonical_string(), "u32:0"); - assert_eq!(NonFungibleId::from_u32(100000).to_canonical_string(), "u32:100000"); + assert_eq!(NonFungibleId::from_u32(0).to_canonical_string(), "u32_0"); + assert_eq!(NonFungibleId::from_u32(100000).to_canonical_string(), "u32_100000"); assert_eq!( NonFungibleId::from_u32(u32::MAX).to_canonical_string(), - format!("u32:{}", u32::MAX) + format!("u32_{}", u32::MAX) ); // u64 - assert_eq!(NonFungibleId::from_u64(0).to_canonical_string(), "u64:0"); - assert_eq!(NonFungibleId::from_u64(1).to_canonical_string(), "u64:1"); - assert_eq!(NonFungibleId::from_u64(10).to_canonical_string(), "u64:10"); - assert_eq!(NonFungibleId::from_u64(100).to_canonical_string(), "u64:100"); + assert_eq!(NonFungibleId::from_u64(0).to_canonical_string(), "u64_0"); + assert_eq!(NonFungibleId::from_u64(1).to_canonical_string(), "u64_1"); + assert_eq!(NonFungibleId::from_u64(10).to_canonical_string(), "u64_10"); + assert_eq!(NonFungibleId::from_u64(100).to_canonical_string(), "u64_100"); assert_eq!( NonFungibleId::from_u64(u64::MAX).to_canonical_string(), - format!("u64:{}", u64::MAX) + format!("u64_{}", u64::MAX) ); // uuid @@ -462,7 +464,7 @@ mod tests { .into_array() ) .to_canonical_string(), - "uuid:736bab0c3af393a0423c578ddcf7e19b81086f6ecbbc148713e95da75ef8171d" + "uuid_736bab0c3af393a0423c578ddcf7e19b81086f6ecbbc148713e95da75ef8171d" ); // string @@ -470,9 +472,17 @@ mod tests { NonFungibleId::try_from_string("hello_world") .unwrap() .to_canonical_string(), - "str:hello_world" + "str_hello_world" ); } + + #[test] + fn it_parses_a_display_string() { + let id = NonFungibleId::from_u32(123); + let s = id.to_string(); + let id2 = NonFungibleId::try_from_canonical_string(&s).unwrap(); + assert_eq!(id, id2); + } } mod serde_deser { diff --git a/dan_layer/template_lib/src/models/non_fungible_index.rs b/dan_layer/template_lib/src/models/non_fungible_index.rs index 32c6d6831..64b9aee91 100644 --- a/dan_layer/template_lib/src/models/non_fungible_index.rs +++ b/dan_layer/template_lib/src/models/non_fungible_index.rs @@ -57,7 +57,11 @@ impl NonFungibleIndexAddress { impl Display for NonFungibleIndexAddress { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{} index_{}", self.resource_address, self.index) + write!(f, "nftindex_")?; + for byte in self.resource_address.as_bytes() { + write!(f, "{:02x}", byte)?; + } + write!(f, "_{}", self.index) } } @@ -65,11 +69,11 @@ impl FromStr for NonFungibleIndexAddress { type Err = NonFungibleIndexAddressParseError; fn from_str(s: &str) -> Result { - let (resource, index) = s.split_once(" index_").ok_or(NonFungibleIndexAddressParseError)?; - + // nftindex_{resource_id}_{index} + let s = s.strip_prefix("nftindex_").unwrap_or(s); + let (resource, index) = s.split_once('_').ok_or(NonFungibleIndexAddressParseError)?; let resource_address = resource.parse().map_err(|_| NonFungibleIndexAddressParseError)?; let index = index.parse().map_err(|_| NonFungibleIndexAddressParseError)?; - Ok(Self::new(resource_address, index)) } } @@ -84,3 +88,19 @@ impl Display for NonFungibleIndexAddressParseError { write!(f, "Invalid non-fungible index address string") } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_parses_a_display_string() { + let address = NonFungibleIndexAddress::new( + ResourceAddress::from_hex("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaA").unwrap(), + 123, + ); + let display = address.to_string(); + let parsed = NonFungibleIndexAddress::from_str(&display).unwrap(); + assert_eq!(address, parsed); + } +} diff --git a/dan_layer/template_lib/src/resource/builder/confidential.rs b/dan_layer/template_lib/src/resource/builder/confidential.rs index 8227615fd..0a4f554c2 100644 --- a/dan_layer/template_lib/src/resource/builder/confidential.rs +++ b/dan_layer/template_lib/src/resource/builder/confidential.rs @@ -118,7 +118,7 @@ impl ConfidentialResourceBuilder { /// ## Examples /// /// Building a resource with a hook from within a component - /// ```rust + /// ```ignore /// # use tari_template_lib::{caller_context::CallerContext, prelude::ResourceBuilder}; /// ResourceBuilder::confidential() /// .with_authorization_hook(CallerContext::current_component_address(), "my_hook") @@ -127,7 +127,7 @@ impl ConfidentialResourceBuilder { /// /// Building a resource with a hook in a static template function. The address is allocated beforehand. /// - /// ```rust + /// ```ignore /// # use tari_template_lib::{caller_context::CallerContext, prelude::ResourceBuilder}; /// let alloc = CallerContext::allocate_component_address(); /// ResourceBuilder::confidential() diff --git a/dan_layer/template_lib/src/resource/builder/fungible.rs b/dan_layer/template_lib/src/resource/builder/fungible.rs index 5691748e5..a06388755 100644 --- a/dan_layer/template_lib/src/resource/builder/fungible.rs +++ b/dan_layer/template_lib/src/resource/builder/fungible.rs @@ -106,7 +106,7 @@ impl FungibleResourceBuilder { /// ## Examples /// /// Building a resource with a hook from within a component - /// ```rust + /// ```ignore /// use tari_template_lib::{caller_context::CallerContext, prelude::ResourceBuilder}; /// ResourceBuilder::fungible() /// .with_authorization_hook(CallerContext::current_component_address(), "my_hook") @@ -115,7 +115,7 @@ impl FungibleResourceBuilder { /// /// Building a resource with a hook in a static template function. The address is allocated beforehand. /// - /// ```rust + /// ```ignore /// use tari_template_lib::{caller_context::CallerContext, prelude::ResourceBuilder}; /// let alloc = CallerContext::allocate_component_address(); /// ResourceBuilder::fungible() diff --git a/dan_layer/template_lib/src/resource/builder/non_fungible.rs b/dan_layer/template_lib/src/resource/builder/non_fungible.rs index f601bc42c..8815a0a6b 100644 --- a/dan_layer/template_lib/src/resource/builder/non_fungible.rs +++ b/dan_layer/template_lib/src/resource/builder/non_fungible.rs @@ -115,7 +115,7 @@ impl NonFungibleResourceBuilder { /// ## Examples /// /// Building a resource with a hook from within a component - /// ```rust + /// ```ignore /// use tari_template_lib::{caller_context::CallerContext, prelude::ResourceBuilder}; /// ResourceBuilder::non_fungible() /// .with_authorization_hook(CallerContext::current_component_address(), "my_hook") @@ -124,7 +124,7 @@ impl NonFungibleResourceBuilder { /// /// Building a resource with a hook in a static template function. The address is allocated beforehand. /// - /// ```rust + /// ```ignore /// use tari_template_lib::{caller_context::CallerContext, prelude::ResourceBuilder}; /// let alloc = CallerContext::allocate_component_address(); /// ResourceBuilder::non_fungible()