Substrate高級語法4寵物權(quán)限和購買--by Skyh0801

繼續(xù)昨天的合約

1 定義變量

如果要更新值,Rust 希望你將變量聲明為 mutable (mut):

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

<Objects<T>>::insert(object_id, object);

2 檢查權(quán)限

始終要對權(quán)限進(jìn)行健全性檢查

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 轉(zhuǎn)讓

swap and pop實(shí)現(xiàn)方法

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)?;

建議你在調(diào)用 transfer() 之后直接執(zhí)行 transfer_from() 函數(shù)。

// 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" 賬戶無法擁有相關(guān)的 kitty
從用戶的 owned_kitty_count 中減去 kitty 時可能會出現(xiàn)下溢
將 kitty 添加到用戶的 owned_kitty_count 時可能會出現(xiàn)溢出

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");

好惡心的語法啊...還要重新傳輸發(fā)現(xiàn)錯誤, 但這也是常見調(diào)bug內(nèi)容, 雖然調(diào)bug不應(yīng)該在開發(fā)階段

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 測試

你應(yīng)該運(yùn)行以下手動測試:
使用 token 為多個用戶提供資金撇吞,以便他們都可以參與
讓每個用戶創(chuàng)建多個 kitties
通過使用正確和錯誤的所有者,嘗試將 kitty 從一個用戶轉(zhuǎn)移給另一個用戶
通過使用正確和錯誤的所有者魄健,嘗試設(shè)置 kitty 的價格
使用所有者和其他用戶購買 kitty
使用不足的資金購買 kitty
高價購買 kitty,確保余額被適當(dāng)減少
培育 kitty插勤,檢查新 DNA 是新舊混合物
完成所有這些操作后沽瘦,確認(rèn)所有用戶都具有正確數(shù)量的 kitty革骨,確認(rèn) kitty 總數(shù)正確,并且所有其他存儲變量都被正確表示

8 改進(jìn)

其實(shí)合約還是很簡單,以下有些改進(jìn)方法:

  • 在調(diào)用 breed_kitty() 期間追蹤 kitty 的父母析恋。也許只是一個 event...良哲?

  • 限制 create_kitty() 可以創(chuàng)建的 kitties 數(shù)量,添加價格曲線以使得創(chuàng)建每個新的 kitty 成本更高助隧。

  • 培育出新 kitty 后筑凫,當(dāng)有人收到這只新 kitty,必須支付一定的費(fèi)用并村。確保資金正確發(fā)送給每個用戶巍实。確保每個人都可以使用自己的 kitty 進(jìn)行培育。

  • 添加一個 kitty "fight"橘霎,使得兩個 kitties 可以參與基于隨機(jī)數(shù)和一些加權(quán)統(tǒng)計的游戲蔫浆。勝利的 kitty 的數(shù)據(jù)有所增加殖属,這使他們有更好的機(jī)會贏得下一輪比賽姐叁。

  • 添加基因突變算法到 kitty 培育中,這將引入父母 kitties 都沒有的特征洗显。

  • 為那些不想簡單地設(shè)定定價以購買 kitty 的所有者推出拍賣系統(tǒng)外潜。如果沒有人投標(biāo)則一段時間后拍賣結(jié)束。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末挠唆,一起剝皮案震驚了整個濱河市处窥,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌玄组,老刑警劉巖滔驾,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異俄讹,居然都是意外死亡哆致,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進(jìn)店門患膛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來摊阀,“玉大人,你說我怎么就攤上這事踪蹬“耍” “怎么了?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵跃捣,是天一觀的道長漱牵。 經(jīng)常有香客問我,道長疚漆,這世上最難降的妖魔是什么酣胀? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任蚊惯,我火速辦了婚禮,結(jié)果婚禮上灵临,老公的妹妹穿的比我還像新娘截型。我一直安慰自己,他們只是感情好儒溉,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布宦焦。 她就那樣靜靜地躺著,像睡著了一般顿涣。 火紅的嫁衣襯著肌膚如雪波闹。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天涛碑,我揣著相機(jī)與錄音精堕,去河邊找鬼。 笑死蒲障,一個胖子當(dāng)著我的面吹牛歹篓,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播揉阎,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼庄撮,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了毙籽?” 一聲冷哼從身側(cè)響起洞斯,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎坑赡,沒想到半個月后烙如,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡毅否,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年亚铁,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片搀突。...
    茶點(diǎn)故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡刀闷,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出仰迁,到底是詐尸還是另有隱情甸昏,我是刑警寧澤,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布徐许,位于F島的核電站施蜜,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏雌隅。R本人自食惡果不足惜翻默,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一缸沃、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧修械,春花似錦趾牧、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蹦渣,卻和暖如春哄芜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背柬唯。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工认臊, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人锄奢。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓失晴,卻偏偏與公主長得像,于是被迫代替她去往敵國和親斟薇。 傳聞我的和親對象是個殘疾皇子师坎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評論 2 355

推薦閱讀更多精彩內(nèi)容

  • (注:本文是在原文的基礎(chǔ)上恕酸,根據(jù)個人的理解堪滨,修改部分內(nèi)容并添加了一些注釋) 買賣部分代碼未調(diào)試通過 基礎(chǔ)版的代幣合...
    中v中閱讀 2,906評論 0 2
  • 專業(yè)考題類型管理運(yùn)行工作負(fù)責(zé)人一般作業(yè)考題內(nèi)容選項A選項B選項C選項D選項E選項F正確答案 變電單選GYSZ本規(guī)程...
    小白兔去釣魚閱讀 9,000評論 0 13
  • 【翻譯】原文:https://medium.com/loom-network/how-to-code-your-o...
    speedx閱讀 761評論 0 3
  • 豬豬紀(jì)鵬閱讀 209評論 0 0
  • 今天,是陽光明媚的一天蕊温。從美國袱箱,來了一個我的表姐,她來到中國游玩义矛,終于來到了“青島” 她的名字叫“翔翔”我叫她“...
    葛禹君閱讀 216評論 0 1