LoginSignup
2
1

More than 1 year has passed since last update.

[前回] Web3.0検証(8)-ブロックチェーンのプライベートネットワーク

はじめに

Blockchainプライベートネットワークを構築できました。
今回は、スマートコントラクトを用いたBlockchain内部構造の検証です。

おさらい

  • Web3.0の分散型アプリ(DApp)のアーキテクチャ(Ethereumの例)

image.png

  • スマートコントラクト(Smart Contract)とは

    • Ethereum仮想マシン(EVM)上で実行されるブロックチェーン専用プログラム
    • ブロックチェーン上のトランザクション(取引)によりトリガー、事前定義ルールに従って自動実行
    • スマートコントラクトの定義は、Solidity(構文はJava Scriptライク)言語を用いる(Ethereumの場合)
  • コントラクト・コードとは

    • Ethereumネットワーク上で、EVM Code(Ethereum Virtual Machine Code)と呼ばれるバイトコードの形式で記述され処理される
      • 今回の検証は、Ethereumパブリックネットワークではなく、ローカルのBlockchainプライベートネットワークを使用
    • コントラクト・コードは、Solidityなど可読性の高い高水準言語で記述し、EVM Codeコンパイル(コンパイラはsolc)

検証シナリオ

スマートコントラクトを作成し実行します。
手順は、Ethereum: スマートコントラクトを作成し実行するを参考にしました。

  • スマートコントラクトの作成から実行までの流れ
    • コントラクト・コードの作成
      • Solidity言語でスマートコントラクトの内容を記述したコントラクト・コードを作成
    • コントラクト・コードのコンパイル
      • コントラクト・コードを、solcを使ってコンパイル
    • Contractアカウントを生成
      • コンパイル済みのコードをトランザクションに付加し、Blockchainプライベートネットワーク送信
      • トランザクションを受信掘者が、トランザクションをブロックチェーンに登録
      • Contractアカウントが生成され、アドレスが発行される
    • スマートコントラクトへのアクセスと実行
      • ユーザーが、Contractアカウントへトランザクションを発行すると、スマートコントラクトが実行される

検証環境準備

環境情報

CPU: Intel(R) Core(TM) 3.30GHz(4コア8スレッド)
メモリ: 16 GB
OS: Ubuntu 20.04 LTS
Geth: 1.10.18

ちなみに、Ubuntuは、Windows 10で以下のように管理者権限で導入しました。

> wsl --install -d Ubuntu-20.04

Solidityコンパイラsolcのインストール

$ sudo add-apt-repository ppa:ethereum/ethereum
$ sudo apt-get update
$ sudo apt-get install solc
$ solc --version
solc, the solidity compiler commandline interface
Version: 0.8.13+commit.abaa5c0e.Linux.g++

Gethを起動

  • Genesisファイルのconfigに3行追記
    "petersburgBlock": 0,
    "istanbulBlock": 0,
    "constantinopleBlock": 0

完全なGenesis設定ファイルの例

$ cat $HOME/eth_private_net/TestGenesis.json
{
  "config": {
    "chainId": 1234,
    "homesteadBlock": 0,
    "petersburgBlock": 0,
    "eip150Block": 0,
    "eip155Block": 0,
    "eip158Block": 0,
    "byzantiumBlock": 0,
    "istanbulBlock": 0,
    "constantinopleBlock": 0
  },
  "difficulty": "0x4000",
  "gasLimit": "0x8000000",
  "alloc": {}
}
  • ノード二つを初期化
$ geth --datadir $HOME/eth_private_net/data1 init $HOME/eth_private_net/TestGenesis.json
$ geth --datadir $HOME/eth_private_net/data2 init $HOME/eth_private_net/TestGenesis.json
  • ノード二つをバックグラウンド起動
$ nohup geth --networkid 1234 --port 30303 --nodiscover --datadir $HOME/eth_private_net/data1 2>> $HOME/eth_private_net/data1/geth_error.log &
$ nohup geth --networkid 1234 --port 30304 --nodiscover --datadir $HOME/eth_private_net/data2 2>> $HOME/eth_private_net/data2/geth_error.log &
  • ノード1にアタッチ
$ geth attach $HOME/eth_private_net/data1/geth.ipc

アカウントの残高を増やす

検証にはgasが必要です。

  • アカウントとコインベースを確認
> eth.accounts
["0x92aad50ffefa0cf6a15ad9b924c98abd3296e05a"]
> eth.coinbase
"0x92aad50ffefa0cf6a15ad9b924c98abd3296e05a"
  • 残高を確認
> eth.getBalance(eth.accounts[0])
0
  • マイニングスタート
> miner.start()
null
> eth.mining
true
> eth.blockNumber
0
  • しばらく待って、再度GenesisブロックのblockNumberを確認
> eth.blockNumber
64

blockNumber増えました、マイニングやっていますね。。。

  • ブロックの中身を確認
> eth.getBlock(64)
{
  difficulty: 135070,
  extraData: "0xd883010a12846765746888676f312e31382e31856c696e7578",
  gasLimit: 126082130,
  gasUsed: 0,
  hash: "0xbb3d9ed065abee93826e4fe12d36f7bb1ef85dd7c40543185acee8632fc2890a",
  logsBloom: "...",
  miner: "0x92aad50ffefa0cf6a15ad9b924c98abd3296e05a",
  mixHash: "0xd1d63c8a0395ba6217e3645963dc8fb31ffb2d487502f2ce9c03491bfc9120a2",
  nonce: "0x780dc568a4d1fbed",
  number: 64,
  parentHash: "0x1fb267c1ce515826fa2c8de5b261dc30fdb524bf19f9171a23b895debb6e52e6",
  receiptsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
  sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
  size: 537,
  stateRoot: "0xcc09514a54af66b44b3cfe29c22b037d4e5350689c787ee2df6e25a1d231e0cf",
  timestamp: 1651703855,
  totalDifficulty: 8530449,
  transactions: [],
  transactionsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
  uncles: []
}
  • 再度残高を確認
> eth.getBalance(eth.accounts[0])

4.767e+21

だいぶ増えています。やったー。

スマートコントラクトを作成し実行

コントラクト・コードを作成

最も簡単な、get()関数とset()関数のみのスマートコントラクトを作成し、
SingleNumRegister.solに保存。
contractで、スマートコントラクトSingleNumRegister を宣言。
複数ユーザーがset()関数を用いて同時にデータ更新しても、ブロックチェーンがその整合性を担保してくれます。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

contract SingleNumRegister {
    uint storedData;

    function set(uint x) public {
        storedData = x;
    }

    function get() public view returns (uint retVal) {
        return storedData;
    }
}

コントラクト・コードのコンパイル

$ solc --abi --bin SingleNumRegister.sol

実行結果、BinaryとContract JSON ABIが表示される

======= SingleNumRegister.sol:SingleNumRegister =======
Binary:
608060405234801561001057600080fd5b50610150806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806360fe47b11461003b5780636d4ce63c14610057575b600080fd5b610055600480360381019061005091906100c3565b610075565b005b61005f61007f565b60405161006c91906100ff565b60405180910390f35b8060008190555050565b60008054905090565b600080fd5b6000819050919050565b6100a08161008d565b81146100ab57600080fd5b50565b6000813590506100bd81610097565b92915050565b6000602082840312156100d9576100d8610088565b5b60006100e7848285016100ae565b91505092915050565b6100f98161008d565b82525050565b600060208201905061011460008301846100f0565b9291505056fea2646970667358221220f61e58e1289d29475ea836cc319ed91fd9ef87f5a8da22f11219d57901b595a764736f6c634300080d0033
Contract JSON ABI
[{"inputs":[],"name":"get","outputs":[{"internalType":"uint256","name":"retVal","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"}],"name":"set","outputs":[],"stateMutability":"nonpayable","type":"function"}]

Contractアカウントを生成

コンパイル済みコードをBlockchainプライベートネットワークに送信し、ブロックチェーンに登録、
これで、他のユーザーもこのContractにアクセス出来るようになる。

  • gethプロンプトで、コンパイル結果を変数に格納
> var bin = "0x608060405234801561001057600080fd5b50610150806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806360fe47b11461003b5780636d4ce63c14610057575b600080fd5b610055600480360381019061005091906100c3565b610075565b005b61005f61007f565b60405161006c91906100ff565b60405180910390f35b8060008190555050565b60008054905090565b600080fd5b6000819050919050565b6100a08161008d565b81146100ab57600080fd5b50565b6000813590506100bd81610097565b92915050565b6000602082840312156100d9576100d8610088565b5b60006100e7848285016100ae565b91505092915050565b6100f98161008d565b82525050565b600060208201905061011460008301846100f0565b9291505056fea2646970667358221220f61e58e1289d29475ea836cc319ed91fd9ef87f5a8da22f11219d57901b595a764736f6c634300080d0033"
undefined
> var abi = [{"inputs":[],"name":"get","outputs":[{"internalType":"uint256","name":"retVal","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"}],"name":"set","outputs":[],"stateMutability":"nonpayable","type":"function"}]
undefined
  • アカウントのロック解除
> personal.unlockAccount(eth.accounts[0])
Unlock account 0x92aad50ffefa0cf6a15ad9b924c98abd3296e05a
Passphrase: MYPASSPHRASE
  • Contractのオブジェクトを生成
> var contract = eth.contract(abi)
undefined
  • オブジェクト情報を含んだトランザクションをBlockchainプライベートネットワークに送信
> var myContract = contract.new({ from: eth.accounts[0], data: bin, gas:1000000})
undefined
  • myContractオブジェクトを確認
> myContract
{
  abi: [{
      inputs: [],
      name: "get",
      outputs: [{...}],
      stateMutability: "view",
      type: "function"
  }, {
      inputs: [{...}],
      name: "set",
      outputs: [],
      stateMutability: "nonpayable",
      type: "function"
  }],
  address: "0x3a988a9ed87358000889ae2a7a92d9833239e053",
  transactionHash: "0x209de73abb36302a47ff7bc5f109503f76544d99fc0ed837b31bac77f408f154",
  allEvents: function bound(),
  get: function bound(),
  set: function bound()
}

Blockchainプライベートネットワークに、Contractアカウントが下記アドレスで登録されました。
address: "0x3a988a9ed87358000889ae2a7a92d9833239e053"

スマートコントラクトへのアクセスと実行

登録されたContractアカウントの情報を参照したり、
トランザクションを発行しスマートコントラクト実行可能。

自身の作成したスマートコントラクトを、他のユーザーに利用してもらうため、
以下2点パラメータを渡す必要あり

  • Contractのアドレス
    • アクセス対象のスマートコントラクトを表す
  • ContractのABI(Application Binary Interface)
    • ABIとは、Contractの取り扱い説明書
      • Contractに定義された関数名
      • 関数の呼び出しに必要なパラメータのデータ型
      • 関数実行結果のデータ型

ノード1で、スマートコントラクトへアクセスし実行

  • アカウントのロック解除
> personal.unlockAccount(eth.accounts[0])
Unlock account 0x92aad50ffefa0cf6a15ad9b924c98abd3296e05a
Passphrase: MYPASSPHRASE
  • デフォルトアカウントを設定
> eth.defaultAccount=eth.accounts[0]
  • Contractアカウントへアクセスするオブジェクトを生成
> var cnt = eth.contract(myContract.abi).at(myContract.address);
undefined
  • オブジェクトcntを用いてContractにアクセス
> cnt.get()
"0x7b8227cfadcd4ef90c8cd2a59dd46ee71e8b2f98a81d068ab79a86456515773d"

ノード2で、スマートコントラクトへアクセスし実行

  • 別端末でノード2にアタッチ
$ geth attach $HOME/eth_private_net/data2/geth.ipc
  • アカウントのロック解除
> personal.unlockAccount(eth.accounts[0])
Unlock account 0x92aad50ffefa0cf6a15ad9b924c98abd3296e05a
Passphrase: MYPASSPHRASE
  • デフォルトアカウントを設定
> eth.defaultAccount=eth.accounts[0]
  • 上述Contractのabi、addressをパラメータとして渡し、Contractアカウントアクセス用オブジェクトを生成
> var abi=[{"inputs":[],"name":"get","outputs":[{"internalType":"uint256","name":"retVal","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"}],"name":"set","outputs":[],"stateMutability":"nonpayable","type":"function"}]
undefined
> var address="0x3a988a9ed87358000889ae2a7a92d9833239e053"
undefined
> var cnt = eth.contract(abi).at(address);
undefined
  • オブジェクトcntを用いてContractにアクセス
> cnt.set.sendTransaction(6,{from:eth.accounts[0]})
"0x09d90e66dd11b9cb219adde57139d08430db5d389a185be3039af707be194b9f"
> cnt.get()
"0x820eb6699cbfd5408adcb437a7634374467e0740b73148aa12a3da6e2abe59a8"

おわりに

スマートコントラクトの生成と使用を検証しました。
次回は、Blockchainのブロックの中身を深掘りします。
お楽しみに。

[次回] Web3.0検証(10)-Blockchainのデータ構造
2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1