Metamaskを使わないDapps開発 (React, プライベートチェーン)

## はじめに

ガス代ゼロの環境下でDAppsを開発しています。
ですのでMetamaskいらないなと思い、Metamask不要実装をしたのでまとめます。

ソースコードはこちらになります。
https://github.com/Yosuke-Aramaki/dapp_without_metamsk.git

下記のバージョンを使用していました。

```
node v11.14.0 
npm v6.7.0)
truffle v5.0.7
web3 v1.0.0-beta.46
```

実装の順番としてはこのようになります。
・プライベートチェーンの構築
・コントラクトのデプロイ
・Web3.jsでクライアント側から書き込む

## 1. プライベートチェーンの構築

PoAのチェーンの構築はかきQiitaを参考にしました。
[EthereumのPoAな開発環境構築](https://qiita.com/aramsan/items/ab245c24083fc21a92ce)

### 1-1 Gethのセットアップ

まず`ethereum`をインストールします。

```
brew tap ethereum/ethereum
brew install ethereum
```

`privatechain`というディレクトリを作成し、そのディレクトリのなかに開発用のGethアカウントを作成します。

```
$ geth account new --datadir ./privateconsortium/node1
NFO [12-20|11:37:00.688] Maximum peer count                       ETH=25 LES=0 total=25
Your new account is locked with a password. Please give a password. Do not forget this password.
Passphrase:
Repeat passphrase:
Address: {4d8c003c1dc28a28a323873752e2b07c2cf3d413}
```

### 1-2 Genecisブロックの作成

`privatechain`のディレクトリに移動しgenecis blockを作成します

```
$ cd privatechain
$ puppeth
```

"privatechain"でネットワーク名を設定します（ネットワーク名はなんでもいいです）

```
Please specify a network name to administer (no spaces, hyphens or capital letters please)
>privatechain
```

新しいgenecis block作成のために2をタイプします。

```
What would you like to do? (default = stats)
 1. Show network stats
 2. Configure new genesis
 3. Track new remote server
 4. Deploy network components
> 2
```


```
What would you like to do? (default = create)
 1. Create new genesis from scratch
 2. Import already existing genesis
> 1
```

今回はPoAを利用します。

```
Which consensus engine to use? (default = clique)
1. Ethash - proof-of-work
2. Clique - proof-of-authority
> 2
How many seconds should blocks take? (default = 15)
> 5
```

自分が作成したアドレスこ書き込み、Enterを二回押します。

```
Which accounts are allowed to seal? (mandatory at least one)
> 0x4d8c003c1dc28a28a323873752e2b07c2cf3d413
> 0x

Which accounts should be pre-funded? (advisable at least one)
> 0x4d8c003c1dc28a28a323873752e2b07c2cf3d413
> 0x
Should the precompile-addresses (0x1 .. 0xff) be pre-funded with 1 wei? (advisable yes)
> yes
```

今回は、ネットワークIDを`1515`にします。（これも任意で書き換えて大丈夫です。）

```
Specify your chain/network ID if you want an explicit one (default = random)
>1515
```

2回押します。
*コントラクトをデプロイする際にRejectされる場合は、`1`でfolk ruleを設定すれば解消すると思います。folk ruleはdefaultに従う形で大丈夫です。

```
What would you like to do? (default = stats)
1. Show network stats
2. Manage existing genesis
3. Track new remote server
4. Deploy network components
> 2

 1. Modify existing fork rules
 2. Export genesis configurations
 3. Remove genesis configuration
> 2
If you don't mind where those will be save, press enter.

Which folder to save the genesis specs into? (default = current)
  Will create privatechain/.json, privatechain/-aleth.json, privatechain/-harmony.json, privatechain/-parity.json
>
IINFO [12-20|22:54:03.661] Saved native genesis chain spec          path=privateconsortium.json
ERROR[12-20|22:54:03.662] Failed to create Aleth chain spec        err="unsupported consensus engine"
ERROR[12-20|22:54:03.662] Failed to create Parity chain spec       err="unsupported consensus engine"
INFO [12-20|22:54:03.663] Saved genesis chain spec                 client=harmony path=privateconsortium-harmony.json
```

### 1-3. ネットワークを始める。

nodeを初期化し、gethを起動します。

```
$ cd privatechain
$ geth --datadir node1 init privatechain.json
$ geth --datadir node1/ --syncmode 'full' --port 30311 --rpc --rpcaddr '0.0.0.0' --rpcport 8545 --rpccorsdomain "*" --rpcvhosts "*" --rpcapi 'personal,db,eth,net,web3,txpool,miner'  --networkid 1515 --gasprice '0'
```

もう1つのターミナルで`privatechain`のディレクトリに移動し、nodeを起動します。

```
$ cd privatechain
$ geth attach ipc:node1/geth.ipc
Welcome to the Geth JavaScript console!
```

アカウントをアンロックしてマイニングを開始します。

```
> personal.unlockAccount(eth.coinbase,"Password",0)
true
> eth.defaultAccount = eth.coinbase
0x4d8c003c1dc28a28a323873752e2b07c2cf3d413
> miner.start()
null
```

ブロックナンバーは`eth.blockNumber`で見れます。

```
> eth.blockNumber
22873
```


## 2. コントラクトのデプロイ

### 2-1. コントラクト

コントラクトは簡単なものを使用します。

```js
pragma solidity ^0.5.0;

contract Greeter {
  string public greeting;

  constructor() public {
    greeting = 'Hello';
  }

  function setGreeting(string memory _greeting) public {
    greeting = _greeting;
  }

  function greet() view public returns (string memory) {
    return greeting;
  } 

}
```

### 2-2. truffle-config.js

・`network_id:`はGenesis blockを作成するときに指定したもの、今回は`15151`
・`HDWalletProvider()`のサーバーはGethを起動したときに指定したもの。今回は`http://0.0.0.0:8545`となります。
・`mnemonic`はMetamaskのmnemonicを利用しました。`mnemonic`は流出してしまうとまずいので、通常は`.env`ファイルで管理して`process.env.MNEMONIC`呼び出すようにします。

上記をまとめると、`truffle-config.js`はこのようになります。

```truffle-config.js
//require('dotenv').config();
//const mnemonic = process.env.MNEMONIC

const HDWalletProvider = require('truffle-hdwallet-provider');
const mnemonic = "receive risk ... present topic"

module.exports = {
  rpc: {
    host:"0.0.0.0",
    port:8545
  },

  networks: {

    privatechain: {
      provider: new HDWalletProvider(process.env.MNEMONIC, 'http://0.0.0.0:8545'),
      network_id: "1515",
      gas: 0,
      gasPrice: 0
    },

  },

  contracts_directory: './src/contracts/',
  contracts_build_directory: `./src/abis/`,

  mocha: {
    // timeout: 100000
  },

  // Configure your compilers
  compilers: {
    solc: {
      optimizer: {
        enabled: true,
        runs: 200
      }
    }
  }
}
```


### 2-3. コントラクトのデプロイ

ここでリポジトリをCloneし、パッケージをインストールします。

```
$ git clone 
$ npm i
```

次に'.env'ファイルを作成します。

```.env
MNEMONIC = "receive ... topic"
```

privatechainのネットワークでコンソールを開いて、コントラクトをデプロイします。

```
$ truffle console --network privatechain 
truffle(local)> migrate --reset
```

`npm start`でサイトを開くとこのようになってると思います。
![スクリーンショット 2019-10-15 8.03.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342028/244b158a-936e-bf12-0bb5-85344f75d361.png)



## 3. Web3.jsで書き込み

transactionの体裁を整える`ethereumjs-tx`と、カスタムチェーンへの書き込みをサポートする`ethereumjs-common`を利用します。

クライアントサイドでのWeb3を利用した書き込みは下記のようなコードになります。

```App.js
import Common from 'ethereumjs-common'
const EthereumTx = require('ethereumjs-tx').Transaction

//①　どのチェーンに繋ぐかの情報を設定
const customCommon = Common.forCustomChain(
  'mainnet',
  {
    name: 'privatechain',
    networkId: 1515,
    chainId: 1515,
  },
  'petersburg',
)

//② チェーンに書き込むためにスマートコントラクトの関数をABI化する
const functionAbi = await this.state.contract.methods.setGreeting(this.state.text).encodeABI()

// チェーンに書き込む際のTransactionの定型文
var details = await {
  nonce : this.state.nonce,
  gasPrice : 0,
  gasLimit: 500000,
  from : this.state.web3.eth.coinbase,
  to : this.state.contractAddress,
  data : functionAbi,
};

//③データの定型文とチェーンの指定を1つのTransactionにまとめる
const transaction = await new EthereumTx(details, { common: customCommon },);

//④transactionを秘密鍵で署名する
await transaction.sign(this.state.pk)
console.log(transaction)

//⑤署名したtransactionをチェーンに書き込むためrawdataにする。
var rawdata = await '0x' + transaction.serialize().toString('hex');
console.log(rawdata)

//⑥　rawdataをweb3を使ってチェーンに書きこむ
await this.state.web3.eth.sendSignedTransaction(rawdata)
.on('transactionHash', function(hash){
  console.log(['transferToStaging Trx Hash:' + hash]);
})
.on('receipt', function(receipt){
  console.log(['transferToStaging Receipt:', receipt]);
})
.on('error', console.error);
```

これで、Metamaskなしでブロックチェーンに書き込みができるようになります。

適当な文字を打ち込んで変わっていたら成功です。
（Submitを押して少し待ってリロードする必要があります）
![スクリーンショット 2019-10-15 8.04.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342028/093fba60-44b7-f453-bb20-25dfcf2e4ab8.png)


## 終わりに

今回の実装を通じて、何となく使っていたMetamaskの中身について実感することができました。Metamaskを使ってるとチェーンへの書き込みが簡単に行われるので、どんなデータが書き込まれるのか意識しないことが多いです。ですが、本実装では自分で書き込むデータを作成するので、ブロックチェーン自体の理解も進みました。

今回はプライベートチェーンを使ったので簡単にできました。
ただ、パブリックチェーンなどでDapps開発を行う時でも、プライベートキーの管理、ガス代の計算をうまくできればMetamaskなしでも開発できると感じました。


ご意見、質問等々ありましたら、ご気軽に連絡ください。