Easiest Blockchain~簡単 ブロックチェーン・Cloudの勉強ブログ~

ブロックチェーン技術の勉強のために学んだことをシェアしていきます。ブロックチェーン技術の基礎から応用まで幅広く勉強していきます。

Truffleフレームワークを利用したサンプル「MetaCoin」実装

こんにちはmiyaです、今回もブロックチェーンの話です。

前回までの記事でブロックチェーンを利用した投票アプリを紹介しましたが、
少し前に公式サンプルとして公開されているMetaCoinを利用してコントラクトを実装する勉強をしました。
※初めての方でもわかりやすいように細かい、難しい箇所は省いています。

今回はその際の手順を忘れないように、メモとしてここに記したいと思います。
参考にしていただけたら嬉しいです。

事前にnpmを利用してtruffleをインストールしておいてください。
コマンドは

npm install -g truffle

それでは早速取り掛かりましょう。
まずは適当にフォルダを作成して移動してください。

mkdir metacoin
cd metacoin

次にtruffle unboxコマンドを利用してMetaCoinサンプルをローカル環境に展開します。

truffle unbox metacoin

以下のように展開されます。

f:id:miya_blockchain:20180619202434p:plain
truffle unbox コマンド実行後のフォルダ内

contractディレクトリにいくつかsolファイルが展開されます。
まずはMetaCoin.solから見ていきましょう。

pragma solidity ^0.4.18;

import "./ConvertLib.sol";

contract MetaCoin {
        //1
	mapping (address => uint) balances;

        //2
	event Transfer(address indexed _from, address indexed _to, uint256 _value);

        //3
	constructor() public {
		balances[tx.origin] = 10000;
	}
        
        //4
	function sendCoin(address receiver, uint amount) public returns(bool sufficient) {
		if (balances[msg.sender] < amount) return false;
		balances[msg.sender] -= amount;
		balances[receiver] += amount;
		emit Transfer(msg.sender, receiver, amount);
		return true;
	}

        //5
	function getBalanceInEth(address addr) public view returns(uint){
		return ConvertLib.convert(getBalance(addr),2);
	}

        //6
	function getBalance(address addr) public view returns(uint) {
		return balances[addr];
	}
}

一番初めにsolidityのバージョンをpragmaにより宣言しておきます。
二文目では同ディレクトリにあるConvertLib.solを読み込みます。

//1
最初のmapping型のbalancesは保持しているコインの量を保持します。
keyがaddressとなりvalueがuintとなっています。
この変数はブロックチェーン外のstate treeに保存されます。

//2
event型のTransferは送金に関して記録します。
_to(送信先)、_from(送信元)そして、_value(金額)を記録し、追跡が可能にします。

//3
contract Metacoinが展開されたときに一度だけ実行され初期化を行うコントラクタです。
あらかじめ10000コインを//1で宣言したbalances変数に入れます。
今回はコントラクトを最初に作成したアカウントは最初から10000コインを入手することになります。

//4
sendCoin関数では送り先アドレスと送る量(金額)を因数として取ります。
実行アカウントのbalancesを確認し、送金額がbalancesを上回るとfalseを返します。
送金額が適正であれば実行アドレスの残高を減らし、送金先アドレスの残高を増やします。
最後にTransferイベントを実行し、metacoinの増減をログに記録します。
処理が完了するとtrueを返します。

//5,//6
アドレス型のaddrを因数としbalancesの所持するコインの量をuintで返します。

ではコンパイルしてみましょう。

まずはインストールしたtruffle develop環境に入りましょう。
そしてコンパイルをします。

truffle develop
>compile

次にデプロイ(ここではマイグレーション)を行います。

>migrate

マイグレーションにが完了すると以下のようなログが表示されます

Using network 'develop'.

Running migration: 1_initial_migration.js
  Deploying Migrations...
  ... 0xcf319e5f024939440825e162cd34ad29e6cc1b161e1bfb2bad3eb2d8a33035f0
  Migrations: 0x8cdaf0cd259887258bc13a92c0a6da92698644c0
Saving successful migration to network...
  ... 0xd7bc86d31bee32fa3988f1c1eabce403a1b5d570340a3a9cdba53a472ee8c956
Saving artifacts...
Running migration: 2_deploy_contracts.js
  Deploying ConvertLib...
  ... 0x509de9e5315657a7a7771db66124991bc1e8d746dd4feffb0f855bcc1a1b5693
  ConvertLib: 0x345ca3e014aaf5dca488057592ee47305d9b3e10
  Linking ConvertLib to MetaCoin
  Deploying MetaCoin...
  ... 0xffbebbe0f06d016e9d9bf3d35e5fbcb743dc564dabcc330b117d1e04137bf21b
  MetaCoin: 0xf25186b5081ff5ce73482ad761db0eb0d25abfbf
Saving successful migration to network...
  ... 0x059cf1bbc372b9348ce487de910358801bbbd1c89182853439bec0afaee6c7db
Saving artifacts...

最初の一文はtruffle developというネットワーク(ブロックチェーン)を使用しているという意味です。

MetaCoin: 0xf25186b5081ff5ce73482ad761db0eb0d25abfbf

ここのアドレスがデプロイされたMetaCoinのアドレスです。
このアドレスはこの後使います。

それではデプロイしたMetaCoinを実行して、確認していきましょう。
まずはMetaCoinを適当な変数に入れて呼び出しやすくします。

truffle(develop)> coin = MetaCoin.at("0xf25186b5081ff5ce73482ad761db0eb0d25abfbf")

次にgetBalance()をつかって所持しているコインを確認します。
accounts[0]はtruffleを起動したときに自動で割り当てられるアドレスの1つ目を指します。
accounts[0]がデフォルトで設定されているため、最初にコントラクトを起動してコインを受け取るのはこのアカウントです。

truffle(develop)> coin.getBalance(web3.eth.accounts[0])
BigNumber { s: 1, e: 4, c: [ 10000 ] }

すると上記のように10000コイン所持していることがわかります。
さきほどの//3で指定したコントラクトが動いたということが確認できます。

次に2つ目のアカウントも確認してみましょう。

truffle(develop)> coin.getBalance(web3.eth.accounts[1])
BigNumber { s: 1, e: 0, c: [ 0 ] }

二つ目のアカウントはコインを所持していないことがわかります。
それではアカウント[0]からアカウント[1]に送金を行ってみましょう。
ここでは100コイン送ってみます。

truffle(develop)>coin.sendCoin(web3.eth.accounts[1], 100)

このtruffle developのブロックチェーンではすぐに結果が反映されますが、実際のブロックチェーンではマイニングが行われブロックに取り込まれるまで時間がかかります。
では送金がされたか確認します。

truffle(develop)> coin.getBalance(web3.eth.accounts[0])
BigNumber { s: 1, e: 3, c: [ 9900 ] }
truffle(develop)> coin.getBalance(web3.eth.accounts[1])
BigNumber { s: 1, e: 2, c: [ 100 ] }

アカウント[0]はマイナス100コインで9900コイン
アカウント[1]はプラス100コインで100コインとなっています。
無事送金ができました。

最後に所持数を超えるコインを送金したらどうなるでしょうか?

truffle(develop)>coin.sendCoin(web3.eth.accounts[1], 10000)

送金が成功したときと比べると違ったログが表示されると思います。

成功時

{ tx: '0x3e9b29cf4058a9550192186c13514e41082ee9fab869b7d6ac8e2366ee78ba3a',
  receipt:
   { transactionHash: '0x3e9b29cf4058a9550192186c13514e41082ee9fab869b7d6ac8e2366ee78ba3a',
     transactionIndex: 0,
     blockHash: '0x94d2c7b7f0ea6461fc23fc0eacd5cb615b588270ae1c1e3bdfb4991458f052f9',
     blockNumber: 6,
     gasUsed: 50993,
     cumulativeGasUsed: 50993,
     contractAddress: null,
     logs: [ [Object] ],
     status: '0x01',
     logsBloom: '0x00000000000000000000000000000000010000000000000000000010000000000000000000000020000000000000000000000000000000000000000000000000000000000000000010000008000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000010000000000000000000010000000000000000000000000000000000000000010000000002000000000000000000000000000000000000002000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' },
  logs:
   [ { logIndex: 0,
       transactionIndex: 0,
       transactionHash: '0x3e9b29cf4058a9550192186c13514e41082ee9fab869b7d6ac8e2366ee78ba3a',
       blockHash: '0x94d2c7b7f0ea6461fc23fc0eacd5cb615b588270ae1c1e3bdfb4991458f052f9',
       blockNumber: 6,
       address: '0xf25186b5081ff5ce73482ad761db0eb0d25abfbf',
       type: 'mined',
       event: 'Transfer',
       args: [Object] } ] }

失敗時

{ tx: '0x175c04f4e568c865ef3735bc5ffd041b8e03b7832889ac4f9b7547cecdc2c3da',
  receipt:
   { transactionHash: '0x175c04f4e568c865ef3735bc5ffd041b8e03b7832889ac4f9b7547cecdc2c3da',
     transactionIndex: 0,
     blockHash: '0x4042286c595000ee58b9114190b01ee9c88550de15a8f24b3aeae85a3ec6e883',
     blockNumber: 7,
     gasUsed: 23594,
     cumulativeGasUsed: 23594,
     contractAddress: null,
     logs: [],
     status: '0x01',
     logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' },
  logs: [] }

一番最後のlogs:[]の部分がからであることが確認できます。eventは送信されたが、プログラムは実行されなかったという意味です。

一応コインの所持数が変化していないか確認してみましょう。

truffle(develop)> coin.getBalance(web3.eth.accounts[0])
BigNumber { s: 1, e: 3, c: [ 9900 ] }
truffle(develop)> coin.getBalance(web3.eth.accounts[1])
BigNumber { s: 1, e: 2, c: [ 100 ] }

以上の手順でMetaCoinを実装することができます。

どうでしたか?すごく簡単ですよね?
今回は公式サンプルとして公開されているMetaCoinの実装を行いました。

ブロックチェーン中級者以上の方には退屈な内容だったかもしれませんが、
最後まで見ていただきありがとうございました。