Substrateでブロックチェーンとアプリケーションを作る Runtimeの設定から〜ゲームPlayまで

こんにちは、Stakedの渡辺創太です。

前回こちらの記事で、Substrateのインストールからアカウントの作成までを行いました。この記事はその続きです。Gavin Woodの Web3 Summitでのプレゼンを参考にしています。

Runtimeモジュールを作成する

RuntimeとはSubstrateにおけるブロック処理ロジックなどを決める機能です。State Transaction Functiuonとも言われるときもあり、WebAssemblyバイナリーでオンチェーンに記載さてています。詳しくはScraoboxに記載しています。

前回、substrate-node-templateをインストールしましたが、./runtime/src/demo.rsのフォルダに記載していきます。

Screen Shot 2019-02-13 at 11.09.02.png

作成したdemo.rsにいくつかのライブラリーをインストールします。

use parity_codec::Encode;

use support::{StorageValue, dispatch::Result, decl_module, decl_storage};
use runtime_primitives::traits::Hash;
use {balances, system::{self, ensure_signed}};

pub trait Trait: balance::Trait {}

Web3 Summitのプレゼンでは、簡単な賭けゲームを作成していました。ゲームに勝てば、Potに溜まっていた金額を得ることができ、負ければPotに没収されるという簡単な仕組みです。

ライブラリーをインストールしたので、次に、モジュールを宣言する必要があります。(module declaration)

decl_module! {

pub struct Module<T: Trait> for enum Call where origin: T::Origin {
fn play(origin) -> Result{
//ゲームをプレイするロジック
}

fn set_payment(_origin, value: T::Balance) -> Result{
//ゲームのpaymentの仕様
}
}
}


decl_storage! {
trait Store for Module<T: Trait> as Demo {
Payment get(payment): Option<T::Balance>;
Pot get(pot): T::Balance;
}
}

strageモジュールはオンチェーンに載せる情報を定義するために使用します。

次に、モジュール内のロジックを書いていきます。

ゲームをプレイするロジック

fn play(origin) -> Result {

let sender = ensure_signed(origin)?;
//decl_strage!で宣言しているからpaymentが使える
let payment = Self::payment().ok_or("Must have payment amount set")?;
 //senderの残高を減少させる
<balances::Module<T>>::decrease_free_balance(&sender, payment)?;

//ハッシュ関数を通してハッシュ値の最初のbyteが128以下であれば勝ち。potにあった金額がSenderに払われる
if (<system::Module<T>>::random_seed(), &sender)
.using_encoded(<T as system::Trait>::Hashing::hash)
.using_encoded(|e| e[0] < 128)
{
<balances::Module<T>>::increase_free_balance_creating(&sender, <Pot<T>>::take());
}
 
//結果どうあれ、senderが賭けに参加した金額がデポジットされる
<Pot<T>>::mutate(|pot| *pot += payment);

Ok(())
}

ゲーム内のPaymentのロジック


fn set_payment(_origin, value: T::Balance) -> Result {
 //イニシャルpaymentがセットされていない場合の処理
if Self::payment().is_none() {

<Payment<T>>::put(value);
<Pot<T>>::put(value);
}

Ok(())
}

変更をアップデートする

上記でModuleを設定しました。変更を実行するために./runtime/src/lib.rsを編集します。

mod demo;の追記。

Screen Shot 2019-02-14 at 13.48.58.png

impl demo::Trait for Runtime {}の追記

Screen Shot 2019-02-14 at 14.20.39.png

Demo: demo::{Module, Call, Storage},の追記

Screen Shot 2019-02-14 at 13.55.29.png

上記で新しいRuntimeモジュールを作成したので、次にブロックチェーンのアップデートをします。

Substrate-node-templateのディレクトリでビルド

substrate-node-template $ ./build.sh

Screen Shot 2019-02-14 at 14.21.40.png

ビルド後、substrate-uiのnpm run devで接続したlocalhost8000にて一番下にRuntime Upgradeができるので、Select Runtimeを押し、

./runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm

を選択する。これでRuntimeのアップデートができました。Developer Consoleでみると動いていることがわかります。

Screen Shot 2019-02-14 at 14.58.08.png

UIを整える

UIを整えるにはsubstrate-uiレポジトリーを編集する必要があります。

./substrate-ui/src/app.jsxを編集します。

新しく以下のセクションを追加します。

<Divider hidden />

<Segment style={{margin: '1em'}} padded>
<Header as='h2'>
<Icon name='game' />
<Header.Content>
Play the game
<Header.Subheader>Play the game here!</Header.Subheader>
</Header.Content>
</Header>
<div style={{paddingBottom: '1em'}}>
<div style={{fontSize: 'small'}}>player</div>
<SignerBond bond={this.player}/>
<If condition={this.player.ready()} then={<span>
<Label>Balance
<Label.Detail>
<Pretty value={runtime.balances.balance(this.player)}/>
</Label.Detail>
</Label>
</span>}/>
</div>
<TransactButton
content="Play"
icon='game'
tx={{
sender: this.player,
call: calls.demo.play()
}}
/>
<Label>Pot Balance
<Label.Detail>
<Pretty value={runtime.demo.pot}/>
</Label.Detail>
</Label>
</Segment>

同時に、exportの部分にthis.player = new Bond;を追記します。

................

this.seedAccount.use()
this.runtime = new Bond;
//これ
this.player = new Bond;
}

これでUIを構築することができ、ゲームをPlayすることができます。

Screen Shot 2019-02-14 at 15.21.10.png

Source: https://www.youtube.com/watch?v=0IoUZdDi5Is&t=22s