こんにちは、日立ソリューションズの小島です。
私たちのチームでは、エンタープライズEthereumクライアントのHyperledger Besuに関する記事を投稿しています。
前回の記事では、Hyperledger Besuのサンプルの紹介・解説を行いました。
この記事では、Hyperledger Besuの特徴的なサービスの一つであるEthSignerについて紹介します。
EthSignerとは?
EthSignerは、Hyperledger Besuが有する鍵管理/署名サービスです。
EthSignerは、アプリケーションがブロックチェーンネットワークへ対してトランザクションを投げる際に中継を行います。
アプリケーションから受信したトランザクションの送信者に応じた鍵を選択し、受信したトランザクションへ署名を付与、ブロックチェーンネットワークへ中継します。
EthSignerを使用する場合のトランザクションのプロセスは以下の図の通りです。
従来のEthereum(Gethなど)では、アプリケーション側で鍵管理と署名を行っていましたが、Hyperledger BesuではEthSignerにその役割を任せることができます。
これにより、アプリケーション側では鍵管理と署名の手間から解放されます。
(EthSignerを使わずに、従来通りアプリケーション側で署名し、直接ブロックチェーンネットワークへトランザクションを投入することも可能です)
また鍵の保管場所として、EthSignerの動作している環境もしくはクラウドサービスを選択できます。
鍵をEthSignerの動作している環境に保存する際は、ファイルをV3 keystoreという形式で暗号化して保存します。
参考: https://docs.ethsigner.consensys.net/en/stable/Concepts/Overview/
前提環境
この記事では以下の環境で動作させてみました。
# | 項目 | バージョン |
---|---|---|
1 | OS | Ubuntu 18.04 LTS |
2 | Node, npm | 12.18.03, 6.14.4 |
3 | Docker | 19.03.12 |
4 | docker-compose | 1.25.0 |
早速使ってみた
今回は、簡単に動作確認を行うため、ethash(Proof of Work)を用いた最小構成のHyperledger Besuネットワークを作成します。
ブロックチェーンネットワークを構成する各ノードの説明は以下の表の通りです。
# | 項目 | 説明 |
---|---|---|
1 | bootnode | ブロックチェーンネットワークの起動と通信の取りまとめを行うノードです。 |
2 | rpcnode | RPCリクエストを受け付けるノードです。今回は、EthSignerからの通信を受け付けます。 |
3 | minernode | マイニングを行うノードです。 |
以下のように適当なワークフォルダを作成し、以降はこの下で作業を行います。
~$ mkdir ethsigner-trial
~$ cd ethsigner-trial
これ以降の手順で、以下のようなファイルを作成します。
.
├── ethsigner
│ ├── docker-compose.yaml
│ ├── keyfile
│ └── passwordfile
├── js
│ ├── createKey.js
│ ├── keyPair.js
│ ├── node_modules/
│ ├── package.json
│ ├── package-lock.json
│ └── sendTransaction.js
└── node
├── bootnode-privatekey
├── docker-compose.yaml
└── genesis.json
npmを用いたパッケージのインストール
npmを用いてパッケージをインストールします。特にethereumjs-walletは、バージョンに注意してください。
~/ethsigner-trial/js$ npm init -y
~/ethsigner-trial/js$ npm install ethereumjs-wallet@0.6.4
~/ethsigner-trial/js$ npm install web3@1.3.0
秘密鍵・公開鍵・アドレスの生成
はじめに、動作確認に使用するための、ethereumの秘密鍵・公開鍵・アドレスを生成します。
今回は、ethereumjs-walletライブラリ(v0.6.4)を用います。
var Wallet = require('ethereumjs-wallet');
var addressData = Wallet.generate();
console.log("key: " + addressData.getPrivateKeyString().slice(2));
console.log("pub: " + addressData.getPublicKeyString().slice(2));
console.log("add: " + addressData.getAddressString().slice(2));
上記のkeyPair.js
を実行します。ここで生成した鍵ペアをbootnodeの鍵として用います。後で使うので控えておいてください。
~/ethsigner-trial/js$ node keyPair.js
key: 4322291ba51ce20c285206ef0756c35df615d573741f05db6b79b1c8c21b117e
pub: db69d24a1a18162e7ab56d59a8f8964a684c13f2d169e2db8754ef5db9d30131ec80674264745591843f19f920f35a418e6ddc2e06902f974219f93e350e1d87
add: b924fd952f22ad352e3c9b0b807859122535632a
もう一度keyPair.js
を実行します。ここで生成したアドレスをcoinbase(マイニング報酬を受け取るアドレス)に設定し、またEthSignerの署名のための鍵としても使用します。こちらも後で使うので控えておいてください。
~/ethsigner-trial/js$ node keyPair.js
key: c6a1a93a43128ea1acb63fb5874fdc221604c7163e6412973b0e2920918528db
pub: 2fd29b7362b88558b5e39e30e7a74752c96d446c9f4a08ec3c53f47a9a13d827d2f2eee3e909bdc67b3ee95574cfa63dfdecf0162c7f86b88c6b3e0d744ce208
add: 3bb6e43b428c55c2601ad15b71117280335744f8
ブロックチェーンネットワークの設定・起動
それでは、Hyperledger Besuを用いたブロックチェーンネットワークの設定をしていきます。
まずは、bootnodeの秘密鍵ファイルを作成します。bootnode-privatekey
というファイルに、先ほど一度目に作成した秘密鍵を貼り付けます。
4322291ba51ce20c285206ef0756c35df615d573741f05db6b79b1c8c21b117e
次に、genesisファイルを作成します。genesisファイルは、チェーン上の最初のブロックを定義します。
{
"config": {
"constantinoplefixblock": 0,
"ethash": {
"fixeddifficulty": 1000
},
"chainID": 2018
},
"nonce": "0x0",
"gasLimit": "0x1000000",
"difficulty": "0x10000"
}
}
・config
にethash
を指定することで、コンセンサスアルゴリズムethash
とします。
・config
にchainID
を指定します。この値を後ほどEthSignerの設定に用います。
次に、dockerのcomposeファイルを作成します。
---
version: '3.4'
services:
bootnode:
image: hyperledger/besu:21.10.2
user: "0:0"
command: [
"--bootnodes",
"--genesis-file=/config/genesis.json",
"--p2p-host=172.16.239.11",
"--data-path=/opt/besu/data",
"--host-allowlist=all"]
volumes:
- ./bootnode-privatekey:/opt/besu/data/key
- ./genesis.json:/config/genesis.json
ports:
- 30303:30303/tcp
- 30303:30303/udp
networks:
besu-network:
ipv4_address: 172.16.239.11
minernode:
image: hyperledger/besu:21.10.2
user: "0:0"
command: [
"--bootnodes=enode://db69d24a1a18162e7ab56d59a8f8964a684c13f2d169e2db8754ef5db9d30131ec80674264745591843f19f920f35a418e6ddc2e06902f974219f93e350e1d87@172.16.239.11:30303",
"--genesis-file=/config/genesis.json",
"--p2p-host=172.16.239.12",
"--data-path=/opt/besu/data",
"--host-allowlist=all",
"--miner-enabled=true",
"--miner-coinbase=3bb6e43b428c55c2601ad15b71117280335744f8"]
volumes:
- ./genesis.json:/config/genesis.json
depends_on:
- bootnode
networks:
besu-network:
ipv4_address: 172.16.239.12
rpcnode:
image: hyperledger/besu:21.10.2
user: "0:0"
command: [
"--bootnodes=enode://db69d24a1a18162e7ab56d59a8f8964a684c13f2d169e2db8754ef5db9d30131ec80674264745591843f19f920f35a418e6ddc2e06902f974219f93e350e1d87@172.16.239.11:30303",
"--genesis-file=/config/genesis.json",
"--p2p-host=172.16.239.13",
"--data-path=/opt/besu/data",
"--host-allowlist=all",
"--rpc-http-enabled=true",
"--rpc-http-host=0.0.0.0",
"--rpc-http-port=8545",
"--rpc-http-cors-origins=all"]
volumes:
- ./genesis.json:/config/genesis.json
depends_on:
- bootnode
ports:
- 8545:8545
networks:
besu-network:
ipv4_address: 172.16.239.13
networks:
besu-network:
external: true
・minernodeとrpcnodeのcommand
の--bootnodesにenode://<bootnodeの公開鍵>@<bootnodeのipアドレス>:<bootnodeのポート番号>
を指定します。<bootnodeの公開鍵>
は、keyPair.js
で1回目に生成した公開鍵です。
・minernodeのcommand
の--miner-coinbaseに、先ほど二度目に生成したアドレスを指定します。
それでは、必要なファイルがそろったので、ブロックチェーンネットワークを起動します。
以下のコマンドを実行し、subnetを指定してbesu-network
という名前のdockerネットワークの作成します。
~/ethsigner-trial/node$ docker network create besu-network --subnet=172.16.239.0/24
以下のコマンドを実行し、dockerイメージのpullとdockerコンテナの起動を行います。
~/ethsigner-trial/node$ docker-compose up -d
コンテナの起動を確認します。各コンテナのSTATUS
にhealthy
と表示されていれば、正常に起動しています。
~/ethsigner-trial/node$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
20c6d33e703a hyperledger/besu:21.10.2 "besu --bootnodes=en…" 40 seconds ago Up 37 seconds (healthy) 8546-8547/tcp, 0.0.0.0:8545->8545/tcp, 30303/tcp node_rpcnode_1
a7f0314c7830 hyperledger/besu:21.10.2 "besu --bootnodes=en…" 40 seconds ago Up 38 seconds (healthy) 8545-8547/tcp, 30303/tcp node_minernode_1
7ac113832492 hyperledger/besu:21.10.2 "besu --bootnodes --…" 42 seconds ago Up 40 seconds (healthy) 8545-8547/tcp, 0.0.0.0:30303->30303/tcp, 0.0.0.0:30303->30303/udp node_bootnode_1
EthSignerの設定・起動
それでは、EthSignerの設定をしていきます。
はじめに、EthSignerが署名に用いる秘密鍵を暗号化して鍵ファイルを生成し、暗号化に用いたパスワードをファイルに保存します。
まず、鍵の暗号化に用いるパスワードの記載されたテキストファイルを作成します。
今回は、passwordfile
というファイル名にします。
Password1
続いて、web3.jsライブラリ(v1.3.0)を用いて、V3 keystoreという形式で秘密鍵を暗号化します。
今回は、coinbaseの秘密鍵を暗号化します。そのために、以下のcreateKey.js
スクリプトを用意します。
var Web3 = require('web3');
var web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545')); // rpcnodeエンドポイントを指定
var V3KeyStore = web3.eth.accounts.encrypt("c6a1a93a43128ea1acb63fb5874fdc221604c7163e6412973b0e2920918528db", "Password1");
console.log(JSON.stringify(V3KeyStore));
process.exit();
・web3jsのhttpproviderにはrpcnodeのエンドポイント(http://localhost:8545
)を指定します。
・秘密鍵は、coinbaseの秘密鍵(keyPair.js
で2回目に生成したもの)を指定してください。
・パスワードは、先ほどパスワードファイルに記載したパスワードを設定してください。
上記のcreateKey.js
を実行し、鍵ファイルの内容をコンソールに出力します。出力される内容は実行する度に異なります。
~/ethsigner-trial/js$ node createKeyFile.js
{"version":3,"id":"aad7c3ed-1d37-4453-8d50-42a94885744e","address":"3bb6e43b428c55c2601ad15b71117280335744f8","crypto":{"ciphertext":"f3396008bb66688b85e2b802509cd352f0fd38831c365b8b054f29f4fd8934cb","cipherparams":{"iv":"0e1455d95bd2aa8bcf81c3db47baddbf"},"cipher":"aes-128-ctr","kdf":"scrypt","kdfparams":{"dklen":32,"salt":"d1d2aa1f4ecced704f3fa94aa47cf6a27a2493820c7b5032cb72a566ef3742f5","n":8192,"r":8,"p":1},"mac":"f98564bc953d53c8401e1125e07def4c69a36d7acbd8daf0b8f9f8173d83e427"}}
出力された内容をコピーしてファイルに貼り付けます。今回は、keyfile
というファイル名にします。
このファイルが、V3 keystoreファイルです。
{"version":3,"id":"aad7c3ed-1d37-4453-8d50-42a94885744e","address":"3bb6e43b428c55c2601ad15b71117280335744f8","crypto":{"ciphertext":"f3396008bb66688b85e2b802509cd352f0fd38831c365b8b054f29f4fd8934cb","cipherparams":{"iv":"0e1455d95bd2aa8bcf81c3db47baddbf"},"cipher":"aes-128-ctr","kdf":"scrypt","kdfparams":{"dklen":32,"salt":"d1d2aa1f4ecced704f3fa94aa47cf6a27a2493820c7b5032cb72a566ef3742f5","n":8192,"r":8,"p":1},"mac":"f98564bc953d53c8401e1125e07def4c69a36d7acbd8daf0b8f9f8173d83e427"}}
参考: https://docs.ethsigner.consensys.net/en/stable/Tutorials/Start-EthSigner/#create-password-and-key-files
続いてEthSigner用のdocker-compose.yaml
を作成します。
---
version: '3.4'
services:
ethsigner:
image: consensys/ethsigner:21.10.0
command: [
"--chain-id=2018",
"--http-listen-host=0.0.0.0",
"--downstream-http-port=8545",
"--downstream-http-host=rpcnode",
"file-based-signer",
"-k",
"/opt/ethsigner/keyfile",
"-p",
"/opt/ethsigner/passwordfile"]
volumes:
- ./passwordfile:/opt/ethsigner/passwordfile
- ./keyfile:/opt/ethsigner/keyfile
ports:
- 18545:8545/tcp
networks:
besu-network:
ipv4_address: 172.16.239.14
networks:
besu-network:
external: true
EthSigner用docker-compose.yaml
に記載したEthSignerのコマンドラインオプションは以下の表の通りです。
# | オプション | 説明 |
---|---|---|
1 | --chain-id=2018 | 接続先ネットワークのchain ID。genesisファイルに記載したものを指定。 |
2 | --http-listen-host=0.0.0.0 | EthSignerのIPアドレス |
3 | --downstream-http-port=8545 | 中継先(rpcnode)のポート番号 |
4 | --downstream-http-host=rpcnode | 中継先(rpcnode)のホスト名称 |
5 | file-based-signer | ファイルベースの鍵を用いる設定 |
6 | -k /opt/ethsigner/keyfile | 暗号化された鍵ファイルを指定 |
7 | -p /opt/ethsigner/passwordfile | 暗号化された鍵に対するパスワードファイルを指定 |
今回は1つのEthSignerに対して1つの鍵を設定しましたが、1つのEthSignerに対して複数の鍵を設定することも可能です。その場合、EthSignerはトランザクションの送信者に応じた鍵を選択します。
これでEthSignerの設定ファイルがそろいました。
次に、docker-compose.yaml
のあるディレクトリで以下のコマンドを実行し、dockerイメージのpullとdockerコンテナの起動を行います。
~/ethsigner-trial/ethsigner$ docker-compose up -d
コンテナの起動を確認します。
~/ethsigner-trial/ethsigner$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
83d6de6a15c5 consensys/ethsigner:21.10.0 "/opt/ethsigner/bin/…" 21 seconds ago Up 18 seconds 0.0.0.0:18545->8545/tcp ethsigner_ethsigner_1
20c6d33e703a hyperledger/besu:21.10.2 "besu --bootnodes=en…" About a minute ago Up About a minute (healthy) 8546-8547/tcp, 0.0.0.0:8545->8545/tcp, 30303/tcp node_rpcnode_1
a7f0314c7830 hyperledger/besu:21.10.2 "besu --bootnodes=en…" About a minute ago Up About a minute (healthy) 8545-8547/tcp, 30303/tcp node_minernode_1
7ac113832492 hyperledger/besu:21.10.2 "besu --bootnodes --…" About a minute ago Up About a minute (healthy) 8545-8547/tcp, 0.0.0.0:30303->30303/tcp, 0.0.0.0:30303->30303/udp node_bootnode_1
これでEthSignerを含むブロックチェーンネットワークが立ち上がりました。
EthSigner動作確認
それでは、EthSignerに対して、未署名のトランザクションを投げてみましょう。
今回のブロックチェーンネットワークでは、唯一のマイナーが大量のETHの報酬をもらいます。その報酬は、coinbaseに指定したアドレスへ貯められます。ブロックチェーンネットワークを起動してしばらくすると大量のETHを持っている状態になります。
そこで動作確認としては、eth_sendTransactionを使用して、coinbaseに指定したアドレスから任意のアドレスへ10ETH送ります。
送金先アドレスには、任意のアドレスを設定してもらって構いませんが、新規に作成する場合は前述のkeyPair.js
を実行してください。
var Web3 = require('web3');
var web3 = new Web3();
web3.setProvider(new web3.providers.HttpProvider('http://localhost:18545')); // EthSignerエンドポイントを指定
var account1 = '<送金先アドレス>';
var account2 = '3bb6e43b428c55c2601ad15b71117280335744f8'; // coinbaseアドレス
web3.eth.getBalance(account1, (error, balance) => {
console.log("account1:" + web3.utils.fromWei(balance, "ether"));
});
var transaction = {
from: account2,
to: account1,
value: web3.utils.toWei("10", "ether")
};
web3.eth.sendTransaction(transaction).then(() => {
console.log("sendTransaction success");
web3.eth.getBalance(account1, (error, balance) => {
console.log("account1:" + web3.utils.fromWei(balance, "ether"));
});
}).catch(e => {
console.log(e);
});
・account2
には、coinbaseのアドレス(keyPair.js
で2回目に生成したもの)を指定してください。
上記のsendTransaction.js
を実行します。
~/ethsigner-trial/js$ node sendTransaction.js
account1:0
sendTransaction success
account1:10
account1
の残高が10ETH増えたため、10ETH送金のトランザクションが成功したことがわかります。
ここではEthSignerが、受け取ったトランザクションに署名を付与し、ブロックチェーンへ転送してくれました。
このように、Hyperledger Besuでは、EthSignerに鍵管理・署名のプロセスを任せることができます。
終わりに
さて、Hyperledger Besuの鍵管理・署名サービスであるEthSignerの紹介を行い、実際に動かしてみました。
EthSignerを使うことで、アプリケーションの鍵管理と署名の手間を省けることがわかりました。
非常に手軽で便利なサービスである一方、EthSignerに持たせる鍵ファイルやパスワードファイルのセキュリティ面を十分考慮する必要があると思いました。
また、今回動作確認に使用した最小構成のHyperledger Besuネットワークが参考になればと思います。
Hyperledger Besu関連の日本語の記事はまだまだ少ないので、今後も情報発信を行っていきたいです。