はじめに
前回(ブロックチェーンアプリ開発基礎の基礎)Gethの基本的な使い方が分かったと思うので、今回は実際にコントラクト指向言語であるSolidityを用いてスマートコントラクトの開発を行ってみたいと思います。
基本的には前回の続きになります。今回必要な部分は以下にまとめておきますが、分からない部分があれば適宜前回記事を参照してください。
最低限必要な準備
$ brew tap ethereum/ethereum
$ brew install ethereum
$ mkdir ~/private_net
{
"config": {
"chainId": 42,
"homesteadBlock": 0,
"eip155Block": 0,
"eip158Block": 0
},
"nonce": "0x0000000000000042",
"timestamp": "0x0",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0x8000000",
"difficulty": "0x100",
"mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"coinbase": "0x3333333333333333333333333333333333333333",
"alloc": {}
}
※ genesis.jsonは~/private_netに置いておいてください。
$ geth --datadir ~/private_net/ init ~/private_net/genesis.json
$ geth --networkid "10" --nodiscover --datadir ~/private_net --rpc --rpcaddr "localhost" --rpcport "8545" --rpccorsdomain "*" --rpcapi "eth,net,web3,personal" console 2>> ~/private_net/err.log
> personal.newAccount("sender")
"0xfb814cf6d7deae6d68d9dea8f8cef23b9ce63e5f"
> personal.newAccount("receiver")
"0x9e495cc88b7d99c28918efb641f564f896eec6f5"
> exit
開発に入る前に、スマートコントラクトの基本的な事項を確認しておきます。
スマートコントラクトとは
スマートコントラクトとは、本来、**「自動的に契約を履行する仕組み」**を指します。
よく例に挙げられるのが自動販売機で、ユーザーが欲しいジュースの代金を自動販売機に投入し、購入したいジュースのボタンを押すと自動的にジュースが出てくるという仕組みになっています。
このような仕組みをさらに応用しブロックチェーン上でプログラムによって自動で契約を履行しようという取り組みが現在非常に注目されています。ブロックチェーンとスマートコントラクトを組み合わせるとブロックチェーンの特性により、次のようなメリットがあります。
- 契約に関する不正が減る
- 契約で発生する手数料が減る
- 契約が安全に履行される
契約に関する不正が減るというのは、契約の改ざんが不可能であることからきています。また、仮想通貨などと同様に、仲介者が不在なため契約で発生する手数料も低く抑えられます。さらに、本来、契約は契約相手を信頼することで成立しています。現実にはお金を払ったが品物が届かないというようなことがあり得るわけです。しかし、ブロックチェーン上では自動で契約が履行されるので安全です。
それでは、さっそくスマートコントラクトの開発をおこなっていきましょう。
Solidityのインストール
まずは、Solidityのコンパイラsolcをインストールします。
$ brew tap ethereum/ethereum
$ brew install solidity
$ brew linkapps solidity
インストールが無事終わったかどうかは、
$ solc --version
などで確認することができます。
コントラクトの作成
まずは、スマートコントラクトの内容、すなわち、契約の中身であるコントラクト・コードを書いていきます。
pragma solidity ^0.4.0;
contract Consume {
int money = 100;
function subtract(int i) {
money -= i;
}
function get() returns(int) {
return money;
}
}
今回のコントラクトは、最初の所持金が100でsubtractを呼び出す度に指定の額を減額するというシンプルなものにしておきます。
コントラクトのコンパイル
続いて、ソースコードをコンパイルします。
$ solc --abi --bin Consume.sol
======= Consume.sol:Consume =======
Binary:
60606040526064600055341561001457600080fd5b60db806100226000396000f3006060604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680636d4ce63c14604e578063ae0f6166146074575b600080fd5b3415605857600080fd5b605e6094565b6040518082815260200191505060405180910390f35b3415607e57600080fd5b60926004808035906020019091905050609d565b005b60008054905090565b806000808282540392505081905550505600a165627a7a72305820315d8335a1d79518ac81b964a07822149e11dd39b780ae3e080d70bc06de6b6b0029
Contract JSON ABI
[{"constant":false,"inputs":[],"name":"get","outputs":[{"name":"","type":"int256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"i","type":"int256"}],"name":"subtract","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]
実行結果としてBinaryとContract JSON ABIが得られますが、これらは次章で使います。
コントラクトのデプロイ
デプロイというのは、コントラクトをEthereumネットワークに送信しブロックチェーンに登録するまでの一連の作業です。
まずは、Gethを起動します。Gethを起動するのに最初に作ったアカウントのパスフレーズを求められます。前回に引き続きの方は"sender"です。
$ geth --networkid "10" --nodiscover --datadir ~/private_net --rpc --rpcaddr "localhost" --rpcport "8545" --rpccorsdomain "*" --rpcapi "eth,net,web3,personal" --unlock 0 console 2>> ~/private_net/err.log
次に、コントラクトのアカウントを作成します。binは、Binaryの先頭に0xを付けたもの、abiは、Contract JSON ABIをそのまま使います。
> var bin = "0x60606040526064600055341561001457600080fd5b60db806100226000396000f3006060604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680636d4ce63c14604e578063ae0f6166146074575b600080fd5b3415605857600080fd5b605e6094565b6040518082815260200191505060405180910390f35b3415607e57600080fd5b60926004808035906020019091905050609d565b005b60008054905090565b806000808282540392505081905550505600a165627a7a72305820315d8335a1d79518ac81b964a07822149e11dd39b780ae3e080d70bc06de6b6b0029"
undefined
> var abi = [{"constant":false,"inputs":[],"name":"get","outputs":[{"name":"","type":"int256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"i","type":"int256"}],"name":"subtract","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]
undefined
続いて、コントラクトのオブジェクトを生成し、オブジェクト情報を含んだトランザクションをEthereumネットワークに送信します。
> var contract = eth.contract(abi)
undefined
> var myContract = contract.new({ from: eth.accounts[0], data: bin, gas: 1000000})
undefined
少し、myContractの中身を見ておきましょう。
> myContract
{
abi: [{
constant: false,
inputs: [],
name: "get",
outputs: [{...}],
payable: false,
stateMutability: "nonpayable",
type: "function"
}, {
constant: false,
inputs: [{...}],
name: "subtract",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function"
}],
address: undefined,
transactionHash: "0x46ff4c60ed1c94f7461d7e7b07e772e91dd5016e24f803470ef3545a4bb39e12"
}
この状態では、addressが「undefined」となっており、ブロックチェーンに承認されていないことが分かります。そこで、マイニングをおこなって、ブロックチェーンに承認します。
> miner.start()
null
マイニングをしてしばらくして、myContractの中身を確認するとaddressが表示され、デプロイが成功したことが分かります。
> myContract
{
abi: [{
constant: false,
inputs: [],
name: "get",
outputs: [{...}],
payable: false,
stateMutability: "nonpayable",
type: "function"
}, {
constant: false,
inputs: [{...}],
name: "subtract",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function"
}],
address: "0xdc9fdd0878e4c7fc0964755b4710fb69f880e2d6",
transactionHash: "0x46ff4c60ed1c94f7461d7e7b07e772e91dd5016e24f803470ef3545a4bb39e12",
allEvents: function(),
get: function(),
subtract: function()
}
デプロイに成功したので、マイニングは一旦中止しておきます。
> miner.stop()
true
コントラクトのメソッド
コントラクトのデプロイが終わったので、このコントラクトにアクセスしていきます。まずは、このコントラクトへアクセスするためのオブジェクトを生成します。
> var cnt = eth.contract(myContract.abi).at(myContract.address);
undefined
Ethereumでは、メソッドの呼び出し方には、「Transaction」、**「Call」**の2種類があります。まずは、getメソッドでコントラクト内の変数money(所持金)を呼びだします。特に何の操作も行なってないので、初期値の100が返ってきます。
> cnt.get.call()
100
さきほどは説明しませんでしたが、Callは、ブロックチェーンの状態を変化させないような命令に使い、承認されることなく使用することができますが、Transactionは、ブロックチェーンの状態を変化させるような命令に使うので承認が必要になります。では、subtractを使って、所持金を20減額させてみます。
> cnt.subtract.sendTransaction(20, {from:eth.accounts[0], gas:1000000})
"0xc35bdd3767b4ba6d99dafa2ef52d07ee533a0aaffe1c2325341aeb8a71f15b53"
Transactionは承認が必要なので、マイニングを行います。
> miner.start()
null
しばらくして、getメソッドで所持金を確認してみます。
> cnt.get.call()
80
期待通り、所持金が100から20減額し80になっていることが分かります。Transactionが承認されたことが分かったのでマイニングを止めておきましょう。
> miner.stop()
true
これで、コントラクトのメソッドの基本的な使い方は理解できたと思います。