今回はSolidityを用いたコントラクトの作り方について説明致します。
開発環境
OS : OSX 10.11.4
Ethereumインストール
Ethereumをインストールしていない方は、インストールしてください。
Consoleで以下のコマンドを叩けばインストールされます。
bash <(curl https://install-geth.ethereum.org -L)
```
その他のインストール方法については[こちら](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum)を参考にしてください。
## Ethereum立ち上げ
続いてEthereumをを立ち上げましょう。今回はテスト用に使うため、Private_netを立ち上げます。
```
geth --networkid "11" --datadir "path_to_directory" --genesis "path_to_genesisjson" console
```
`--networkid`:networkIDを指定します。1,2,3以外の数字を使いましょう。
`--genesis` :genesis blockを指定します。
`--datadir` :databaseのあるディレクトリを指定します。
`console` :consoleを開くために必要です。
[geth command line options](https://github.com/ethereum/go-ethereum/wiki/Command-Line-Options)
## コントラクト作成
早速コントラクトを記述してみましょう。言語はSolidityを使います。
(事前にsolidityコンパイラ(solc)がインストールされているか確認してください。されてない場合は[こちらの記事](http://book.ethereum-jp.net/first_use/contract.html)に沿ってインストールしてください。)
エディタで次のようなコントラクトを記述しましょう。これはEthereum上に独自のコインを作るためのコントラクトです。
```token.sol
contract token {
mapping (address => uint) public coinBalanceOf;
event CoinTransfer(address sender, address receiver, uint amount);
/* Contract初期化のための関数。contract名と同じにする */
function token(uint supply) {
coinBalanceOf[msg.sender] = supply;
}
/* コインを送るための関数 */
function sendCoin(address receiver, uint amount) returns(bool sufficient) {
if (coinBalanceOf[msg.sender] < amount) return false;
coinBalanceOf[msg.sender] -= amount;
coinBalanceOf[receiver] += amount;
CoinTransfer(msg.sender, receiver, amount);
return true;
}
}
```
このコントラクトから改行を削除します。テキストから改行を削除する方法については[こちらの記事](http://linux.just4fun.biz/%E9%80%86%E5%BC%95%E3%81%8DUNIX%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89/%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88%E3%81%8B%E3%82%89%E6%94%B9%E8%A1%8C%E3%82%92%E5%89%8A%E9%99%A4%E3%81%99%E3%82%8B.html)を参考にしてください。
```token_one_line.sol
contract token { mapping (address => uint) public coinBalanceOf; event CoinTransfer(address sender, address receiver, uint amount); /* Contract初期化のための関数。contract名と同じにする */ function token(uint supply) { coinBalanceOf[msg.sender] = supply; } /* コインを送るための関数 */ function sendCoin(address receiver, uint amount) returns(bool sufficient) { if (coinBalanceOf[msg.sender] < amount) return false; coinBalanceOf[msg.sender] -= amount; coinBalanceOf[receiver] += amount; CoinTransfer(msg.sender, receiver, amount); return true; }}
```
### コンパイル
続いて、`solc`を用いてコントラクトをコンパイルします。
```
> var source = "contract token { mapping (address => uint) public coinBalanceOf; event CoinTransfer(address sender, address receiver, uint amount); /* Contract初期化のための関数。contract名と同じにする */ function token(uint supply) { coinBalanceOf[msg.sender] = supply; } /* コインを送るための関数 */ function sendCoin(address receiver, uint amount) returns(bool sufficient) { if (coinBalanceOf[msg.sender] < amount) return false; coinBalanceOf[msg.sender] -= amount; coinBalanceOf[receiver] += amount; CoinTransfer(msg.sender, receiver, amount); return true; }}"
> var compiledSource = eth.compile.solidity(source)
```
これでコンパイルは完了しました。続いてコンパイルしたコードをEthereumのネットワークに送信します。
```
> var abiDefinition = compiledSource.token.info.abiDefinition
> var compiledContract = eth.contract(abiDefinition)
> var supply = 10000
> var contract = compiledContract.new(supply, {from:eth.accounts[0], data: compiledSource.token.code, gas:1000000})
```
これでネットワークへの送信は完了しました。しかしまだマイニングは行われていないので、このコントラクトは承認されていません。そのためaddressもまだ未定です。
```
> contract
{
address: undefined,
transactionHash: "0x09b7b27e8d948b601ef1dd0548c7d33604c2fc7b8ee31d7d8e637f66da302a69"
}
```
マイニングが完了すると以下のようにaddressが付与されます。
```
> contract
{
address: "0xb4ffc81cb032941a6cfd29f1f1817dc76413fae6",
transactionHash: "0x09b7b27e8d948b601ef1dd0548c7d33604c2fc7b8ee31d7d8e637f66da302a69",
CoinTransfer: function(),
allEvents: function(),
coinBalanceOf: function(),
sendCoin: function()
}
```
### コントラクトのメソッドを用いる
コントラクトで定義されているメソッドを呼び出してみましょう。
以下のsendCoinメソッドは引数を2つとります。1つ目は受取主、2つ目は送金額です。
```
> contract.sendCoin.sendTransaction(eth.accounts[1], 1000, {from: eth.accounts[0]})
I0511 22:09:00.939817 8397 xeth.go:1028] Tx(0xcd1003befe323aa1cdaee4e81e032b07cfe70fffc33400f46d033dc86cf4c4a1) to: 0xb4ffc81cb032941a6cfd29f1f1817dc76413fae6
"0xcd1003befe323aa1cdaee4e81e032b07cfe70fffc33400f46d033dc86cf4c4a1"
```
メソッドがネットワークに送信されました。しかしまだマイニングされていないので、eth.accounts[0]の残高は10,000、eth.accounts[1]の残高は0のままです。
```
> contract.coinBalanceOf(eth.accounts[0])
10000
> contract.coinBalanceOf(eth.accounts[1])
0
```
マイニングを行います。すると残高が以下のように変化します。
```
> contract.coinBalanceOf(eth.accounts[0])
9000
> contract.coinBalanceOf(eth.accounts[1])
1000
```
以上がコントラクト内のメソッドの呼び出し方です。
### 外部からコントラクトにアクセスする
第三者がコントラクトにアクセスするには`AbiDefinition`と`address`が必要です。今回の場合は以下になります。
```
> abiDefinition
[{
constant: false,
inputs: [{
name: "receiver",
type: "address"
}, {
name: "amount",
type: "uint256"
}],
name: "sendCoin",
outputs: [{
name: "sufficient",
type: "bool"
}],
type: "function"
}, {
constant: true,
inputs: [{
name: "",
type: "address"
}],
name: "coinBalanceOf",
outputs: [{
name: "",
type: "uint256"
}],
type: "function"
}, {
inputs: [{
name: "supply",
type: "uint256"
}],
type: "constructor"
}, {
anonymous: false,
inputs: [{
indexed: false,
name: "sender",
type: "address"
}, {
indexed: false,
name: "receiver",
type: "address"
}, {
indexed: false,
name: "amount",
type: "uint256"
}],
name: "CoinTransfer",
type: "event"
}]
```
```
> contract.address
"0xb4ffc81cb032941a6cfd29f1f1817dc76413fae6"
```
上記のデータを用いて、コントラクトにアクセスする変数を作成します。
```
> var theContract = eth.contract(abiDefinition).at(contract.address)
> theContract
{
address: "0xb4ffc81cb032941a6cfd29f1f1817dc76413fae6",
CoinTransfer: function(),
allEvents: function(),
coinBalanceOf: function(),
sendCoin: function()
}
```
これで第三者がコントラクトにアクセス出来るようになりました。
### まとめ
以上Solidityを使ったコントラクトの作成方法でした。次回はSolidityを用いたデリバティブの実装方法についてご説明致します。
### 参考記事
https://ethereum.gitbooks.io/frontier-guide/content/ether_transfer.html
http://book.ethereum-jp.net/first_use/contract.html
https://solidity.readthedocs.io/en/latest/index.html
http://linux.just4fun.biz/%E9%80%86%E5%BC%95%E3%81%8DUNIX%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89/%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88%E3%81%8B%E3%82%89%E6%94%B9%E8%A1%8C%E3%82%92%E5%89%8A%E9%99%A4%E3%81%99%E3%82%8B.html