Web3.0検証(9)-スマートコントラクトの作成

##### [前回] [Web3.0検証(8)-ブロックチェーンのプライベートネットワーク](https://qiita.com/mingchun_zhao/items/d7a5f88852241eca8cf3)

## はじめに

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

## おさらい

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523098/2702c782-0eda-5a72-e2b4-73d441cbe1cc.png)

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

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

## 検証シナリオ

スマートコントラクトを作成し実行します。
手順は、[Ethereum: スマートコントラクトを作成し実行する](https://book.ethereum-jp.net/first_use/contract)を参考にしました。

- スマートコントラクトの作成から実行までの流れ
    - コントラクト・コードの作成
        - 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()関数を用いて同時にデータ更新しても、ブロックチェーンがその整合性を担保してくれます。

```solidity
// 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のデータ構造](https://qiita.com/mingchun_zhao/items/19cc407c49e69f920178)