【Ethereum】web3.js を使用してプライベートネットにスマートコントラクトをデプロイする

#はじめに
[前回](https://qiita.com/y_kitchens/items/2802b9373110bed31b74)web3.jsを使用してEthereumノードのプライベートネットにアクセスする方法を試したので、今回はプライベートネットにコントラクトをデプロイするのを試してみようと思います。
まだjsのPromiseに慣れていないので途中おかしな点があればご指摘いただければありがたいです。

#コントラクト用のコードを用意する

まずはEthereumプライベートネット上にデプロイするコントラクトを作成します。
uint型の変数を宣言し、その状態を変更できるセッターと、変数の値を取得できるゲッターを作成してみます。

```sampleNum.sol
contract SampleNum {
  uint256 num;

  function setNum(uint256 x) public {
    num = x;
  }

  function getNum() public view returns (uint256) {
    return num;
  }
}
```

コードが準備出来たらコンパイルします

```
======= sampleNum.sol:SampleNum =======
Binary:
608060405234801561001057600080fd5b50610150806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806367e0badb1461003b578063cd16ecbf14610059575b600080fd5b610043610075565b60405161005091906100d9565b60405180910390f35b610073600480360381019061006e919061009d565b61007e565b005b60008054905090565b8060008190555050565b60008135905061009781610103565b92915050565b6000602082840312156100b3576100b26100fe565b5b60006100c184828501610088565b91505092915050565b6100d3816100f4565b82525050565b60006020820190506100ee60008301846100ca565b92915050565b6000819050919050565b600080fd5b61010c816100f4565b811461011757600080fd5b5056fea2646970667358221220a05992295b67cb004f513a033e3cde57a0290c5b90b17f63601b626b2268d6cc64736f6c63430008070033
Contract JSON ABI
[{"inputs":[],"name":"getNum","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"}],"name":"setNum","outputs":[],"stateMutability":"nonpayable","type":"function"}]
```
バイナリデータとABIが取得出来ました。


#デプロイ用のjsファイルを用意する

次に先ほど取得したバイナリデータとABIをトランザクションに書き込みデプロイするための処理を記述します。
(web3.jsのインストールがまだの方は[前回(Web3.jsを使ってみる)](https://qiita.com/y_kitchens/items/2802b9373110bed31b74)の記事を見てみてください)

```javascript:test_deploy.js
// web3パッケージをインポートし、インスタンスを作成する。
const Web3 = require('web3');
const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));

// バイナリインターフェイスとバイナリデータを変数に格納
let bin = "0x608060405234801561001057600080fd5b50610150806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806367e0badb1461003b578063cd16ecbf14610059575b600080fd5b610043610075565b60405161005091906100d9565b60405180910390f35b610073600480360381019061006e919061009d565b61007e565b005b60008054905090565b8060008190555050565b60008135905061009781610103565b92915050565b6000602082840312156100b3576100b26100fe565b5b60006100c184828501610088565b91505092915050565b6100d3816100f4565b82525050565b60006020820190506100ee60008301846100ca565b92915050565b6000819050919050565b600080fd5b61010c816100f4565b811461011757600080fd5b5056fea2646970667358221220a05992295b67cb004f513a033e3cde57a0290c5b90b17f63601b626b2268d6cc64736f6c63430008070033";

let abi = [{"inputs":[],"name":"getNum","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"}],"name":"setNum","outputs":[],"stateMutability":"nonpayable","type":"function"}];

// バイナリデータを格納するオブジェクト
let data = {
  'data': bin
};

// コントラクトオブジェクトを生成
let contract = new web3.eth.Contract(abi, null, data);

// デプロイするためのgasに関する設定
let gas = 0;
let gasPrice = '1000000000';

// デプロイするためのガス量を取得し、トランザクションに書き込む
contract.deploy().estimateGas().
    then((estimatedGas) => {
        // console.log("Estimated gas: " + estimatedGas);
        gas = estimatedGas;

        //ガス量が取得できたらトランザクションに書き込み
        contract.deploy().send({
        from: '0x6ec9ebccbf29b2f76f31bfe66f68dfe8ef21460b',
        gasPrice: gasPrice, 
        gas: gas
    }).then((instance) => { 
        console.log("contract address: " + instance.options.address);
        console.log('contract instance: ', instance);
    });
    }).catch(console.error);
```

test_deploy.jsを実行する前にgethコンソールでマイニングを開始しておきます。
これで事前の準備は整いました。


#test_deploy.jsを実行する

さっそくtest_deploy.jsを実行してみます

```bash
$ node test_deproy.js 
> contract address: 0xC10Ee5648331DFD977AcF47C036679f75b86aE12
  contract instance:  Contract {
  ~中略~
  options:
   { data:'0x608060405234801561001057600080fd5b50610150806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806367e0badb1461003b578063cd16ecbf14610059575b600080fd5b610043610075565b60405161005091906100d9565b60405180910390f35b610073600480360381019061006e919061009d565b61007e565b005b60008054905090565b8060008190555050565b60008135905061009781610103565b92915050565b6000602082840312156100b3576100b26100fe565b5b60006100c184828501610088565b91505092915050565b6100d3816100f4565b82525050565b60006020820190506100ee60008301846100ca565b92915050565b6000819050919050565b600080fd5b61010c816100f4565b811461011757600080fd5b5056fea2646970667358221220a05992295b67cb004f513a033e3cde57a0290c5b90b17f63601b626b2268d6cc64736f6c63430008070033',
  ~中略~
  methods:
   { getNum: [Function: bound _createTxObject],
     '0x67e0badb': [Function: bound _createTxObject],
     'getNum()': [Function: bound _createTxObject],
     setNum: [Function: bound _createTxObject],
     '0xcd16ecbf': [Function: bound _createTxObject],
     'setNum(uint256)': [Function: bound _createTxObject] },
  events: { allEvents: [Function: bound ] },
  _address: '0xC10Ee5648331DFD977AcF47C036679f75b86aE12',
  _jsonInterface:
   [ { inputs: [],
       name: 'getNum',
       outputs: [Array],
       stateMutability: 'view',
       type: 'function',
       constant: true,
       payable: undefined,
       signature: '0x67e0badb' },
     { inputs: [Array],
       name: 'setNum',
       outputs: [],
       stateMutability: 'nonpayable',
       type: 'function',
       constant: undefined,
       payable: undefined,
       signature: '0xcd16ecbf' } ] }
```

マイニングが行われていればトランザクションがブロックに取り込まれコントラクトアドレスにアクセスできるようになっているはずです。
今回はキチンとブロックに取り込まれているようです。

あとで使うので、_addressの値と_jsonInterfaceの各functionのsignatureの値を控えておきます。


#デプロイしたスマートコントラクトにアクセスしてみる

##setNum()に値をsendしてみる
今回コントラクト内の変数numには初期値を設定していないのでデフォルトで0が代入されています。
まずはセッターのsendNum()に値を送り変数の状態を変更してみます。

リクエストを送るためのjsファイルを作成します

```test_request.js
// web3パッケージをインポートし、インスタンスを作成する。
const Web3 = require('web3');
const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));

// senderのアドレスとコントラクトのアドレスを変数に格納
let sender_address = "0x6ec9ebccbf29b2f76f31bfe66f68dfe8ef21460b";
let contract_address = "0xC10Ee5648331DFD977AcF47C036679f75b86aE12";

// 各functionのsignatureを変数に格納
let getNum_signature = "0x67e0badb";
let setNum_signature = "0xcd16ecbf";

// setNum()へ渡すパラメーター。今回は10進数255(16進数ff)を設定
let data = "00000000000000000000000000000000000000000000000000000000000000ff"

// sendTransaction()へ渡すパラメーターオブジェクト
let send_data = {
  'from': sender_address,
  'to': contract_address,
  'data': setNum_signature + data, 
}

web3.eth.sendTransaction(send_data).then(console.log);
```
* contract_address: 先ほどデプロイした際に取得した_addressの値を設定します。
* sender_address: 必要ether量を保有しているアカウントのアドレスを設定します。
* data: 先ほど取得したsetNum()のsignatureに64桁のパラメーターのデータをくっつけた値を設定します。
web3.jsの詳細は下記参考サイトをご覧ください。

作成したtest_request.jsを実行してみます。

```bash
$ node test_request.js 
> { blockHash:
   '0x3d90c796fc6a060450c30b75dca8cb2cf6110a82a483a68c32be33bd16bb0466',
   blockNumber: 61,
   contractAddress: null,
   cumulativeGasUsed: 43724,
   effectiveGasPrice: '0x3b9aca00',
   from: '0x6ec9ebccbf29b2f76f31bfe66f68dfe8ef21460b',
   gasUsed: 43724,
   logs: [],
   logsBloom:'0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
   status: true,
   to: '0xc10ee5648331dfd977acf47c036679f75b86ae12',
   transactionHash: '0x2fd6356eb09d56c69a20c2badcd7a140da91d20815cfd949d5d2394e551e3edf',
   transactionIndex: 0,
   type: '0x0' }
```
無事ブロックに取り込まれたようです。

トランザクションも確認しておきます。
先ほど作成したtest_request.jsの内容を変更します。

```test_request.js
// web3パッケージをインポートし、インスタンスを作成する。
const Web3 = require('web3');
const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));

//getTransaction()に渡すパラメーター。transactionHashの値。
let tx = "0x2fd6356eb09d56c69a20c2badcd7a140da91d20815cfd949d5d2394e551e3edf"

// トランザクションの取得
web3.eth.getTransaction(tx).then(console.log);
```

再度test_request.jsを実行します。

```bash
$ node test_web3.js 
> { blockHash:'0x3d90c796fc6a060450c30b75dca8cb2cf6110a82a483a68c32be33bd16bb0466',
   blockNumber: 61,
   from: '0x6Ec9EBcCbF29b2F76F31bfe66f68DFe8Ef21460B',
   gas: 43724,
   gasPrice: '1000000000',
   hash: '0x2fd6356eb09d56c69a20c2badcd7a140da91d20815cfd949d5d2394e551e3edf',
   input: '0xcd16ecbf00000000000000000000000000000000000000000000000000000000000000ff',
   nonce: 1,
   to: '0xC10Ee5648331DFD977AcF47C036679f75b86aE12',
   transactionIndex: 0,
   value: '0',
   type: 0,
   v: '0x4b',
   r: '0x10b9de1c32d3f70da642dc8acd2bfa2d5f7d446da0fb5f4099155632187bd1d9',
   s: '0x38c1be067ea25d90778fe38ffcef4ea44c5f8bf6f2acda412912e5e83a46c975' }
```
トランザクションのinputにsetNum()に渡したパラメーターのdataが入っています。
ちゃんとコントラクトに送れたようです。

##getNum()をcallしてみる

次にコントラクト内の変数numの値を取得し、状態が変更されているか確認してみます。
ゲッターのgetNum()を呼び出し値を取得します。

再度test_request.jsの内容を変更。

```test_request.js
// web3パッケージをインポートし、インスタンスを作成する。
const Web3 = require('web3');
const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));

// senderのアドレスとコントラクトのアドレスを変数に格納
let sender_address = "0x6ec9ebccbf29b2f76f31bfe66f68dfe8ef21460b";
let contract_address = "0xC10Ee5648331DFD977AcF47C036679f75b86aE12";

// 各functionのsignatureを変数に格納
let getNum_signature = "0x67e0badb";
let setNum_signature = "0xcd16ecbf";

// call()へ渡すパラメーターオブジェクト
let get_data = {
	'from': sender_address,
	'to': contract_address,
	'data': getNum_signature
}

web3.eth.call(get_data).then(console.log);
```
test_request.jsを実行してみます。

```bash
$ node test_request.js 
> 0x00000000000000000000000000000000000000000000000000000000000000ff
```

最初にsetNum()にsendした値(0xff)が返ってきました。
無事コントラクトの変数の状態は変更されているようです。

#おわりに
web3.jsを使ってスマートコントラクトのデプロイ、トランザクションの作成が出来るのは便利ですね。
本来Ethereumネットワークにトランザクションを書き込むにはトランザクションデータを秘密鍵で署名する処理があるはずですが、この辺りをバックグラウンドで処理してくれるのはありがたいです。
次はこの署名に関する処理について試していけたらと思います。

#参考サイト
[web3.js - Ethereum JavaScript API](https://web3js.readthedocs.io/en/v1.4.0/index.html#)
web3.jsのドキュメント。各メソッドの使い方も詳しく書かれているので結構使いやすいです。