見出し画像

ここからはじめるPlasm Network:第三回: WASMベース(ink!)のスマートコントラクトの開発とデプロイ方法

みなさん、こんにちは。Fintertechでエンジニアをやっている高橋です。
本日は「ここからはじめるPlasm Network」の第3回目です。今回からスマートコントラクトの開発に入っていきます。今回はWASMベースのsubstrateのink!での実装をご紹介します。ink!は、Substrateベースのブロックチェーン上で動作するWebAssemblyスマートコントラクトを作成するためのRustベースの組み込みドメイン固有言語(eDSL)です。

(1) Plasm Networkとは何か~ノード起動方法まで☆前々回
(2) テストネットValidatorになる方法☆前回
(3) WASMベース(ink!)のスマートコントラクトの開発とデプロイ方法★今回
(4) solidityをWASMに変換してスマートコントラクトをデプロイする方法
(5) EVMベース(Remix)でのスマートコントラクトの開発とデプロイ方法

はじめに

まずはじめに、ローカルのPlasmノードを使用します(ローカルPCである必然性はなくクラウド上で動作させているPlasmノードでも問題ありません)。第一回をやっていない方は、まず第1回目に取り組んで頂き、ノードの準備をお願い致します。本日のプログラムは以下の通りです。
(1) 環境を準備
(2) サンプルプログラムの準備とコンパイル
(3) ローカルノードへのアップロードとデプロイ
(4) Dustyテストネットへのアップロードとデプロイ

(1) 環境を準備

それではスマートコントラクトを実装するための準備をしていきます。第一回で紹介しているRustのコンパイル環境が必要ですので、第一回目を飛ばしている方は、環境構築からお願い致します。以下のコマンドを使ってインストールしていきます。引用:Substrate公式ドキュメント

cargo install canvas-node --git https://github.com/paritytech/canvas-node.git --tag v0.1.4 --force --locked
cargo install cargo-contract --vers 0.8.0 --force --locked

以下のようにバージョンコマンドを打って正常インストールを確認して下さい。

cargo contract --version

(2) サンプルプログラムの準備とコンパイル

環境の準備が出来たので、次はサンプルプログラムを準備してコンパイルしていきます。以下のコマンドでプロジェクトを作成しましょう。プロジェクトを作成すると、サンプルプロジェクトである「flipper」が用意されるので、今回はプロジェクトの名前も合わせて「flipper」をPlasm Networkにデプロイしていきましょう。
以下のコマンドで作成します。

cargo contract new flipper

以下の画面のようにプロジェクトが作成されていることを確認して下さい。

画像1

ソースコードの中身を確認していきましょう。以下にflipperコントラクトのソースコードを抜粋しました。順を追って見てみます。
・まずstorage注釈がある部分を見て下さい。ここは、このコントラクトがチェーンに保存するデータを定義しています。今回はboolの変数が定義されているのが確認出来ます。
・下に行くとconstractor注釈のついた二つの関数が確認出来ます。これは書いてあるとおりコンストラクター関数です。デプロイする時に呼ばれるものであり、どちらかを選択して呼ぶことが出来る実装になっています。
さらに下に行くとmessage注釈のついた二つの関数があることが確認出来ます。
・flipですが、関数の中身はチェーンに格納されているvalueと反対の値(すなわちTrueならFalse、FalseならTrue)をチェーンに格納する関数になります。この関数の呼び出しはチェーンの中身を書き換えるのでトランザクションの実行が必要になります。
・もう一つの関数getは単純にチェーンの値を取り出して参照していることがわかります。この関数はRPC呼び出しとなり、トランザクションの実行は必要ありません。つまり、flip関数にはガス代が必要で、get関数には不要であるということになります。

-- snip --
   /// Defines the storage of your contract.
   /// Add new fields to the below struct in order
   /// to add new static storage fields to your contract.
   #[ink(storage)]
   pub struct Flipper {
       /// Stores a single `bool` value on the storage.
       value: bool,
   }
   impl Flipper {
       /// Constructor that initializes the `bool` value to the given `init_value`.
       #[ink(constructor)]
       pub fn new(init_value: bool) -> Self {
           Self { value: init_value }
       }
       /// Constructor that initializes the `bool` value to `false`.
       ///
       /// Constructors can delegate to other constructors.
       #[ink(constructor)]
       pub fn default() -> Self {
           Self::new(Default::default())
       }
       /// A message that can be called on instantiated contracts.
       /// This one flips the value of the stored `bool` from `true`
       /// to `false` and vice versa.
       #[ink(message)]
       pub fn flip(&mut self) {
           self.value = !self.value;
       }
       /// Simply returns the current value of our `bool`.
       #[ink(message)]
       pub fn get(&self) -> bool {
           self.value
       }
   }
-- snip --

関数の中身が理解出来たところでコンパイルに進みたいと思うのですが、テストコードが実装されているので、少し寄り道してテストコードを実行してみましょう。以下のコマンドです。

cargo test

以下の画面のように全件パスしたことを確認出来ると思います。

画像2

寄り道はここまでにして、コンパイルしていきます。以下のコマンドを実行します。

 ​cargo +nightly contract build

以下のように「flipper.contract」ファイルが出来ていることを確認出来ればOKです。

画像3

(3) ローカルノードへのアップロードとデプロイ

用意が出来たところで、まずはローカルノードにアップロードし、デプロイしていきましょう。まずはローカルノードを起動しましょう。第一回で作成したPlasmディレクトリに移動し、以下のコマンドを実行します。前回実行した時のデータが残っていると上手く動作しない場合がありますので、一度ローカルDBを削除してから実行しましょう。(以下のpurge-chainが入っているコマンドです)

cd Plasm
./target/release/plasm-node purge-chain --dev
./target/release/plasm-node --dev

起動が完了しましたら、ここからノードに接続しましょう。その後下の画面のようにコントラクトの画面に移動しましょう。

画像4

「WASMをアップロードする」ボタンを押下してデプロイしていきます。下の画像の赤枠部分をクリックしてコンパイルして作成した「flipper.contract」を設定して下さい。

画像5

アップロードが完了したらデプロイボタンを押下してデプロイしましょう。私は以下のように設定しました。

画像6

デプロイが完了したら、「Contracts」の「Messages」をクリックして開き、「Current Value」の値を確認して下さい。上の画面の状態でデプロイすると「True」になっているはずです。
確認出来たら、「exec」ボタンを押下して「flip()」関数を実行して、値を変更してみましょう。

画像7

上手く実行出来たでしょうか?
まずはここまでくれば基礎はOKです。色々自分なりのコントラクトを実装してデプロイしてみて下さい。

(4) Dustyテストネットへのアップロードとデプロイ

次は同じ要領でDusty Testnetでデプロイしていきましょう。
テストネットバリデーターになれている人は報酬としてトークンを得ることが出来るので、十分にDustyでコントラクトを実行することが出来ると思いますが、バリデーターになれていない方は十分なトークンを持っていないと思います。faucetでもらうことが前提ですが、足りない場合はDiscordの日本語チャンネルで私にご連絡下さい。先着何名様かに限って対応させて頂きます。
ここからDustyテストネットにアクセスして下さい。

コントラクトのアップロード・デプロイ方法はローカルノードと基本的には同じなので、説明は省略させて頂きます。
私があげたコントラクトの画面とアドレスをあげておきます。もしこのコントラクトが生きていたら、あなたも実行することが出来ますので、是非試してみて下さい。「既存のコントラクトを追加する」ボタンから、以下に記載するの「コントラクトアドレス」と「jsonファイル」を指定して実行して下さい。
コントラクトアドレス:「XctvNJaPohtx6EWgmRjoCwQ2kuz9TbYNGpnh81nxzo9gEGB」

画像8

私がデプロイしたコントラクトが復元できるようにjsonファイルをのせておきます。現状はデプロイ時の「寄付金」が少ないとステータスが「Tombstone」となり、使えなくなるようです。しかし、確かにデプロイしたことは確認出来ます。

{
 "metadataVersion": "0.1.0",
 "source": {
   "hash": "0x5b02ceadaacee8408d3c6496394847092c099bcb897221dbe8d22c16d372fa17",
   "language": "ink! 3.0.0-rc2",
   "compiler": "rustc 1.51.0-nightly"
 },
 "contract": {
   "name": "flipper",
   "version": "0.1.0",
   "authors": [
     "[your_name] <[your_email]>"
   ]
 },
 "spec": {
   "constructors": [
     {
       "args": [
         {
           "name": "init_value",
           "type": {
             "displayName": [
               "bool"
             ],
             "type": 1
           }
         }
       ],
       "docs": [
         " Constructor that initializes the `bool` value to the given `init_value`."
       ],
       "name": [
         "new"
       ],
       "selector": "0xd183512b"
     },
     {
       "args": [],
       "docs": [
         " Constructor that initializes the `bool` value to `false`.",
         "",
         " Constructors can delegate to other constructors."
       ],
       "name": [
         "default"
       ],
       "selector": "0x6a3712e2"
     }
   ],
   "docs": [],
   "events": [],
   "messages": [
     {
       "args": [],
       "docs": [
         " A message that can be called on instantiated contracts.",
         " This one flips the value of the stored `bool` from `true`",
         " to `false` and vice versa."
       ],
       "mutates": true,
       "name": [
         "flip"
       ],
       "payable": false,
       "returnType": null,
       "selector": "0xc096a5f3"
     },
     {
       "args": [],
       "docs": [
         " Simply returns the current value of our `bool`."
       ],
       "mutates": false,
       "name": [
         "get"
       ],
       "payable": false,
       "returnType": {
         "displayName": [
           "bool"
         ],
         "type": 1
       },
       "selector": "0x1e5ca456"
     }
   ]
 },
 "storage": {
   "struct": {
     "fields": [
       {
         "layout": {
           "cell": {
             "key": "0x0000000000000000000000000000000000000000000000000000000000000000",
             "ty": 1
           }
         },
         "name": "value"
       }
     ]
   }
 },
 "types": [
   {
     "def": {
       "primitive": "bool"
     }
   }
 ]
}ま

まとめ

今回はsubstrate ink!によるPlasm Networkのスマートコントラクト開発をご紹介させて頂きました。Rust言語に慣れている方であれば、色々と試せると思いますので、是非色々なアイデアを着想した上で試して頂ければと思います。Plasm Networkでは役に立つコントラクトの開発者に報酬を与える機能dAppsRewardsがありますので、開発者には非常に魅力的なプラットフォームだと私は考えます。
さて、次回は「私はEthereum派なんだよねー、Solidityは実装出来るけど、Rustは・・・」という方向けです!Plasm Networkでは二つの方法でEthereumのコントラクト開発者に道を用意しています。そのうちの一つSolidityで実装したコントラクトをink!ベースに変換するSolangプロジェクトを利用したスマートコントラクト開発を紹介させて頂きます。是非ご期待下さい。
興味を持って頂けた方は是非、Plasm Networkのコミュニティに参加して下さい。
Website | Twitter | Telegram | Discord | GitHub

この記事が参加している募集