はじめに
今まではdappの開発を、gethを使ったプライベートネットワークで行っていたが、マシンがしょぼいためかブロック生成に時間がかかり、開発が全然進まなかった。
調べるとTruffleという素晴らしいフレームワークがあるとのことで、早速使ってみることにする。
環境
macOS High Sierra
Truffleのインストール
以下のコマンドでTruffleをインストール
$ npm install truffle -g
プロジェクトの作成
Truffleを使うにはプロジェクトがないと話にならない。
ゼロから作っても良いが、まずはサンプルプロジェクトから始める。
Truffleフレームワークには、Truffle Boxesというものがあり、このサンプルアプリケーションを使ってTruffleの使い方および、dapp開発の流れを学べる。
今回は、トークンを作り、アカウント同士で交換するまでを体験できるMetaCoin boxを使ってみる。
まず、プロジェクト用のディレクトリを用意。
$ mkdir MetaCoin
$ cd MetaCoin/
そして、MetaCoin boxを取り込む
$ truffle unbox metacoin
Downloading...
Unpacking...
Setting up...
Unbox successful. Sweet!
Commands:
Compile contracts: truffle compile
Migrate contracts: truffle migrate
Test contracts: truffle test
成功すると以下のフォルダ構成が作成される。
$ tree
.
├── LICENSE
├── contracts
│ ├── ConvertLib.sol
│ ├── MetaCoin.sol
│ └── Migrations.sol
├── migrations
│ ├── 1_initial_migration.js
│ └── 2_deploy_contracts.js
├── test
│ ├── TestMetacoin.sol
│ └── metacoin.js
├── truffle-config.js
└── truffle.js
3 directories, 10 files
contrats/
はSolidityファイル
migrations/
はデプロイ用のスクリプト
test/
はテスト用のスクリプト
が格納されている。
初めてだし、どんなコントラクトなのか覗いてみる。
pragma solidity ^0.4.18;
import "./ConvertLib.sol";
contract MetaCoin {
mapping (address => uint) balances;
event Transfer(address indexed _from, address indexed _to, uint256 _value);
constructor() public {
balances[tx.origin] = 10000;
}
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;
}
function getBalanceInEth(address addr) public view returns(uint){
return ConvertLib.convert(getBalance(addr),2);
}
function getBalance(address addr) public view returns(uint) {
return balances[addr];
}
}
MetaCoinトークンのコントラクト。
MetaCoinの送信を行うsendCoin
、
MetaCoinとEthの変換を行うgetBalanceInEth
、
MetaCoin残高を返すgetBalance
、
だけの比較的シンプルなコントラクト。
EthとMetaCoinの変換を行う処理は、ConvertLib.sol
でライブラリとして提供されている。
pragma solidity ^0.4.4;
library ConvertLib{
function convert(uint amount,uint conversionRate) public pure returns (uint convertedAmount)
{
return amount * conversionRate;
}
}
Migrations.sol
はMetaCoinコントラクトには関係なく、デプロイの履歴をブロックチェーンに保存してくれるコントラクトのようだ。
詳細は後日調べることにする。
コントラクトのコンパイル
コントラクトのコンパイルもTruffleでできる。
$ truffle compile
Compiling ./contracts/ConvertLib.sol...
Compiling ./contracts/MetaCoin.sol...
Compiling ./contracts/Migrations.sol...
Writing artifacts to ./build/contracts
contracts
以下の.sol
ファイルが自動でコンパイルされ、buildフォルダ以下にartifacts
と呼ばれるコントラクト情報を格納したファイルが生成される。
$ tree
.
├── LICENSE
├── build
│ └── contracts
│ ├── ConvertLib.json
│ ├── MetaCoin.json
│ └── Migrations.json
├── contracts
:
(略)
テスト
コントラクトのテストもTruffleで実施できる。
$ truffle test ./test/TestMetacoin.sol
Using network 'test'.
Compiling ./contracts/ConvertLib.sol...
Compiling ./contracts/MetaCoin.sol...
Compiling ./test/TestMetacoin.sol...
Compiling truffle/Assert.sol...
Compiling truffle/DeployedAddresses.sol...
TestMetacoin
✓ testInitialBalanceUsingDeployedContract (73ms)
✓ testInitialBalanceWithNewMetaCoin (72ms)
2 passing (692ms)
あらかじめ.sol
をコンパイルしておかなくても、必要なファイルはコンパイルされてからテストが実施されるようだ。
javascriptを用いたテストは以下の通り。
$ truffle test ./test/metacoin.js
Using network 'test'.
Contract: MetaCoin
✓ should put 10000 MetaCoin in the first account (50ms)
✓ should call a function that depends on a linked library (56ms)
✓ should send coin correctly (127ms)
3 passing (257ms)
違いは、.sol
の方はコントラクトの関数テスト。.js
の方はネットワーク上でトークンの送受信ができているかのテストだろうか。
テスト用ソースの書き方と、.sol
と.js
のテストの違いについては後日調べる。
デプロイ
Truffleには、ローカルマシンで起動するテストネットワークが備わっている。
本物のEthereumネットワークとは相互作用しないので、安心してテストができる。
デプロイのコマンドを実行。
$ truffle develop
Truffle Develop started at http://127.0.0.1:9545/
Accounts:
(0) 0x627306090abab3a6e1400e9345bc60c78a8bef57
(1) 0xf17f52151ebef6c7334fad080c5704d77216b732
(2) 0xc5fdf4076b8f3a5357c5e395ab970b5b54098fef
(3) 0x821aea9a577a9b44299b9c15c88cf3087f3b5544
(4) 0x0d1d4e623d10f9fba5db95830f7d3839406c6af2
(5) 0x2932b7a2355d6fecc4b5c0b6bd44cc31df247a2e
(6) 0x2191ef87e392377ec08e7c08eb105ef5448eced5
(7) 0x0f4f2ac550a1b4e2280d04c21cea7ebd822934b5
(8) 0x6330a553fc93768f612722bb8c2ec78ac90b3bbc
(9) 0x5aeda56215b167893e80b4fe645ba6d5bab767de
Private Keys:
(0) c87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3
(1) ae6ae8e5ccbfb04590405997ee2d52d2b330726137b875053c36d94e974d162f
(2) 0dbbe8e4ae425a6d2687f1a7e3ba17bc98c673636790f1b8ad91193c05875ef1
(3) c88b703fb08cbea894b6aeff5a544fb92e78a18e19814cd85da83b71f772aa6c
(4) 388c684f0ba1ef5017716adb5d21a053ea8e90277d0868337519f97bede61418
(5) 659cbb0e2411a44db63778987b1e22153c086a95eb6b18bdf89de078917abc63
(6) 82d052c865f5763aad42add438569276c00d3d88a2d062d36b2bae914d58b8c8
(7) aa3680d5d48a8283413f7a108367c7299ca73f553735860a87b08f39395618b7
(8) 0f62d96d6675f32685bbdb8ac13cda7c23436f63efbb9d07700d8669ff12b7c4
(9) 8d5366123cb560bb606379f90a0bfd4769eecc0557f1b362dcae9012b548b1e5
Mnemonic: candy maple cake sugar pudding cream honey rich smooth crumble sweet treat
⚠️ Important ⚠️ : This mnemonic was created for you by Truffle. It is not secure.
Ensure you do not use it on production blockchains, or else you risk losing funds.
truffle(develop)>
10個のアカウントとそのprivate keyが自動で発行される、Truffleのdevelopプロンプトで操作できるようになる。
ここでは、いままでコマンドに付けてきたtruffle
コマンドが不要になる。例えば、
$ truffle compile
と打っていたものが、
truffle(develop)> compile
で実行できる。
migrateしてみる。
truffle(develop)> migrate
Using network 'develop'.
Running migration: 1_initial_migration.js
Deploying Migrations...
... 0xb674c966783b1c021c542dc9114fc928df0b0925b0d65a9fbc10dd79b61158b4
Migrations: 0x8cdaf0cd259887258bc13a92c0a6da92698644c0
Saving successful migration to network...
... 0xd7bc86d31bee32fa3988f1c1eabce403a1b5d570340a3a9cdba53a472ee8c956
Saving artifacts...
Running migration: 2_deploy_contracts.js
Deploying ConvertLib...
... 0xedec131c49eb231aaa58c9e7bd1cda0a63452ea053bc986fb211edba4e646920
ConvertLib: 0x345ca3e014aaf5dca488057592ee47305d9b3e10
Linking ConvertLib to MetaCoin
Deploying MetaCoin...
... 0x33f1f0adcdd957fd78b78bcf1e5be5caae6a929b19c77267476a0d6f6db550be
MetaCoin: 0xf25186b5081ff5ce73482ad761db0eb0d25abfbf
Saving successful migration to network...
... 0x059cf1bbc372b9348ce487de910358801bbbd1c89182853439bec0afaee6c7db
Saving artifacts...
migration用のスクリプトが実行され、コントラクトのアドレスが取得できた。
MetaCoin: 0xf25186b5081ff5ce73482ad761db0eb0d25abfbf
トークンの送信
デプロイできたので、コントラクトのメソッドを呼び出してみる。
getBalance
の呼び出し:
truffle(develop)> MetaCoin.deployed().then(function(instance){return instance.getBalance(web3.eth.accounts[0]);}).then(function(value){return value.toNumber()});
10000
truffle(develop)> MetaCoin.deployed().then(function(instance){return instance.getBalance(web3.eth.accounts[1]);}).then(function(value){return value.toNumber()});
0
account[0]
が10000 MetaCoin
。他のアカウントが0 MetaCoin
持っていることが確認できた。
getBalanceInEth
の呼び出し:
truffle(develop)> MetaCoin.deployed().then(function(instance){return instance.getBalanceInEth(web3.eth.accounts[0]);}).then(function(value){return value.toNumber()});
20000
今回は、1 MetaCoin = 2 Eth
としたので、20000 Eth
持っていることが確認できた。
sendCoin
の呼び出し:
account[0]
からaccount[1]
へ、500 MetaCoin
送ってみる。
truffle(develop)> MetaCoin.deployed().then(function(instance){return instance.sendCoin(web3.eth.accounts[1], 500);});
{ tx:
'0xa6c465de6c68c8fa29b1a7020006715d2749381421376d72ae1a1d2f6803a695',
receipt:
{ transactionHash:
'0xa6c465de6c68c8fa29b1a7020006715d2749381421376d72ae1a1d2f6803a695',
transactionIndex: 0,
blockHash:
'0xc4eafb83f144ee3d93249895e38db546084842e1645ce4ab3126dc478029e074',
blockNumber: 6,
gasUsed: 51057,
cumulativeGasUsed: 51057,
contractAddress: null,
logs: [ [Object] ],
status: '0x01',
logsBloom:
'0x00000000000000000000000000000000010000000000000000000010000000000000000000000020000000000000000000000000000000000000000000000000000000000000000010000008000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000010000000000000000000010000000000000000000000000000000000000000010000000002000000000000000000000000000000000000002000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' },
logs:
[ { logIndex: 0,
transactionIndex: 0,
transactionHash:
'0xa6c465de6c68c8fa29b1a7020006715d2749381421376d72ae1a1d2f6803a695',
blockHash:
'0xc4eafb83f144ee3d93249895e38db546084842e1645ce4ab3126dc478029e074',
blockNumber: 6,
address: '0xf25186b5081ff5ce73482ad761db0eb0d25abfbf',
type: 'mined',
event: 'Transfer',
args: [Object] } ] }
トランザクション情報が出力され、ブロックにも記録されていることが確認できる。
truffle(develop)> MetaCoin.deployed().then(function(instance){return instance.getBalance(web3.eth.accounts[0]);}).then(function(value){return value.toNumber()});
9500
truffle(develop)> MetaCoin.deployed().then(function(instance){return instance.getBalance(web3.eth.accounts[1]);}).then(function(value){return value.toNumber()});
500
アカウントの残高を確認すると、確かにaccount[0]
から500 MetaCoin
減り、account[1]
に500 MetaCoin
増えていることが確認できた。
さいごに
gethでメソッドの実行確認をしていたのが残念だったくらい快適に開発ができる。
いくつか発展的なチュートリアルをこなしてから、自分のdapp開発にも使ってみよう。
宿題
-
migration.sol
が何をしているか -
テストコード
.sol
と.js
の違い → 【Ethereum】Truffleによるコントラクトのテスト - テストコードの書き方 → 【Ethereum】Truffleによるコントラクトのテスト
-
tx.origin
とは → 【Solidity】tx.originとmsg.senderの違い