【Substrate Collectables教程】【第2章Kitties】4 拥有多个 Kitties
拥有多个 Kitties
4.1 使用 Tuples 去模拟高阶数组
我们需要引入一些更复杂的存储项来表示多个用户对多个项目的所有权。
幸运的是,根据我们的需求,使用一个由 AccountId
和 Index
组成的 tuple 几乎就可以解决我们的问题了。
以下是我们如何使用这样的结构构造每个人独有的 "friends list":
MyFriendsArray get(my_friends_array): map (T::AccountId, u32) => T::AccountId; MyFriendsCount get(my_friends_count): map T::AccountId => u32;
这模拟了一个标准的二维数组,如:
MyFriendsArray[AccountId][Index] -> AccountId
我们也可以获取用户的朋友数量:
MyFriendsArray[AccountId].length()
4.2 相对索引
与以前一样,我们可以通过索引项目的位置来优化 runtime 需要执行的计算工作。一般的方法是反转 MyFriendsArray
的映射,并创建一个这样的存储项:
MyFriendsIndex: map (T::AccountId, T::AccountId) => u32;
如果 AccountId
代表用户和他们的朋友, 那么返回值将是 MyFriendsArray
的索引,即该朋友在该用户的朋友列表中的存储位置。
但是,由于我们的 kitty 都具有唯一标识符作为 Hash
,并且不能被多个用户所拥有,所以我们实际上可以简化此结构
MyKittiesIndex: map T::Hash => u32;
这个索引告诉了我们对于一个给定的 kitty,可以在哪里查看该项的 所有者 数组。
4.3 示例
use support::{decl_storage, decl_module, StorageValue, StorageMap, dispatch::Result, ensure, decl_event}; use system::ensure_signed; use runtime_primitives::traits::{As, Hash}; use parity_codec::{Encode, Decode}; #[derive(Encode, Decode, Default, Clone, PartialEq)] #[cfg_attr(feature = "std", derive(Debug))] pub struct Kitty<Hash, Balance> { id: Hash, dna: Hash, price: Balance, gen: u64, } pub trait Trait: balances::Trait { type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>; } decl_event!( pub enum Event<T> where <T as system::Trait>::AccountId, <T as system::Trait>::Hash { Created(AccountId, Hash), } ); decl_storage! { trait Store for Module<T: Trait> as KittyStorage { Kitties get(kitty): map T::Hash => Kitty<T::Hash, T::Balance>; KittyOwner get(owner_of): map T::Hash => Option<T::AccountId>; AllKittiesArray get(kitty_by_index): map u64 => T::Hash; AllKittiesCount get(all_kitties_count): u64; AllKittiesIndex: map T::Hash => u64; OwnedKittiesArray get(kitty_of_owner_by_index): map (T::AccountId, u64) => T::Hash; OwnedKittiesCount get(owned_kitty_count): map T::AccountId => u64; OwnedKittiesIndex: map T::Hash => u64; Nonce: u64; } } decl_module! { pub struct Module<T: Trait> for enum Call where origin: T::Origin { fn deposit_event<T>() = default; fn create_kitty(origin) -> Result { let sender = ensure_signed(origin)?; let owned_kitty_count = Self::owned_kitty_count(&sender); let new_owned_kitty_count = owned_kitty_count.checked_add(1) .ok_or("Overflow adding a new kitty to account balance")?; let all_kitties_count = Self::all_kitties_count(); let new_all_kitties_count = all_kitties_count.checked_add(1) .ok_or("Overflow adding a new kitty to total supply")?; let nonce = <Nonce<T>>::get(); let random_hash = (<system::Module<T>>::random_seed(), &sender, nonce) .using_encoded(<T as system::Trait>::Hashing::hash); ensure!(!<KittyOwner<T>>::exists(random_hash), "Kitty already exists"); let new_kitty = Kitty { id: random_hash, dna: random_hash, price: <T::Balance as As<u64>>::sa(0), gen: 0, }; <Kitties<T>>::insert(random_hash, new_kitty); <KittyOwner<T>>::insert(random_hash, &sender); <AllKittiesArray<T>>::insert(all_kitties_count, random_hash); <AllKittiesCount<T>>::put(new_all_kitties_count); <AllKittiesIndex<T>>::insert(random_hash, all_kitties_count); <OwnedKittiesArray<T>>::insert((sender.clone(), owned_kitty_count), random_hash); <OwnedKittiesCount<T>>::insert(&sender, new_owned_kitty_count); <OwnedKittiesIndex<T>>::insert(random_hash, owned_kitty_count); <Nonce<T>>::mutate(|n| *n += 1); Self::deposit_event(RawEvent::Created(sender, random_hash)); Ok(()) } } }