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

继续昨天的合约

1 定义变量

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

let mut object = Self::get_object(object_id);
object.value = new_value;

>::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!(>::exists(object_id));

3 转让

swap and pop实现方法

let kitty_index = >::get(kitty_id);

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

4 购买

检查为空

let my_value = >::sa(0);
ensure!(my_value.is_zero(), "Value is not zero")

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

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

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

// nothing after this line should fail
 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 {
    id: Hash,
    dna: Hash,
    price: Balance,
    gen: u64,
}

pub trait Trait: balances::Trait {
    type Event: From> + Into<::Event>;
}

decl_event!(
    pub enum Event
    where
        ::AccountId,
        ::Hash,
        ::Balance
    {
        Created(AccountId, Hash),
        PriceSet(AccountId, Hash, Balance),
        Transferred(AccountId, AccountId, Hash),
        Bought(AccountId, AccountId, Hash, Balance),
    }
);

decl_storage! {
    trait Store for Module as KittyStorage {
        Kitties get(kitty): map T::Hash => Kitty;
        KittyOwner get(owner_of): map T::Hash => Option;

        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 for enum Call where origin: T::Origin {

        fn deposit_event() = default;

        fn create_kitty(origin) -> Result {
            let sender = ensure_signed(origin)?;
            let nonce = >::get();
            let random_hash = (>::random_seed(), &sender, nonce)
                .using_encoded(::Hashing::hash);

            let new_kitty = Kitty {
                id: random_hash,
                dna: random_hash,
                price: >::sa(0),
                gen: 0,
            };

            Self::mint(sender, random_hash, new_kitty)?;

            >::mutate(|n| *n += 1);

            Ok(())
        }

        fn set_price(origin, kitty_id: T::Hash, new_price: T::Balance) -> Result {
            let sender = ensure_signed(origin)?;

            ensure!(>::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;

            >::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!(>::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");

             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 = >::sa(0);
            >::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!(>::exists(kitty_id_1), "This cat 1 does not exist");
            ensure!(>::exists(kitty_id_2), "This cat 2 does not exist");

            let nonce = >::get();
            let random_hash = (>::random_seed(), &sender, nonce)
                .using_encoded(::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: >::sa(0),
                gen: cmp::max(kitty_1.gen, kitty_2.gen) + 1,
            };

            Self::mint(sender, random_hash, new_kitty)?;

            >::mutate(|n| *n += 1);

            Ok(())
        }
    }
}

impl Module {
    fn mint(to: T::AccountId, kitty_id: T::Hash, new_kitty: Kitty) -> Result {
        ensure!(!>::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")?;

        >::insert(kitty_id, new_kitty);
        >::insert(kitty_id, &to);

        >::insert(all_kitties_count, kitty_id);
        >::put(new_all_kitties_count);
        >::insert(kitty_id, all_kitties_count);

        >::insert((to.clone(), owned_kitty_count), kitty_id);
        >::insert(&to, new_owned_kitty_count);
        >::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 = >::get(kitty_id);
        if kitty_index != new_owned_kitty_count_from {
            let last_kitty_id = >::get((from.clone(), new_owned_kitty_count_from));
            >::insert((from.clone(), kitty_index), last_kitty_id);
            >::insert(last_kitty_id, kitty_index);
        }

        >::insert(&kitty_id, &to);
        >::insert(kitty_id, owned_kitty_count_to);

        >::remove((from.clone(), new_owned_kitty_count_from));
        >::insert((to.clone(), owned_kitty_count_to), kitty_id);

        >::insert(&from, new_owned_kitty_count_from);
        >::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 Skyh0801)