Remixは、スマートコントラクトの開発言語であるSolidityのIDEです。今回、このRemix上の JavaScript VM を使ってSolidityのデバッグとスマートコントラクトの実行シミュレーションを行ってみました。
#[1] デバッガの動作確認
動作確認用のコントラクトとして以下のようなソースコードを用意しました。コントラクトの中にいくつか変数を宣言し、関数を通してそれら変数に値を設定し、かつ戻り値として出力してみるという簡単なものです。
pragma solidity ^0.4.20;
contract Sample {
// Variables
address public addr;
string public name;
uint public age;
string public telNo;
event Message(string);
// Constructor
function Sample() public {
}
// Set variables
function set(string _name, uint _age, string _telNo) public returns(address, string, uint, string){
addr = msg.sender;
name = _name;
age = _age;
telNo = _telNo;
Message("変数に引数の値を設定しました。");
return (addr, name, age, telNo);
}
}
Remixのエディタでソースコードを作成、あるいはローカルファイルよりコピーし貼り付けます。ここで注意を要するのは、現行バージョンのRemixでは、コメントにかな漢字混じりの日本語を使用すると、デバッガを起動した時、実行するステップとエディタ上でカーソルが示す行が一致しなくなってしまうということでした。
[Compile]タブでコンパイルエラーがないことを確認後、[Run]タブを開きます。[Emvironment]は、[JavaScript VM]を選択します。[Account]をドロップダウンすると、それぞれ 100ether づつの残高を持った 5個のアカウントがあらかじめ準備されています。
###1. コントラクトのデプロイ
アカウント 0xca3...a733c を選択し、[Create]ボタンをクリックします。
コントラクトがデプロイされ、Remix画面下部のターミナル部分に、下記メッセージが表示されます。
[Details]ボタンをクリックすると、トランザクションの詳細が表示され、アカウント 0xca3...a733c から Sampleコントラクトのコンストラクタに送られたトランザクションであり、そのトランザクションコストとして 642439wei のGasが消費されたことが分かります。
アカウント 0xca3...a733c の残高を調べてみると、当初あった残高から 642439wei だけ減っていることが確認できます。
###2. 変数に値を設定するトランザクション
変数nameに "日本太郎"、ageに35、telNoに"03-1234-5678" を設定するトランザクションを実行します。アカウント 0x147...c160c を選択し、set()関数の引数として、[set]ボタン右の入力欄に "日本太郎",35,"03-1234-5678" と入力し、[set]ボタンをクリックします。
ターミナル部分に出力された下記メッセージの[Details]ボタンをクリックします。
トランザクションの詳細が表示され、引数として入力した値が[decoded input]欄に、戻り値として出力した値が[decoded output]欄に、そして、ソースコードの event文 に Message として設定した文言が[logs]欄に表示されているのが確認できます。
###3. デバッグ
デバッグを行うには、先ほどの[Details]ボタンの右にある[debug]ボタンをクリックします。
右パネルに[Debugger]タブが開き、[Solidity Locals]欄にset()関数の引数として入力した値が表示され、エディタ画面では function set() の定義行にカーソルが移動します。(引数のない関数の場合は、カーソルは関数内の最初の実行可能な行に移動します。)
続いて、[Step over forward] ボタンをクリックするとステップ実行が始まります。エディタ画面ではカーソルが順次移動、[Solidity State]欄では各変数に順次値が設定されて行きます。
(注. [Step over forward] ボタンを順次クリックして行く過程で、次のステップに移行する前にボタンを複数回押す必要があったり、エディタ画面全体が紫色に変わったりすることがありますが、そのまま押し続けると次のステップに進行します。)
操作ボタンとしては、他に[Step over back]、[Step into]、[Step back]があり、関数内を先に行ったり戻ったりすることができます。また、エディタの行番号をクリックすることによりブレークポイントが設定でき、[Jump to the next breakpoint]、[Jump to the previous breakpoint]ボタンが使えるようになります。また他に「Start debugging]、[stop debugging]、[Jump out]ボタンも使えます。
#[2] イーサリアム送金のシミュレーション
次に、メンバー制のチャリティ基金で寄付金を募金するスマートコントラクトを想定し、イーサリアム送金のシミュレーションを行ってみました。ここでは、メンバー情報を保持する構造体と、寄付金情報を保持する構造体を使います。
pragma solidity ^0.4.20;
contract Donation {
// Structure to keep member information
struct Member {
address addr;
string name;
uint age;
string telNo;
}
mapping (uint => Member) public members; // Mapping to manage member information
uint public memberId;
// Structure to keep donation information
struct Contribution {
address adddr;
uint amount;
}
mapping (uint => Contribution) public contribs; // Mapping to manage donation information
uint public contribId;
address public owner;
uint public totalAmount; // Total amount of donation
function Donation() public {
owner = msg.sender;
}
// Membership registration
function register(string _name, uint _age, string _telNo) public {
Member storage mm = members[memberId];
mm.addr = msg.sender;
mm.name = _name;
mm.age = _age;
mm.telNo = _telNo;
memberId++;
}
// Members send donations
function donate() payable public {
contribs[contribId] = Contribution(msg.sender, msg.value);
totalAmount += msg.value;
contribId++;
}
// Check donation total kept in contract account
function checkBalance() constant public returns(uint) {
return this.balance;
}
// Send donation total from contract account to owner account
function collect() public returns (bool) {
require(msg.sender == owner);
require(totalAmount > 0);
owner.transfer(this.balance);
totalAmount = 0;
return true;
}
}
###1. コントラクトのデプロイ
アカウント 0xca3...a733c を選択し、[Create]をクリックしコントラクトをデプロイします。
###2. メンバー登録
アカウント 0x147...c160c を選択し、[register]ボタンの右にある入力欄に "日本太郎",35,"03-1234-5678" と入力し、[register]ボタンをクリックします。
###3. 構造体の各メンバ変数の値をデバッガで確認
本トランザクション実行後、ターミナルに表示されたメッセージの[Detail]ボタンをクリックし、トランザクション詳細を確認後、[Debug]ボタンをクリックしデバッガを開きます。
[Step over forward]ボタンをクリックしステップ実行を開始します。その過程で、[Solodity state]欄の[members:mapping], 次いで[struct Donation.Member] のドリルダウンボタンをクリックし展開すると、下図の様に構造体の各メンバ変数に順次値が設定されて行く様子が確認できます。
上図のターミナル部分に表示されているトランザクション詳細の[transaction cost]欄に、本トランザクションで 126552wei のGasが消費されたことが示されています。アカウント 0x147...c160c の残高をみてみると確かに 126552wei 減っていることが確認できます。
###4. 寄付金を送金
メンバー登録を済ませたアカウント保持者が、寄付金をコントラクトアカウントへ送金するトランザクションをシミュレートします。
アカウント 0x147...c160c を10ether を選択し、Value欄には 10 を入力します。通貨単位は ether を選択します。
ターミナルにメッセージが表示されるので、[Detail]ボタンをクリックしトランザクション詳細を見てみます。[Value]欄に 10ether が表示されています。
本トランザクションで 102702wei のGasが消費されたことも分かります。アカウント 0x147...c160c の残高を再度見てみると、メンバー登録時消費された 126552wei と合わせ、合計 229254wei、当初の 100ether より減っていることが確認できます。
また、[checkBalance]ボタン、[totalAmount]ボタンをクリックすると、コントラクトアカウントの残高、変数totalAmountの値、共に 10ether となっており、本送金トランザクションがコントラクトで受理されたことが確認できます。
###4. 2人目のメンバー登録と寄付金送金
[Run]画面に戻り、アカウント 0x4b0...4d2db を選択し、2人目のメンバーを "日本花子",32,"03-2345-6789" で登録し、続いて 20ether をコントラクトアカウントへ送金します。
デバッガを起動し [Jump out]ボタンをクリックすると、関数内のステップ実行を最終ステップまで行ってくれますので、それを実行後、各構造体を展開すると下図のようにコントラクトの全体の状態を俯瞰することができます。
デバッガが示す変数totalAmountの値は 30ether となっており、[checkBalance]ボタンをクリックして得られるコントラクトアカウントの残高も 30ether となっていることが確認できます。
###5. 寄付金残高をオーナーアカウントへ送金
コントラクトアカウントにある寄付金残高をオーナーアカウントへ送金します。このトランザクションは、本コントラクトのオーナーからしか実行できません。
オーナーアカウント 0xca3...a733c を選択し、[collect]ボタンをクリックします。
トランザクション詳細を見ると、本トランザクション実行で 20084wei のGasが消費されたことが分かります。
本トランザクション実行後のオーナーアカウント 0xca3...a733c の残高を見てみると 129.99999999999926241etherとなっています。
これは、本トランザクション実行前の残高に、コントラクトアカウントから振替え送金された 30ether を加え、そこから本トランザクションで消費されたGas 20084weiを引いたものであることが確認できます。
変数totalAmountの値、コントラクトアカウントの残高も以下の通り 0 となっており、コントラクトアカウントからオーナーアカウントに 30ether が確かに送金されたことが確認できます。
#[3] おわりに
Remix JavaScript VM は、若干のくせはあるが、それを理解した上で使えば、gethターミナルと接続はせずにRemix単独でエデット、デバックから、Ethereum の送金シミュレーションまで一連で行うことができ、開発効率も大いに高まるツールかと感じました。
参考書籍:「堅牢なスマートコントラクト開発のためのブロックチェーン[技術]入門」田篭照博著 株式会社技術評論社 2017/10