繼續(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é)束。