Skip to content

Commit

Permalink
fix(dan)!: update nft address format (#1129)
Browse files Browse the repository at this point in the history
Description
---
Resolves #1080

Motivation and Context
---
Updates the format of the NFT address by removing the blank space
between the `resource` and `nft` components

Breaking Changes
---
NFT address format updated

- [ ] None
- [ ] Requires data directory to be deleted
- [X] Other - Please specify

---------

Co-authored-by: Stan Bondi <sdbondi@users.noreply.github.com>
  • Loading branch information
therealdannzor and sdbondi committed Sep 5, 2024
1 parent 0ca3504 commit 2b5ba47
Show file tree
Hide file tree
Showing 14 changed files with 148 additions and 97 deletions.
2 changes: 1 addition & 1 deletion .license.ignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
./applications/tari_indexer/src/substate_storage_sqlite/schema.rs
./networking/sqlite_message_logger/src/schema.rs
./dan_layer/storage_sqlite/src/global/schema.rs
./dan_layer/state_store_sqlite/src/schema.rs
./dan_layer/wallet/storage_sqlite/src/schema.rs
./networking/sqlite_message_logger/src/schema.rs
1 change: 1 addition & 0 deletions bindings/dist/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export * from "./types/SubstateDiff";
export * from "./types/SubstateId";
export * from "./types/SubstateLockType";
export * from "./types/SubstateRecord";
export * from "./types/SubstateRequirementLockIntent";
export * from "./types/SubstateRequirement";
export * from "./types/Substate";
export * from "./types/SubstateType";
Expand Down
1 change: 1 addition & 0 deletions bindings/dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export * from "./types/SubstateDiff";
export * from "./types/SubstateId";
export * from "./types/SubstateLockType";
export * from "./types/SubstateRecord";
export * from "./types/SubstateRequirementLockIntent";
export * from "./types/SubstateRequirement";
export * from "./types/Substate";
export * from "./types/SubstateType";
Expand Down
7 changes: 7 additions & 0 deletions bindings/dist/types/SubstateRequirementLockIntent.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { SubstateLockType } from "./SubstateLockType";
import type { SubstateRequirement } from "./SubstateRequirement";
export interface SubstateRequirementLockIntent {
substate_requirement: SubstateRequirement;
version_to_lock: number;
lock_type: SubstateLockType;
}
1 change: 1 addition & 0 deletions bindings/dist/types/SubstateRequirementLockIntent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {};
1 change: 1 addition & 0 deletions bindings/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export * from "./types/SubstateDiff";
export * from "./types/SubstateId";
export * from "./types/SubstateLockType";
export * from "./types/SubstateRecord";
export * from "./types/SubstateRequirementLockIntent";
export * from "./types/SubstateRequirement";
export * from "./types/Substate";
export * from "./types/SubstateType";
Expand Down
9 changes: 9 additions & 0 deletions bindings/src/types/SubstateRequirementLockIntent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { SubstateLockType } from "./SubstateLockType";
import type { SubstateRequirement } from "./SubstateRequirement";

export interface SubstateRequirementLockIntent {
substate_requirement: SubstateRequirement;
version_to_lock: number;
lock_type: SubstateLockType;
}
8 changes: 4 additions & 4 deletions dan_layer/engine/tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -759,16 +759,16 @@ mod basic_nft {
.collect::<Vec<_>>();
assert_eq!(
nfts.iter()
.filter(|n| n.to_canonical_string() == "str:SpecialNft")
.filter(|n| n.to_canonical_string() == "str_SpecialNft")
.count(),
1
);
assert_eq!(nfts.iter().filter(|n| n.to_canonical_string() == "u32:123").count(), 1);
assert_eq!(nfts.iter().filter(|n| n.to_canonical_string() == "u64:456").count(), 1);
assert_eq!(nfts.iter().filter(|n| n.to_canonical_string() == "u32_123").count(), 1);
assert_eq!(nfts.iter().filter(|n| n.to_canonical_string() == "u64_456").count(), 1);
assert_eq!(
nfts.iter()
.filter(|n| n.to_canonical_string() ==
"uuid:746869732077696c6c20626520696e7465727072657465642061732075756964")
"uuid_746869732077696c6c20626520696e7465727072657465642061732075756964")
.count(),
1
);
Expand Down
79 changes: 41 additions & 38 deletions dan_layer/engine_types/src/substate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -355,33 +355,20 @@ impl FromStr for SubstateId {
Ok(SubstateId::Component(addr))
},
Some(("resource", addr)) => {
match addr.split_once(' ') {
Some((resource_str, addr)) => match addr.split_once('_') {
// resource_xxxx nft_xxxxx
Some(("nft", _)) => {
let nft_address =
NonFungibleAddress::from_str(s).map_err(|e| InvalidSubstateIdFormat(e.to_string()))?;
Ok(SubstateId::NonFungible(nft_address))
},
// resource_xxxx index_
Some(("index", index_str)) => {
let resource_addr = ResourceAddress::from_hex(resource_str)
.map_err(|_| InvalidSubstateIdFormat(s.to_string()))?;
let index = u64::from_str(index_str).map_err(|_| InvalidSubstateIdFormat(s.to_string()))?;
Ok(SubstateId::NonFungibleIndex(NonFungibleIndexAddress::new(
resource_addr,
index,
)))
},
_ => Err(InvalidSubstateIdFormat(s.to_string())),
},
// resource_xxxx
None => {
let addr =
ResourceAddress::from_hex(addr).map_err(|_| InvalidSubstateIdFormat(s.to_string()))?;
Ok(SubstateId::Resource(addr))
},
}
// resource_xxxxx
let addr = ResourceAddress::from_hex(addr).map_err(|_| InvalidSubstateIdFormat(s.to_string()))?;
Ok(SubstateId::Resource(addr))
},
Some(("nft", rest)) => {
// nft_{resource_hex}_{id_type}_{id}
let addr = NonFungibleAddress::from_str(rest).map_err(|_| InvalidSubstateIdFormat(s.to_string()))?;
Ok(SubstateId::NonFungible(addr))
},
Some(("nftindex", rest)) => {
// nftindex_{resource_id}_{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()))?;
Expand Down Expand Up @@ -732,11 +719,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()
Expand All @@ -749,23 +736,39 @@ mod tests {
.unwrap()
.as_resource_address()
.unwrap();
SubstateId::from_str("nft_7cbfe29101c24924b1b6ccefbfff98986d648622272ae24f7585dab5ffffffff_str_SpecialNft")
.unwrap()
.as_non_fungible_address()
.unwrap();
SubstateId::from_str(
"resource_7cbfe29101c24924b1b6ccefbfff98986d648622272ae24f7585dab5ffffffff nft_str:SpecialNft",
)
.unwrap()
.as_non_fungible_address()
.unwrap();
SubstateId::from_str(
"resource_a7cf4fd18ada7f367b1c102a9c158abc3754491665033231c5eb907fffffffff \
nft_uuid:7f19c3fe5fa13ff66a0d379fe5f9e3508acbd338db6bedd7350d8d565b2c5d32",
"nft_a7cf4fd18ada7f367b1c102a9c158abc3754491665033231c5eb907fffffffff_uuid_7f19c3fe5fa13ff66a0d379fe5f9e3508acbd338db6bedd7350d8d565b2c5d32",
)
.unwrap()
.as_non_fungible_address()
.unwrap();
SubstateId::from_str("resource_7cbfe29101c24924b1b6ccefbfff98986d648622272ae24f7585dab5ffffffff index_0")
SubstateId::from_str("nftindex_7cbfe29101c24924b1b6ccefbfff98986d648622272ae24f7585dab5ffffffff_0")
.unwrap()
.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");
}
}
}
96 changes: 52 additions & 44 deletions dan_layer/template_lib/src/models/non_fungible.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -66,15 +64,15 @@ impl NonFungibleId {
}

/// A string in one of the following formats
/// - uuid:736bab0c3af393a0423c578ddcf7e19b81086f6ecbbc148713e95da75ef8171d
/// - str:my_special_nft_name
/// - u32:1234
/// - u64:1234
/// - uuid_736bab0c3af393a0423c578ddcf7e19b81086f6ecbbc148713e95da75ef8171d
/// - str_my_special_nft_name
/// - u32_1234
/// - u64_1234
pub fn to_canonical_string(&self) -> String {
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) => {
Expand Down Expand Up @@ -133,7 +131,7 @@ impl NonFungibleId {
}

pub fn try_from_canonical_string(s: &str) -> Result<Self, ParseNonFungibleIdError> {
let (id_type, id) = s.split_once(':').ok_or(ParseNonFungibleIdError::InvalidFormat)?;
let (id_type, id) = s.split_once('_').ok_or(ParseNonFungibleIdError::InvalidFormat)?;
match id_type {
"uuid" => Ok(NonFungibleId::U256(
Hash::from_hex(id)
Expand Down Expand Up @@ -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),
}
}
}
Expand Down Expand Up @@ -263,20 +261,19 @@ impl FromStr for NonFungibleAddress {
type Err = ParseNonFungibleAddressError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
// the expected format is "resource_xxxx nft_xxxxx"
match s.split_once(' ') {
Some((resource_str, addr_str)) => match addr_str.split_once('_') {
Some(("nft", id_str)) => {
let resource_addr = ResourceAddress::from_str(resource_str)
.map_err(|e| ParseNonFungibleAddressError::InvalidResource(e.to_string()))?;
let id = NonFungibleId::try_from_canonical_string(id_str)
.map_err(ParseNonFungibleAddressError::InvalidId)?;
Ok(NonFungibleAddress::new(resource_addr, id))
},
_ => Err(ParseNonFungibleAddressError::InvalidFormat),
},
None => Err(ParseNonFungibleAddressError::InvalidFormat),
}
// nft_{resource_hex}_{type}_{id}

let rest = s.strip_prefix("nft_").unwrap_or(s);
let (resource, nft_rest) = rest
.split_once('_')
.ok_or(ParseNonFungibleAddressError::InvalidFormat)?;

let resource_addr =
ResourceAddress::from_hex(resource).map_err(|_| ParseNonFungibleAddressError::InvalidFormat)?;
let nft_id = NonFungibleId::try_from_canonical_string(nft_rest)
.map_err(|_| ParseNonFungibleAddressError::InvalidFormat)?;

Ok(NonFungibleAddress::new(resource_addr, nft_id))
}
}

Expand All @@ -288,7 +285,11 @@ impl From<NonFungibleAddressContents> 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())
}
}

Expand Down Expand Up @@ -438,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
Expand All @@ -463,17 +464,25 @@ mod tests {
.into_array()
)
.to_canonical_string(),
"uuid:736bab0c3af393a0423c578ddcf7e19b81086f6ecbbc148713e95da75ef8171d"
"uuid_736bab0c3af393a0423c578ddcf7e19b81086f6ecbbc148713e95da75ef8171d"
);

// string
assert_eq!(
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 {
Expand Down Expand Up @@ -510,33 +519,32 @@ mod tests {
#[test]
fn it_parses_valid_strings() {
NonFungibleAddress::from_str(
"resource_7cbfe29101c24924b1b6ccefbfff98986d648622272ae24f7585dab5ffffffff nft_str:SpecialNft",
"nft_7cbfe29101c24924b1b6ccefbfff98986d648622272ae24f7585dab5ffffffff_str_SpecialNft",
)
.unwrap();
NonFungibleAddress::from_str(
"resource_a7cf4fd18ada7f367b1c102a9c158abc3754491665033231c5eb907fffffffff \
nft_uuid:7f19c3fe5fa13ff66a0d379fe5f9e3508acbd338db6bedd7350d8d565b2c5d32",
"nft_a7cf4fd18ada7f367b1c102a9c158abc3754491665033231c5eb907fffffffff_uuid_7f19c3fe5fa13ff66a0d379fe5f9e3508acbd338db6bedd7350d8d565b2c5d32",
)
.unwrap();
}

#[test]
fn it_rejects_invalid_strings() {
NonFungibleAddress::from_str(
"resource_7cbfe29101c24924b1b6ccefbfff98986d648622272ae24f7585dab5ffffffff nft_xxxxx:SpecialNft",
"resource_7cbfe29101c24924b1b6ccefbfff98986d648622272ae24f7585dab5ffffffff_nft_xxxxx_SpecialNft",
)
.unwrap_err();
NonFungibleAddress::from_str(
"nft_uuid:7f19c3fe5fa13ff66a0d379fe5f9e3508acbd338db6bedd7350d8d565b2c5d32ffffffff",
"nft_uuid_7f19c3fe5fa13ff66a0d379fe5f9e3508acbd338db6bedd7350d8d565b2c5d32ffffffff",
)
.unwrap_err();
NonFungibleAddress::from_str("resource_x nft_str:SpecialNft").unwrap_err();
NonFungibleAddress::from_str(
"resource_7cbfe29101c24924b1b6ccefbfff98986d648622272ae24f7585dab5ffffffff nft_str:",
"resource_7cbfe29101c24924b1b6ccefbfff98986d648622272ae24f7585dab5ffffffff_nft_str_",
)
.unwrap_err();
NonFungibleAddress::from_str(
"resource_7cbfe29101c24924b1b6ccefbfff98986d648622272ae24f7585dab5ffffffff nftx_str:SpecialNft",
"resource_7cbfe29101c24924b1b6ccefbfff98986d648622272ae24f7585dab5ffffffff_nftx_str_SpecialNft",
)
.unwrap_err();
}
Expand Down
Loading

0 comments on commit 2b5ba47

Please sign in to comment.