目次

ethereumブロックチェーンの構築(Ganache編)

Ganacheを使用してethereumブロックチェーンを構築し、gethでブロックチェーンに接続して送金、スマートコントラクトを実行するハンズオンです。

Ganacheとは?

Ganache は ethereum に対応した開発用ノード(ブロックチェーン)です。 アプリケーションをインストールするだけで、すぐに ethereum ブロックチェーンを利用した開発が始められます。 また、デフォルトで採掘が自動的に行われますので、必要なトランザクションは自動的に処理されます。 権限についてもデフォルトでゆるく設定されており、アカウントのアンロック(eth.unlockAccount)しなくても送金やガス代の支払いができるようになっています。

インストール

Ganacheはアプリケーションとして配布されており、アプリケーションをインストールするだけで簡単にethereumブロックチェーンを開発できます。 Linux、macOS、Windows 用のパッケージが公開さています。

お使いの環境に合わせて Ganache パッケージをダウンロードし、インストールしてください。

起動

Ganache が起動したら「QUICKSTART」をクリックしてください

すると、以下のようなウィンドウが立ち上がります。

起動した段階で既に10アカウントが作成されており、それぞれに 100 ETH 割り振られています。

後ほど、この Ganache の ethereum ブロックチェーンに geth でアクセスする際、このウィンドウにある「RPC SERVER URL」を使用します。(下図参照)

各アカウントの PRIVATE KEY は以下の手順で確認できます。

geth attach でブロックチェーンにコンソール接続する

「起動」の手順で確認した「RPC SERVER URL」を使用して、以下のようにコンソール接続することができます。

geth --nodiscover attach http://127.0.0.1:7545

送金

送金前

> eth.getBalance(eth.accounts[0])
100000000000000000000
> eth.getBalance(eth.accounts[1])
100000000000000000000

送金

> eth.sendTransaction({from: eth.accounts[0], to: eth.accounts[1], value: web3.toWei(5, "ether")})

送金後(eth.accounts[0] についてはガス代分も少なくなっている)

> eth.getBalance(eth.accounts[0])
94999580000000000000
> eth.getBalance(eth.accounts[1])
105000000000000000000

Ganache でも残金やトランザクションが確認できる。

スマートコントラクト

コンパイラ(solc)のインストール

solidity releases でコンパイラーが公開されていますので、ダウンロードします。

(上記で配布されているのでコンパイラの実行ファイルでので、解凍やインストールは必要なく、すぐにコンパイラを利用できます。)

実装

内部にカウンターをもち、カウンターの値を取得する(get)と、カウンターの値を1増やす(inc)するスマートコントラクトを実装します。

Counter.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
 
contract Counter {
    uint public count;
 
    // Function to get the current count
    function get() public view returns (uint) {
        return count;
    }
 
    // Function to increment count by 1
    function inc() public {
        count += 1;
    }
 
    // Function to decrement count by 1
    function dec() public {
        count -= 1;
    }
}

コンパイル

スマートコントラクトのコンパイルはコマンドラインから solc を使用して実行します。

solc-windows.exe --abi --bin Counter.sol

コマンドの意味

上記コンパイルを実行すると、以下のように「Binary」と「Contract JSON API」が出力されます。

今後利用するときに「Binary」の先頭に「0x」を付ける必要がありますので、「0x」を付けてメモしておいてください。

(例) 608060… → 0x608060…

コンパイル実行結果
======= Counter.sol:Counter =======
Binary:
608060405234801561001057600080fd5b50610209806100206000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c806306661abd14610051578063371303c01461006f5780636d4ce63c14610079578063b3bcfa8214610097575b600080fd5b6100596100a1565b60405161006691906100ff565b60405180910390f35b6100776100a7565b005b6100816100c2565b60405161008e91906100ff565b60405180910390f35b61009f6100cb565b005b60005481565b60016000808282546100b99190610149565b92505081905550565b60008054905090565b60016000808282546100dd919061019f565b92505081905550565b6000819050919050565b6100f9816100e6565b82525050565b600060208201905061011460008301846100f0565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610154826100e6565b915061015f836100e6565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156101945761019361011a565b5b828201905092915050565b60006101aa826100e6565b91506101b5836100e6565b9250828210156101c8576101c761011a565b5b82820390509291505056fea26469706673582212207a90fc8477fe04ed648a217280498cc223d36737bb62448b5f15b12f4496447a64736f6c634300080d0033
Contract JSON ABI
[{"inputs":[],"name":"count","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dec","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"get","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"inc","outputs":[],"stateMutability":"nonpayable","type":"function"}]

デプロイ

スマートコントラクトのデプロイは geth console 上で行います。

geth --datadir private_network --nodiscover console
> var bin = "0x608060405234801561001057600080fd5b50610209806100206000396000f3fe608060405234801561001057600080fd5
b506004361061004c5760003560e01c806306661abd14610051578063371303c01461006f5780636d4ce63c14610079578063b3bcfa82146
10097575b600080fd5b6100596100a1565b60405161006691906100ff565b60405180910390f35b6100776100a7565b005b6100816100c25
65b60405161008e91906100ff565b60405180910390f35b61009f6100cb565b005b60005481565b60016000808282546100b99190610149565b92505081905550565b60008054905090565b60016000808282546100dd919061019f565b92505081905550565b6000819050919050565b6100f9816100e6565b82525050565b600060208201905061011460008301846100f0565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610154826100e6565b915061015f836100e6565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156101945761019361011a565b5b828201905092915050565b60006101aa826100e6565b91506101b5836100e6565b9250828210156101c8576101c761011a565b5b82820390509291505056fea26469706673582212207a90fc8477fe04ed648a217280498cc223d36737bb62448b5f15b12f4496447a64736f6c634300080d0033"
undefined
> var abi = [{"inputs":[],"name":"count","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dec","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"get","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"inc","outputs":[],"stateMutability":"nonpayable","type":"function"}]
undefined
> var contract = eth.contract(abi)
undefined
> var myContract = contract.new({ from: eth.accounts[0], data: bin, gas: '1000000'})
undefined

上記の作業でデプロイの「登録」は完了しました。 デプロイ処理の実行は採掘時に行われます。Ganache では自動的に採掘が行われているため、ほぼ即時にデプロイが完了します。

デプロイ処理が完了しているため「Contract Account」がaddressに付与されています。

> miner.start()
null
> myContract
{
  abi: [{
      inputs: [],
      name: "count",
      outputs: [{...}],
      stateMutability: "view",
      type: "function"
  }, {
      inputs: [],
      name: "dec",
      outputs: [],
      stateMutability: "nonpayable",
      type: "function"
  }, {
      inputs: [],
      name: "get",
      outputs: [{...}],
      stateMutability: "view",
      type: "function"
  }, {
      inputs: [],
      name: "inc",
      outputs: [],
      stateMutability: "nonpayable",
      type: "function"
  }],
  address: "0x642851bf1760f1aaf0a7a3de8abc646ccd76699c",
  transactionHash: "0x621b3dffcaf4c656ab69e65bfa0fccd27bc3f61e65440ba6b3d392c0df2bc117",
  allEvents: function bound(),
  count: function bound(),
  dec: function bound(),
  get: function bound(),
  inc: function bound()
}
> miner.stop()

実行

スマートコントラクトを呼び出してカウンターの値を1増やしてみます。 この処理はトランザクションを伴うため、採掘時に実行されます。

> myContract.inc.sendTransaction({from:eth.accounts[0]})
"0xd254b4b2e830350da787720e9b037e1e27cc11ad93452ff79345e88d716b9cf2"

カウンターの値を参照してみましょう。

採掘前後で確認してみます。

get でカウンターの値を参照する。

> myContract.get.call()
0
> myContract.get.call()
1
> miner.stop()

ガス代(手数料)の確認

トランザクションの実行にはガス代と呼ばれる手数料がかかります。 そのため、トランザクションを発行したaccount[0]は想定よりも少しだけコインが減っているはずです。

確認してみましょう。

> eth.getBalance(eth.accounts[0])
1.496999999999998206439e+21