Tapyrus ブロックチェーンに挑戦: ブロックを生成してみる

## はじめに

「ブロックチェーン技術を学びたい」と思いながらも、なかなか行動に移せずにいました。
ローカル環境で Tapyrus ブロックチェーンを触ってみることで、「ブロックチェーンとは何か？」を体験しながら学んでいきます。

本記事では、[以前の記事](https://qiita.com/mnishiguchi/items/2cd72b485ddf2e178f3f) で構築した Docker Compose 環境を前提に、Ruby 製クライアント（[tapyrusrb]()）を使ってブロックを1つだけ生成し、その結果を確認することに挑戦します。

## Tapyrus とは

[Tapyrus][tapyrus-core-github] は [Bitcoin][bitcoin-org] をベースにしたオープンソースのブロックチェーンプラットフォームで、日本の企業 [Chaintope][chaintope-site] によって開発されています。企業や自治体での実用を想定した設計が特徴です。

## 手元の環境

今回の動作確認は以下のローカル環境で行いました：

* OS：LMDE 6（Linux Mint Debian Edition）
* [Docker][docker-site]：28.2.2
* [Docker Compose][docker-compose-docs]：v2.36.2
* シェル：[bash][bash-site]
* その他：インターネット接続（イメージ取得のため）

※ Docker 環境があれば、macOS や WSL2 でも同様に動くはずです。

## ブロックを生成してみる

以下は、[以前の記事](https://qiita.com/mnishiguchi/items/2cd72b485ddf2e178f3f) で構築した Docker Compose 環境が既に準備済みであることを前提としています。

### Tapyrus ノードを起動

`tapyrusd` サービスをバックグラウンドで起動します。  
JSON-RPC サーバはポート `2377` で待ち受けを開始します。

```bash:ターミナル
docker compose up -d tapyrusd
```

### Ruby クライアントを起動

`tapyrus-client` を起動して IRB セッションに入ります。

この時点で `$client` が `Tapyrus::RPC::TapyrusCoreClient` インスタンスとして使える状態です。

```bash:ターミナル
docker compose run --rm tapyrus-client
```

```ruby:IRB
irb>
```

### 新しいアドレスを取得

ブロック報酬の送金先となるアドレスを生成します。
このアドレスを変数 `addr` に保存しておきます。

```ruby:IRB
irb> addr = $client.getnewaddress
#=> "mpPpXvtfRqFFZnWDexSVH2gZU7w4ndsZkL"
```

### アグリゲート秘密鍵をインポート

Tapyrus の dev モードでは、ブロックを生成する際に対応するアグリゲート公開鍵に対する「秘密鍵」での署名が必要です。
この秘密鍵（WIF形式）は [Tapyrus Core の Docker ドキュメント][tapyrus-core-docs-docker] にて公開されています。

```ruby:IRB
irb> agg_wif = "cUJN5RVzYWFoeY8rUztd47jzXCu1p57Ay8V7pqCzsBD3PEXN7Dd4"
```

この鍵をノードにインポートして、署名可能にします。

```ruby:IRB
irb> $client.importprivkey(agg_wif, "dev_aggkey", false)
#=> nil
```

- 第1引数：インポートする秘密鍵（WIF形式）
- 第2引数：ラベル（任意）
- 第3引数：`false` でウォレットの再スキャンを省略（高速化のため）

### ブロックを 1 個生成

アグリゲート秘密鍵をインポートしたら、いよいよブロックを生成してみます。  

`generatetoaddress` メソッドを使うと、指定したアドレス宛にブロック報酬が送られたブロックを 即座に 生成できます。

```ruby:IRB
irb> $client.generatetoaddress(1, addr, agg_wif)
#=> ["f1c6c92140c194291aa7423a4f788d41d1796fe06fbe7e4f6a25ffb8144e3b43"]
```

- 第1引数：生成するブロック数（今回は 1）
- 第2引数：ブロック報酬の送り先アドレス（先ほど取得した `addr`）
- 第3引数：署名に使用するアグリゲート秘密鍵（`agg_wif`）

配列で返される値は、生成されたブロックのハッシュです。
PoW（プルーフ・オブ・ワーク） をスキップできる dev モードでは、ブロック生成が即完了します。

### チェーン情報を確認

ブロックが正しく追加されたかどうかを `getblockchaininfo` で確認します。

```ruby:IRB
irb> $client.getblockchaininfo
=>
{"chain" => "1905960821",
 "mode" => "dev",
 "blocks" => 1,
 "headers" => 1,
 "bestblockhash" => "f1c6c92140c194291aa7423a4f788d41d1796fe06fbe7e4f6a25ffb8144e3b43",
 "mediantime" => 1749250809,
 "verificationprogress" => 1,
 "initialblockdownload" => false,
 "size_on_disk" => 606,
 "pruned" => false,
 "aggregatePubkeys" => [{"03af80b90d25145da28c583359beb47b21796b2fe1a23c1511e443e7a64dfdb27d" => 0}],
 "maxBlockSizes" => [{"1000000" => 0}],
 "warnings" => ""}

```

- `blocks` と `headers` が `1` に増えていれば成功です。
- `bestblockhash` が先ほど返ってきたブロックハッシュと一致することを確認します。

### UTXO を確認

最後に、生成されたブロックにより新たに発生した UTXO（未使用トランザクション出力）を確認します。

```ruby:IRB
irb> $client.listunspent
=>
[{"txid" => "792824ba1ac90a9a5bdad8f3c6c563929a29ed3a84f2d45ba418a6d1582d0939",
  "vout" => 0,
  "address" => "n2ihuhVBuSTqPLCRpGMxzoSnS9m5aesFGS",
  "token" => "TPC",
  "amount" => "50.0",
  "label" => "",
  "scriptPubKey" => "76a914e8939fe8c088d109484decd71d4b35fd5ea3a40888ac",
  "confirmations" => 1,
  "spendable" => true,
  "solvable" => true,
  "safe" => true}]
```

- `amount: "50.0"` → ブロック報酬は 50 TPC
- `confirmations: 1` → ブロックが 1 回確認されたことを意味します

このようにして、報酬が反映された UTXO を自分のウォレットで確認できます。

## おわりに

本記事では、Tapyrus dev モードノードに対して「ブロックを 1 つだけ生成する」手順を紹介しました。

dev モードではブロック生成が瞬時に完了するため、手軽に「マイニング体験」ができます。

次回以降は、この UTXO を実際に送金してみたりして、さらにブロックチェーンの動きを深堀りしていこうと思います。

## 資料

* [Tapyrus ブロックチェーンに挑戦 – 環境構築（dev モード）](https://qiita.com/mnishiguchi/items/2cd72b485ddf2e178f3f)
* [Tapyrus ブロックチェーンに挑戦: RPC を叩いてみる](https://qiita.com/mnishiguchi/items/…)
* [Tapyrus Core GitHub][tapyrus-core-github]
* [Tapyrus Docker イメージ][tapyrusd-docker-hub]
* [Tapyrus 開発者向けドキュメント（Docker）][tapyrus-core-docs-docker]
* [Tapyrus 開発者向けドキュメント（Getting Started）][tapyrus-core-docs-getting-started]
* [Tapyrus Ruby gem (tapyrusrb)][tapyrusrb]
* [Chaintope 公式サイト][chaintope-site]
* https://qiita.com/John110/items/0ac6deb458af976b0cf8
* https://qiita.com/mnishiguchi/items/2cd72b485ddf2e178f3f

<!-- リンク定義 -->

[bitcoin-org]: https://bitcoin.org/
[bash-site]: https://www.gnu.org/software/bash/
[chaintope-site]: https://www.chaintope.com/
[docker-site]: https://www.docker.com/
[docker-compose-docs]: https://docs.docker.com/compose/
[irb-docs]: https://docs.ruby-lang.org/ja/latest/library/irb.html
[rpc-wiki]: https://ja.wikipedia.org/wiki/Remote_Procedure_Call
[ruby-site]: https://www.ruby-lang.org/
[tapyrus-core-github]: https://github.com/chaintope/tapyrus-core
[tapyrus-core-docs-docker]: https://github.com/chaintope/tapyrus-core/blob/master/doc/docker_image.md
[tapyrus-core-docs-getting-started]: https://github.com/chaintope/tapyrus-core/blob/master/doc/tapyrus/getting_started.md
[tapyrusrb]: https://github.com/chaintope/tapyrusrb
[tapyrusd-docker-hub]: https://hub.docker.com/r/tapyrus/tapyrusd