Web3Signerを使ってトランザクション署名してみた

# はじめに
私が最近関わったブロックチェーン案件で、「トランザクション署名に使う秘密鍵をセキュアに保管/利用する方法」を調査する機会がありました。
この調査したものの一つにWeb3Signerという署名ツールがあり、このツールに関しては簡単なトランザクション署名の動作確認まで行いました。
その動作確認内容を、本記事ではメモしておこうと思っています。
（一応、最後にまとめと感想も書いています）

# Web3Signerとは
[Web3Signer](https://github.com/Consensys/web3signer)とは、一般にVaultと呼ばれる外部サービスまたはディスクに保存された秘密鍵を使って、データに署名できるツール[^consensys-signing-tools]です。Consensysによって提供されています。
Vaultと呼ばれる外部サービスとは、Azure Key VaultやAWS KMSなどの鍵管理サービス[^external-vaults]のことです。

また、Web3Signerによる署名は、以下の2つのレイヤーで利用できます。
- 実行レイヤー(eth1): EOAによるトランザクションへの署名
- コンセンサスレイヤー(eth2): バリデータによるデータへの署名

以下は実行レイヤー(eth1)でのトランザクション署名～トランザクション送信のイメージ図です。
(コンセンサスレイヤー(eth2)に関しては、私が理解できていないので省略します！)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624762/05f72d42-fcc4-4ee5-2166-bb8c903babbc.png)

[^consensys-signing-tools]: Consensysが提供する署名ツールには、他にも以下があるようですが、今はWeb3Signerがメインで開発されているようです。
    - [EthSigner](https://github.com/ConsenSys/ethsigner)
    - [Quorum Key Manager](https://github.com/Consensys/quorum-key-manager) 

[^external-vaults]: Web3Signerと連携できる鍵管理サービスについては、[こちら](https://docs.web3signer.consensys.io/how-to/store-keys)を参照してください。


# やったことの概要
まず、基盤部分の準備として、Web3Signerとブロックチェーンネットワークを構築します。
Web3Signerに対しては、秘密鍵情報として暗号化していない平文ファイルを用意します。
ブロックチェーンネットワークには、Hardhatに組み込まれた開発用のEthereumネットワークを用意します。

基盤部分が準備できたら、Web3Signerに対してトランザクション署名をしてもらい、署名済みのトランザクションをブロックチェーンネットワークに送信します。
そして、このトランザクション送信がちゃんと処理されるか確認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624762/d3ae8945-5aea-171f-f2d0-f4fc4ca24c17.png)


# 動作環境
以下の環境で動かしてみました。
Java JDKは、Web3Signerを動かす上で必要です。(バージョンは17以上)
Nodejsは、Hardhatが動けば何でもいいです。

| # | 項目 | バージョン | 
| --- | --- | --- | 
| 1 | OS | Ubuntu 20.04 (WSL) | 
| 2 | Java JDK | 21.0.1 (17より上ならOK) | 
| 3 | Nodejs, npm | 20.9.0, 10.1.0 (Hardhatが動けば何でもOK)| 
| 4 | Web3Signer | 23.11.0 |
| 5 | Hardhat | 2.19.2 |


# 詳細な手順
以下から記載する手順は、作業ディレクトリを作ってその配下で行います。
ディレクトリ名は何でもいいです。
```
mkdir web3signer-test
cd web3signer-test
```

## 必要なパッケージのインストール
まず、Web3SignerとHardhatをインストールします。
(Java JDK,Nodejs,npmはインストール済みの想定です)

### Web3Signerのインストール
以下を実行して、ビルド済みのWeb3Signerをダウンロードし、作業ディレクトリに展開します。
```
wget https://artifacts.consensys.net/public/web3signer/raw/names/web3signer.tar.gz/versions/23.11.0/web3signer-23.11.0.tar.gz
tar xvzf web3signer-23.11.0.tar.gz
```

### Hardhatのインストール
以下を実行して、Hardhatをインストールします。
```
npm install hardhat
```

## ブロックチェーンネットワークの準備
今回は簡単のため、ブロックチェーンネットワークとしてHardhat Networkを使います。
Hardhat Networkとは、Hardhatに組み込まれ、開発用に設計されたローカルのEthereumネットワークです。

### Hardhat Networkの起動
まず以下を実行します。
```
npx hardhat init
```
実行後、以下の画面がでるので、`Create an empty hardhat.config.js`を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624762/075a81ab-80eb-e5cc-4f10-17f0ff265086.png)
その後、以下を実行しHardhat Networkを起動します。
Hardhat Networkは、起動時に10000 ETHを持ったEOAを20個作ってくれます。今回の手順では、その内の2つのEOAを使います。
```
# Hardhat Networkの起動
npx hardhat node

# 以下の2つのEOAを使います
Account #18: 0xdD2FD4581271e230360230F9337D5c0430Bf44C0 (10000 ETH)
Private Key: 0xde9be858da4a475276426320d5e9262ecfc3ba460bfac56360bfa6c4c28b4ee0

Account #19: 0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199 (10000 ETH)
Private Key: 0xdf57089febbacf7ba0bc227dafbffa9fc08a93fdc68e1e42411a14efcf23656e
```

## Web3Signerの起動

Web3Signerは、保管する鍵を`.yaml`形式の「key configurationファイル」で保持します。
そのため、保管する鍵情報を「key configurationファイル」として作成した後に、Web3Signerを起動します。

### key configurationファイルの作成
今回は簡単のため、key configurationファイルを、暗号化していない平文ファイル(Web3Signerの定義だと`file-raw`形式)で作成します。

まず、keysディレクトリを作成します。
```
mkdir keys
```
その後、keysディレクトリ配下に、以下のファイルを配置します。
- keys19.yaml ※名前はこの通りである必要はありませんが、`.yaml`形式にします
    ```
    # Account #19
    type: "file-raw"
    keyType: "SECP256K1"
    privateKey: "0xdf57089febbacf7ba0bc227dafbffa9fc08a93fdc68e1e42411a14efcf23656e"
    ```
:::note info
実際にシステムを組む際は、鍵はAzure Key VaultやAWS KMSなどに保管し、それとWeb3Signerを連携させる方が良いと思います。
詳しく知りたい場合は、以下から調べてみてください。
https://docs.web3signer.consensys.io/how-to/store-keys
:::

### 起動コマンドの実行
以下を実行してWeb3Signerを起動します。
```
./web3signer-23.11.0/bin/web3signer --key-store-path=keys eth1 --chain-id=31337
```
以下は、各オプションの説明です。
- `--key-store-path`
    「key configurationファイル」を配置したディレクトリへのパスを指定します。
- `eth1`
    実行レイヤー(eth1)でWeb3Signerを起動します。`eth2`を指定するととコンセンサスレイヤー(eth2)での起動になります。`eth1`か`eth2`で、使用可能なサブコマンドが変わります。
    - `--chain-id`
        実行レイヤー(eth1)用のサブコマンドです。
        ブロックチェーンネットワークのchainIdを指定します。Hardhat NetworkのデフォルトchainIdである31337を指定しています。

## Web3Signerによるトランザクション署名
基盤部分が用意できたら、Web3Signerでトランザクションを署名します。
ここで署名するトランザクションは、上述のAccount#19からAccount#18へ、ETH送金を行うトランザクションです。
以下を実行し、`eth_signTransaction`で送金トランザクションに署名します。
```
# トランザクションの署名リクエスト(To: Web3Signerのポート9000)
curl -X POST --data '{"jsonrpc":"2.0","method":"eth_signTransaction","params":[{"from": "0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199","to": "0xdD2FD4581271e230360230F9337D5c0430Bf44C0","gas": "0x7600","gasPrice": "0x9184e72a000","value": "0x9184e72a", "nonce":"0x0"}], "id":1}' http://127.0.0.1:9000
```
実行後、以下のような応答が返ってきます。
`result`の項目に入っているのが署名済みのトランザクションです。これをブロックチェーンネットワークに送信します。
```
# 署名済みのトランザクションが返ってくる
{"jsonrpc":"2.0","id":1,"result":"0xf86b808609184e72a00082760094dd2fd4581271e230360230f9337d5c0430bf44c0849184e72a8082f4f5a0ea487bb8f9fb8413e1f7f45e1e5845ab0a61005414e4828ab2adf5c9d743d57ba06ef102661655699748bca4fe210c50dc459e7be1f25e4dde2624044024c53de2"}
```

## 署名したトランザクションの送信
Web3Signerで署名したトランザクションをブロックチェーンネットワークに送信します。
以下を実行し、`eth_sendRawTransaction`で上記の署名済みトランザクションをブロックチェーンネットワークへ送信します。
(`params`の項目に上記の署名済みトランザクションが入っています。)
```
# トランザクション送信リクエスト(To: Hardhat Networkノードのポート8545)
curl -X POST --data '{"jsonrpc":"2.0","method":"eth_sendRawTransaction","params":["0xf86b808609184e72a00082760094dd2fd4581271e230360230f9337d5c0430bf44c0849184e72a8082f4f5a0ea487bb8f9fb8413e1f7f45e1e5845ab0a61005414e4828ab2adf5c9d743d57ba06ef102661655699748bca4fe210c50dc459e7be1f25e4dde2624044024c53de2"],"id":1}' http://127.0.0.1:8545
```
送信成功すると、Hardhat Networkを起動しているターミナルで以下が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624762/2ad4bfd1-6642-16fe-6c1c-c6ae8844aab7.png)

## (補足) eth_sendTransaction(署名&送信)の使い方
補足ですが、`eth_sendTransaction`を使うと、上記の署名(`eth_signTransaction`)と送信(`eth_sendRawTransaction`)をまとめて行うことができます。
署名と送信の2ステップに分ける必要が無く、簡便にトランザクションを送りたい場合は、`eth_sendTransaction`を使うことで1リクエストで2ステップを行えます。
(反対に、署名と送信の2ステップに分けると、シーケンスを詳細にコントロールできるという利点があるので、2ステップに分ける分けないの使い分けができるという点が便利なポイントです。)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624762/e05ce6ca-c6e3-1e6f-768e-84dcaa0419a7.png)

ただ、`eth_sendTransaction`を使うためには、Web3Signerに対して、ブロックチェーンノードへのdownstream設定をする必要があります。
以下を実行してWeb3Signerを起動すると、Web3Signerがブロックチェーンノード(ポート番号8545)へアクセスしてくれるようになります。
```
./web3signer-23.11.0/bin/web3signer --key-store-path=keys eth1 --chain-id=31337 --downstream-http-port=8545
```

downstreamの設定をすると、`eth_sendTransaction`を使えるようになります。
以下を実行すると、一度でトランザクションの署名から送信まで行ってくれます。
```
# トランザクションの署名&送信リクエスト(To: Web3Signerのポート9000)
curl -X POST --data '{"jsonrpc":"2.0","method":"eth_sendTransaction","params":[{"from": "0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199","to": "0xdD2FD4581271e230360230F9337D5c0430Bf44C0","gas": "0x7600","gasPrice": "0x9184e72a000","value": "0x9184e72a"}], "id":1}' http://127.0.0.1:9000
```

# まとめと感想
本記事ではWeb3Signerの紹介をし、簡単な構成と簡単な鍵管理方法でWeb3Signerを起動してみました。
そして、Web3Signerの実行レイヤー(eth1)での署名機能を使ったトランザクション署名&送信を試してみて、正常にトランザクション処理がされることを確認しました。

今回Web3Signerを触ってみて、Web3Signerの起動方法やAPIの使い方が分かったのは良かったです。
また、Web3Signerに使ってもらう秘密鍵情報を、暗号化していない平文ファイルで用意できるので、テスト環境の構築や今回のような簡易的な評価なら簡単にできるのは便利だと感じました。

ただ、Web3Signerを使う上で重要なポイントは「秘密鍵をセキュアに保管/利用する方法」だと思いますので、既存の鍵管理サービス(Azure Key Vault, AWS KMSなど)との連携方法をさらに調査しないといけないなと思いました。
また、コンセンサスレイヤー(eth2)の署名に関しても、バリデータノードのコンセンサス関連の処理について勉強しながら理解していきたいと思いました。

# 参考
- EthSignerの使い方(本当に参考になりました。ありがとうございます。)
    https://qiita.com/kojima_k/items/a0b66d68ba209b245828
- Web3Signerの公式ドキュメント
    - イントロダクション: https://docs.web3signer.consensys.io/#what-can-you-do-with-web3signer
    - インストール手順: https://docs.web3signer.consensys.io/get-started/install-binaries
    - JSON-RPC API: https://docs.web3signer.consensys.io/reference/api/json-rpc
    - 鍵ファイルの書き方: https://docs.web3signer.consensys.io/reference/key-config-file-params
    - 起動オプション: https://docs.web3signer.consensys.io/reference/cli/options
    - 実行レイヤー(eth1)用のサブコマンド: https://docs.web3signer.consensys.io/reference/cli/subcommands#eth1