Solidityによるコントラクトの作成(1) - 基礎

今回はSolidityを用いたコントラクトの作り方について説明致します。

開発環境 OS : OSX 10.11.4

Ethereumインストール

Ethereumをインストールしていない方は、インストールしてください。
Consoleで以下のコマンドを叩けばインストールされます。

bash <(curl https://install-geth.ethereum.org -L)

その他のインストール方法についてはこちらを参考にしてください。

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

コントラクト作成

早速コントラクトを記述してみましょう。言語はSolidityを使います。
(事前にsolidityコンパイラ(solc)がインストールされているか確認してください。されてない場合はこちらの記事に沿ってインストールしてください。)

エディタで次のようなコントラクトを記述しましょう。これはEthereum上に独自のコインを作るためのコントラクトです。

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;
    }
}

このコントラクトから改行を削除します。テキストから改行を削除する方法についてはこちらの記事を参考にしてください。

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

以上がコントラクト内のメソッドの呼び出し方です。

外部からコントラクトにアクセスする

第三者がコントラクトにアクセスするにはAbiDefinitionaddressが必要です。今回の場合は以下になります。

> 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