美文网首页区块布道区块链研习社
Substrate高级语法4宠物权限和购买--by Skyh08

Substrate高级语法4宠物权限和购买--by Skyh08

作者: skyh25 | 来源:发表于2019-08-03 16:23 被阅读2次

    继续昨天的合约

    1 定义变量

    如果要更新值,Rust 希望你将变量声明为 mutable (mut):

    let mut object = Self::get_object(object_id);
    object.value = new_value;
    
    <Objects<T>>::insert(object_id, object);
    

    2 检查权限

    始终要对权限进行健全性检查

    let owner = Self::owner_of(object_id).ok_or("No owner for this object")?;
    ensure!(owner == sender, "You are not the owner");
    ensure!(<MyObject<T>>::exists(object_id));
    

    3 转让

    swap and pop实现方法

    let kitty_index = <OwnedKittiesIndex<T>>::get(kitty_id);
    
    if kitty_index != new_owned_kitty_count_from {
        let last_kitty_id = <OwnedKittiesArray<T>>::get((from.clone(), new_owned_kitty_count_from));
        <OwnedKittiesArray<T>>::insert((from.clone(), kitty_index), last_kitty_id);
        <OwnedKittiesIndex<T>>::insert(last_kitty_id, kitty_index);
    }
    

    4 购买

    检查为空

    let my_value = <T::Balance as As<u64>>::sa(0);
    ensure!(my_value.is_zero(), "Value is not zero")
    

    付款必须引用use support::traits::Currency;

    <balances::Module<T> as Currency<_>>::transfer(&from, &to, value)?;
    
    

    建议你在调用 transfer() 之后直接执行 transfer_from() 函数。

    // nothing after this line should fail
    <balances::Module<T> as Currency<_>>::transfer(&from, &to, value)?;
    // but this function technically "could" fail
    Self::transfer_from(owner.clone(), sender.clone(), kitty_id)?;
    
    

    transfer_from() 中可能失败的检查:

    kitty 不存在所有者
    "from" 账户无法拥有相关的 kitty
    从用户的 owned_kitty_count 中减去 kitty 时可能会出现下溢
    将 kitty 添加到用户的 owned_kitty_count 时可能会出现溢出

    Self::transfer_from(owner.clone(), sender.clone(), kitty_id)
        .expect("`owner` is shown to own the kitty; \
        `owner` must have greater than 0 kitties, so transfer cannot cause underflow; \
        `all_kitty_count` shares the same type as `owned_kitty_count` \
        and minting ensure there won't ever be more than `max()` kitties, \
        which means transfer cannot cause an overflow; \
        qed");
    
    

    好恶心的语法啊...还要重新传输发现错误, 但这也是常见调bug内容, 虽然调bug不应该在开发阶段

    5 培育

    这就直接参考kitties的算法了...

    6 Show me the code

    话不多说了

    use support::{decl_storage, decl_module, StorageValue, StorageMap,
        dispatch::Result, ensure, decl_event, traits::Currency};
    use system::ensure_signed;
    use runtime_primitives::traits::{As, Hash, Zero};
    use parity_codec::{Encode, Decode};
    use rstd::cmp;
    
    #[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,
            <T as balances::Trait>::Balance
        {
            Created(AccountId, Hash),
            PriceSet(AccountId, Hash, Balance),
            Transferred(AccountId, AccountId, Hash),
            Bought(AccountId, AccountId, Hash, Balance),
        }
    );
    
    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 nonce = <Nonce<T>>::get();
                let random_hash = (<system::Module<T>>::random_seed(), &sender, nonce)
                    .using_encoded(<T as system::Trait>::Hashing::hash);
    
                let new_kitty = Kitty {
                    id: random_hash,
                    dna: random_hash,
                    price: <T::Balance as As<u64>>::sa(0),
                    gen: 0,
                };
    
                Self::mint(sender, random_hash, new_kitty)?;
    
                <Nonce<T>>::mutate(|n| *n += 1);
    
                Ok(())
            }
    
            fn set_price(origin, kitty_id: T::Hash, new_price: T::Balance) -> Result {
                let sender = ensure_signed(origin)?;
    
                ensure!(<Kitties<T>>::exists(kitty_id), "This cat does not exist");
    
                let owner = Self::owner_of(kitty_id).ok_or("No owner for this kitty")?;
                ensure!(owner == sender, "You do not own this cat");
    
                let mut kitty = Self::kitty(kitty_id);
                kitty.price = new_price;
    
                <Kitties<T>>::insert(kitty_id, kitty);
    
                Self::deposit_event(RawEvent::PriceSet(sender, kitty_id, new_price));
    
                Ok(())
            }
    
            fn transfer(origin, to: T::AccountId, kitty_id: T::Hash) -> Result {
                let sender = ensure_signed(origin)?;
    
                let owner = Self::owner_of(kitty_id).ok_or("No owner for this kitty")?;
                ensure!(owner == sender, "You do not own this kitty");
    
                Self::transfer_from(sender, to, kitty_id)?;
    
                Ok(())
            }
    
            fn buy_kitty(origin, kitty_id: T::Hash, max_price: T::Balance) -> Result {
                let sender = ensure_signed(origin)?;
    
                ensure!(<Kitties<T>>::exists(kitty_id), "This cat does not exist");
    
                let owner = Self::owner_of(kitty_id).ok_or("No owner for this kitty")?;
                ensure!(owner != sender, "You can't buy your own cat");
    
                let mut kitty = Self::kitty(kitty_id);
    
                let kitty_price = kitty.price;
                ensure!(!kitty_price.is_zero(), "The cat you want to buy is not for sale");
                ensure!(kitty_price <= max_price, "The cat you want to buy costs more than your max price");
    
                <balances::Module<T> as Currency<_>>::transfer(&sender, &owner, kitty_price)?;
    
                Self::transfer_from(owner.clone(), sender.clone(), kitty_id)
                    .expect("`owner` is shown to own the kitty; \
                    `owner` must have greater than 0 kitties, so transfer cannot cause underflow; \
                    `all_kitty_count` shares the same type as `owned_kitty_count` \
                    and minting ensure there won't ever be more than `max()` kitties, \
                    which means transfer cannot cause an overflow; \
                    qed");
    
                kitty.price = <T::Balance as As<u64>>::sa(0);
                <Kitties<T>>::insert(kitty_id, kitty);
    
                Self::deposit_event(RawEvent::Bought(sender, owner, kitty_id, kitty_price));
    
                Ok(())
            }
    
            fn breed_kitty(origin, kitty_id_1: T::Hash, kitty_id_2: T::Hash) -> Result{
                let sender = ensure_signed(origin)?;
    
                ensure!(<Kitties<T>>::exists(kitty_id_1), "This cat 1 does not exist");
                ensure!(<Kitties<T>>::exists(kitty_id_2), "This cat 2 does not exist");
    
                let nonce = <Nonce<T>>::get();
                let random_hash = (<system::Module<T>>::random_seed(), &sender, nonce)
                    .using_encoded(<T as system::Trait>::Hashing::hash);
    
                let kitty_1 = Self::kitty(kitty_id_1);
                let kitty_2 = Self::kitty(kitty_id_2);
    
                let mut final_dna = kitty_1.dna;
                for (i, (dna_2_element, r)) in kitty_2.dna.as_ref().iter().zip(random_hash.as_ref().iter()).enumerate() {
                    if r % 2 == 0 {
                        final_dna.as_mut()[i] = *dna_2_element;
                    }
                }
    
                let new_kitty = Kitty {
                    id: random_hash,
                    dna: final_dna,
                    price: <T::Balance as As<u64>>::sa(0),
                    gen: cmp::max(kitty_1.gen, kitty_2.gen) + 1,
                };
    
                Self::mint(sender, random_hash, new_kitty)?;
    
                <Nonce<T>>::mutate(|n| *n += 1);
    
                Ok(())
            }
        }
    }
    
    impl<T: Trait> Module<T> {
        fn mint(to: T::AccountId, kitty_id: T::Hash, new_kitty: Kitty<T::Hash, T::Balance>) -> Result {
            ensure!(!<KittyOwner<T>>::exists(kitty_id), "Kitty already exists");
    
            let owned_kitty_count = Self::owned_kitty_count(&to);
    
            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")?;
    
            <Kitties<T>>::insert(kitty_id, new_kitty);
            <KittyOwner<T>>::insert(kitty_id, &to);
    
            <AllKittiesArray<T>>::insert(all_kitties_count, kitty_id);
            <AllKittiesCount<T>>::put(new_all_kitties_count);
            <AllKittiesIndex<T>>::insert(kitty_id, all_kitties_count);
    
            <OwnedKittiesArray<T>>::insert((to.clone(), owned_kitty_count), kitty_id);
            <OwnedKittiesCount<T>>::insert(&to, new_owned_kitty_count);
            <OwnedKittiesIndex<T>>::insert(kitty_id, owned_kitty_count);
    
            Self::deposit_event(RawEvent::Created(to, kitty_id));
    
            Ok(())
        }
    
        fn transfer_from(from: T::AccountId, to: T::AccountId, kitty_id: T::Hash) -> Result {
            let owner = Self::owner_of(kitty_id).ok_or("No owner for this kitty")?;
    
            ensure!(owner == from, "'from' account does not own this kitty");
    
            let owned_kitty_count_from = Self::owned_kitty_count(&from);
            let owned_kitty_count_to = Self::owned_kitty_count(&to);
    
            let new_owned_kitty_count_to = owned_kitty_count_to.checked_add(1)
                .ok_or("Transfer causes overflow of 'to' kitty balance")?;
    
            let new_owned_kitty_count_from = owned_kitty_count_from.checked_sub(1)
                .ok_or("Transfer causes underflow of 'from' kitty balance")?;
    
            let kitty_index = <OwnedKittiesIndex<T>>::get(kitty_id);
            if kitty_index != new_owned_kitty_count_from {
                let last_kitty_id = <OwnedKittiesArray<T>>::get((from.clone(), new_owned_kitty_count_from));
                <OwnedKittiesArray<T>>::insert((from.clone(), kitty_index), last_kitty_id);
                <OwnedKittiesIndex<T>>::insert(last_kitty_id, kitty_index);
            }
    
            <KittyOwner<T>>::insert(&kitty_id, &to);
            <OwnedKittiesIndex<T>>::insert(kitty_id, owned_kitty_count_to);
    
            <OwnedKittiesArray<T>>::remove((from.clone(), new_owned_kitty_count_from));
            <OwnedKittiesArray<T>>::insert((to.clone(), owned_kitty_count_to), kitty_id);
    
            <OwnedKittiesCount<T>>::insert(&from, new_owned_kitty_count_from);
            <OwnedKittiesCount<T>>::insert(&to, new_owned_kitty_count_to);
    
            Self::deposit_event(RawEvent::Transferred(from, to, kitty_id));
    
            Ok(())
        }
    }
    
    

    7 测试

    你应该运行以下手动测试:
    使用 token 为多个用户提供资金,以便他们都可以参与
    让每个用户创建多个 kitties
    通过使用正确和错误的所有者,尝试将 kitty 从一个用户转移给另一个用户
    通过使用正确和错误的所有者,尝试设置 kitty 的价格
    使用所有者和其他用户购买 kitty
    使用不足的资金购买 kitty
    高价购买 kitty,确保余额被适当减少
    培育 kitty,检查新 DNA 是新旧混合物
    完成所有这些操作后,确认所有用户都具有正确数量的 kitty,确认 kitty 总数正确,并且所有其他存储变量都被正确表示

    8 改进

    其实合约还是很简单,以下有些改进方法:

    • 在调用 breed_kitty() 期间追踪 kitty 的父母。也许只是一个 event...?

    • 限制 create_kitty() 可以创建的 kitties 数量,添加价格曲线以使得创建每个新的 kitty 成本更高。

    • 培育出新 kitty 后,当有人收到这只新 kitty,必须支付一定的费用。确保资金正确发送给每个用户。确保每个人都可以使用自己的 kitty 进行培育。

    • 添加一个 kitty "fight",使得两个 kitties 可以参与基于随机数和一些加权统计的游戏。胜利的 kitty 的数据有所增加,这使他们有更好的机会赢得下一轮比赛。

    • 添加基因突变算法到 kitty 培育中,这将引入父母 kitties 都没有的特征。

    • 为那些不想简单地设定定价以购买 kitty 的所有者推出拍卖系统。如果没有人投标则一段时间后拍卖结束。

    相关文章

      网友评论

        本文标题:Substrate高级语法4宠物权限和购买--by Skyh08

        本文链接:https://www.haomeiwen.com/subject/jribkctx.html