【Ethereum】中央集権なブロックチェーンアプリケーションの実装&考察

# 概要

ビジネス面の観点からウォレットをユーザー側が持つ非中央集権アプリケーション（Dapps）[^1] ではなく、ウォレットを運営側が持つブロックチェーンアプリケーションを開発したい [^2] という方もいるのではないでしょうか。

この記事では「ウォレットを運営側が持つブロックチェーンアプリケーション」のことを「中央集権なブロックチェーンアプリケーション」と呼び、それを簡易的に実装することで、実際に運用していく面での課題を考えてみようと思います。

# 作成物

<img width="884" alt="スクリーンショット 2018-10-10 18.11.37.png" src="https://qiita-image-store.s3.amazonaws.com/0/94508/5b21f841-b673-4a4c-a6e7-03c322c2d0a5.png">

今回は、ユーザーが疑問に思っていることを質問できたり、知っていることについて回答できるアプリケーションを作成します。

質問者はベストな回答を選ぶことができ、ベストな回答に選ばれたユーザーに一定のトークンを付与するスキームにします。（オーナーのEthereumアドレスから対象者のEthereumアドレスにトークンを付与する）


# 実装

今回実装したアプリケーションのソースコードは[こちらのリポジトリ](https://github.com/hc0208/answers)にあがっています。

## 使用技術
上記のリポジトリを見ていただければわかりますが、今回はバックエンドにFlaskを用いて、Pythonで実装しています。また、トークンの発行部分はTruffleを使って、ERC20ベースのトークンを発行しています。

なお、Ethereum clientにはparityを利用し、テストネットにはkovanを利用しています。

## ポイント

実装の詳細は省きますが、いくつかのポイントだけピックアップします。

### 1. トークン発行部分

トークン発行部分はOpenZeppelinのライブラリを利用しています。 ~~[OpenZeppelin 2.0 RC.1](https://github.com/OpenZeppelin/openzeppelin-solidity/releases/tag/v2.0.0-rc.1)がでていた~~ OpenZeppelin v2.0がリリースされた(2018/10/22 現在)ので、最新のバージョンを利用します。

```shell-session
$ yarn add openzeppelin-solidity
```

v2.0になってからトークンの発行方法が微妙に変わっているので紹介します。

ERC20の実装はインポートするファイル名が変わり、今までよりもわかりやすくなった印象です。

```js:AnswersToken.sol
pragma solidity ^0.4.24;

import 'openzeppelin-solidity/contracts/token/ERC20/ERC20Burnable.sol';
import 'openzeppelin-solidity/contracts/token/ERC20/ERC20Detailed.sol';
import 'openzeppelin-solidity/contracts/token/ERC20/ERC20Mintable.sol';

contract AnswersToken is ERC20Burnable, ERC20Detailed, ERC20Mintable {
  uint256 public constant INITIAL_SUPPLY = 5000 * (10 ** 18);

  constructor()
    ERC20Burnable()
    ERC20Detailed("AnswersToken", "AST", 18)
    ERC20Mintable()
    public
  {
    _mint(msg.sender, INITIAL_SUPPLY);
  }
}

```

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

一度デプロイに必要なアカウントをアンロックした状態でparityを起動し、テストネットワークであるkovanにデプロイしました。

```shell-session
$ parity --jsonrpc-apis all --jsonrpc-hosts all --chain kovan --unlock "YOUR_ACCOUNT" --password password.txt
```
```shell-session
$ truffle migrate --network kovan
```

### 3. バックエンドでのトークン付与

ライブラリ内のtransferメソッドを用いてトークンを付与します。

```js:openzeppelin-solidity/contracts/token/ERC20/ERC20.sol
function transfer(address to, uint256 value) public returns (bool) {
  _transfer(msg.sender, to, value);
  return true;
} 
```


また、Ethereum nodeとのやりとりは、web3.pyを利用しているので、`web3.personal.sendTransaction` を用いてトークン付与のためのトランザクションを送ります。

```python:main.py
web3 = Web3(HTTPProvider('http://127.0.0.1:8545'))
to_address = web3.toChecksumAddress("TO_ADDRESS")

data = '0xa9059cbb000000000000000000000000' + to_address[2:] + '{0:064x}'.format("AMOUNT")
transaction = {
    'from': web3.toChecksumAddress("OWNER_ADDRESS"),
    'to': web3.toChecksumAddress("CONTRACT_ADDRESS"),
    'value': '0x0',
    'data': data
}
web3.personal.sendTransaction(transaction, "OWNER_ADDRESS_PASSWORD")
```

dataの値は、[Contract ABI Specification](https://solidity.readthedocs.io/en/develop/abi-spec.html) を読んでみると、何を書けばいいのかわかります。

コントラクト内の使用するメソッド `transfer(address,uint256)` をハッシュ関数SHA-3でエンコードした値のはじめの4byte（メソッドid）を取得し、付与先アドレスを32byteで0埋めした値と付与したいトークン数を32byteで0埋めした値を続けて記述します。

```python

>>> web3.sha3(text='transfer(address,uint256)').hex()
'a9059cbb2ab09eb219583f4a59a5d0623ade346d962bcd4e46b11da047c9049b'

# 例：メソッドid(4byte) + アドレス(32byte) + 付与したいトークン数(32byte)
'a9059cbb000000000000000000000000fb578f247E657EC2268D8E007996bFcbde0CaB550000000000000000000000000000000000000000000000000000000000000002'
```

# 課題

実際に簡易的なアプリケーションを実装してみて感じた課題を書き出していきます。

## 秘密鍵の管理方法

秘密鍵の管理方法は中央集権なブロックチェーンアプリケーションを作る上でもっとも考えられうる課題だと思います。ユーザーではなく、運営側でウォレットを持つ以上、セキュリティ面はしっかりとしなければなりません。

今回作成した簡易アプリケーションでは特別な対策していませんが、本番環境でサービスを運用していくとなると対策していかなければいけない部分になります。

仮想通貨取引所から資産が盗まれるハッキングニュースを記憶している方も多いと思いますが、資金が十分にあり、セキュリティに力を入れられる会社でも起こりうる課題です。

どのレベルまでセキュリティを強くするのかによって管理方法は変わってくると思いますが、いくつかの対策例として、以下の方法が思いつきました。[^3]

### 例1. コールドウォレットで管理する

ホットウォレットではなく、コールドウォレットで管理する方法です。

コールドウォレットは、ホットウォレットに比べ安全ですが、運用コストがかかってきます。また、トランザクションの署名までにラグがでてくるので、すぐにトランザクションをチェーンに記録する必要があるアプリケーションには向かない可能性が高いです。

一方で、トランザクションをある期間にまとめて実行するアプリケーションや資産をすぐに動かすことができないアプリケーションであれば利用していくべきだと思います。

### 例2. 複数アドレスにしたり、マルチシグにする

あるユーザーに紐づいている資産の管理を複数アドレスで行ったり、マルチシグにしたりすることでセキュリティを向上させる方法もあるかと思います。

また、そこで利用する秘密鍵の保管場所を分散させることでリスクを抑えるのも有効です。

### 例3. HSMを利用する

HSM（ハードウェア セキュリティ モジュール）を利用して秘密鍵を管理する方法もあります。
より安全に保管できるので利用を検討してもいいと思います。

## 法律面との兼ね合い

作成するアプリケーションの座組によって変わってきますが、仮想通貨を扱うアプリケーションは法律面のハードルが高いので注意が必要です。（例えば、サービス内で日本円からトークンを購入できるアプリケーションの場合、仮想通貨交換業が必要だと思われます。）

素人で考えられる部分ではないので、専門家に聞くべき部分です。

## トランザクションの承認時間が長く、余計な手数料がかかる

ブロックチェーンにデータを書き込むのには手数料がかかります。
手数料を誰が負担するのかによりますが、ユーザーからしても運営側からしても無視できない額になる可能性があります。また、トンランザクションの承認までの時間も長くかかってくるので、注意が必要です。

これに関しては非中央集権アプリケーションでも問題になるので、汎用的なPlasmaの実用化に期待です。

# まとめ

中央集権の要素を残すとさまざまな課題があることがわかりました。

特にウォレットの管理方法の部分や法律面での課題が大きいように感じます。

今回はあえて中央集権なブロックチェーンアプリケーションを作成してみましたが、現時点(2018年10月時点)では話題性のあるブロックチェーンを無理に利用するのではなく単純なDB管理でアプリケーションを作る方が実装や運用、法律、セキュリティ面のハードルは下がりそうです。

ただ、話題性にベッドする、明確なメリットや目指す世界観がある場合はうまく利用する価値はあると思います。

[^1]: 厳密には、CryptoKittiesなどに代表されるDappsも中央集権の要素は残っています。
[^2]: あるサービス内で使えるポイントをトークンに置き換えてみるなど。
[^3]: 実際に作成するアプリケーションによって選択できる方法は変わってきます。また、扱う金額が大きくなってくると求められるセキュリティレベルも高くなるので、設備の問題やPCの管理など他にも考えなければならない点がたくさんある点に注意です。