8
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

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

これまでの記事では、Tapyrus ブロックチェーンのローカル環境構築から、RPC を使った基本操作、そしてブロック生成までを試してきました。

本記事では、既に得たコインベース報酬(UTXO)を使って、別アドレスに TPC を送金することに挑戦します。

これまでの記事

Tapyrus とは

TapyrusBitcoin をベースにしたオープンソースのブロックチェーンプラットフォームで、日本の企業 Chaintope によって開発されています。企業や自治体での実用を想定した設計が特徴です。

手元の環境

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

  • OS:LMDE 6(Linux Mint Debian Edition)
  • Docker:28.2.2
  • Docker Compose:v2.36.2
  • シェル:bash
  • その他:インターネット接続(イメージ取得のため)

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

手順概要

今回の流れは以下の通りです:

  1. コインベース報酬を mature(使用可能状態)にする
  2. 送金先アドレスを生成
  3. sendtoaddress コマンドで送金
  4. 確認用ブロックを生成し、送金トランザクションをブロックに取り込む
  5. UTXO 状態の変化を確認

ノードとクライアント起動

以下は、以前の記事 で作成した Docker Compose 環境がすでに存在している前提です。

ターミナル
docker compose up -d tapyrusd
docker compose run --rm tapyrus-client

これで Tapyrus ノードがバックグラウンドで起動し、Ruby クライアント(IRB セッション)が開始されます。

コインベース報酬の成熟(ブロック生成)

Tapyrus の dev モード(dev=1)では、コインベース報酬は 1 ブロックの確認 で spendable(使用可能)になります。
まずは 1 ブロックを生成して報酬を使用可能にしましょう。

報酬受取用アドレスの生成とアグリゲート秘密鍵のインポート

まず、コインベース報酬の送り先となるアドレスを新規に生成します。

IRB
addr_mine = $client.getnewaddress

次に、Tapyrus dev モードで必要となる アグリゲート秘密鍵 をインポートします。

IRB
agg_wif = "cUJN5RVzYWFoeY8rUztd47jzXCu1p57Ay8V7pqCzsBD3PEXN7Dd4"
$client.importprivkey(agg_wif, "dev_aggkey", false)
  • 第1引数:インポートする秘密鍵(WIF 形式)
  • 第2引数:任意のラベル名(ここでは "dev_aggkey"
  • 第3引数:false を指定するとウォレットの再スキャンをスキップ(処理が高速化されます)

ブロックを生成

これで準備が整ったので、ブロックを生成します。

IRB
$client.generatetoaddress(1, addr_mine, agg_wif)
  • 第1引数:生成するブロック数(今回は 1)
  • 第2引数:コインベース報酬の送り先アドレス
  • 第3引数:署名に使用するアグリゲート秘密鍵

これで、50 TPC のコインベース報酬が 1 確認済み で使える状態(confirmations=1, spendable=true )になります。

UTXO 状態の確認

以下のコマンドで、現在の UTXO 一覧を確認してみます。

IRB
$client.listunspent.find { |u| u["confirmations"] == 1 }
出力例
{"txid" => "09e260b9d1e5afe0a252e7cf1205c4c13a6278357d656f8258aeeda8d27b4a52",
 "vout" => 0,
 "address" => "mqopVBKSY2TkXoJx3kdPbHPrChsXDvCCUB",
 "token" => "TPC",
 "amount" => "50.0",
 "label" => "",
 "scriptPubKey" => "76a91470e1cfad1d447569775a700dcd14acf6e69bbd0e88ac",
 "confirmations" => 1,
 "spendable" => true,
 "solvable" => true,
 "safe" => true}

送金先アドレスの生成

次に、送金先となる新しいアドレスをウォレット内で作成します。

IRB
addr_recv = $client.getnewaddress

このコマンドにより、ウォレットが新しい受取用アドレスを 1 つ生成し、文字列として返してくれます。

今回はこのアドレス(addr_recv)を送金先とし、先ほど生成したコインベース UTXO をここに送ります。

dev モードでは、すべてのアドレスはローカルウォレット内で管理されています。外部ノードや外部ウォレットが存在しないため、「同一ノード内での自己送金」という形になります。

UTXO を使って送金

準備が整ったので、実際に UTXO を使って 10 TPC を先ほど生成したアドレスに送金してみます。

Tapyrus Core では、ウォレット内の未使用 UTXO から送金額と手数料分を消費して、新しいトランザクションが作成されます。

以下のコマンドで、指定したアドレスに 10 TPC を送ります。

IRB
txid = $client.sendtoaddress(addr_recv, 10.0)
  • 第1引数:送信先アドレス
  • 第2引数:送金額(単位は TPC)

戻り値は、作成されたトランザクションの txid(トランザクション ID) です。

手数料について

特に設定しない場合、dev モードでは非常に小さい固定手数料(例:0.000045 TPC) が自動で適用されます。

ウォレット残高が少ない場合でも、通常はこのままで問題なく送金が完了します。

トランザクション情報を確認

送金直後のトランザクション状態は、以下のように確認できます。

IRB
info = $client.gettransaction(txid)
出力例
{"confirmations" => 0,
 "trusted" => true,
 "txid" => "e9da62fb5200ace43f9ae1f70da082120b6c8d28dc7128f6ae660e56431dd8d4",
 "walletconflicts" => [],
 "time" => 1751372216,
 "timereceived" => 1751372216,
 "bip125-replaceable" => "no",
 "details" =>
  [{"address" => "mrgEDw3W3Ht75yF3Gdmp3G3CtxrSXs6Ukn",
    "category" => "send",
    "token" => "TPC",
    "amount" => "-10.0",
    "label" => "",
    "vout" => 0,
    "fee" => "-4.5e-05",
    "abandoned" => false},
   {"address" => "mrgEDw3W3Ht75yF3Gdmp3G3CtxrSXs6Ukn",
    "category" => "receive",
    "token" => "TPC",
    "amount" => "10.0",
    "label" => "",
    "vout" => 0}],
 "hex" =>
  "0100000001524a7bd2a8edae58826f657d3578623ac1c40512cfe752a2e0afe5d1b960e209000000006a473044022013e7bba19a3242c2ef2e213af417de62299e40c748f075d11b028b6396566a820220068a56995ee8a2574db80ce5c5c7d03c57b86c2990a4e489b8b9b82873bc2c99012103b0499b7a7bb348b227cd5b10eea7b5174884c6cea27ab99847b92ed3af32362efeffffff0200ca9a3b000000001976a9147a6a6c91069323f3f32e2fb6763dd47d995b24f388ac6c166bee000000001976a914dc6eda65c811bb975bde6462f8b63270a8c8694888ac00000000"}
  • confirmations: 現在の確認数(送金直後は 0
  • details: 各アドレスごとの出入金情報
  • hex: 生のトランザクションデータ(hex)

送金直後は未承認(confirmations = 0)です。

IRB
info["confirmations"]
#=> 0
IRB
sent = info["details"].find { |d| d["category"] == "send" }["amount"]
#=> "-10.0"

fee  = info["details"].find { |d| d["category"] == "send" }["fee"]
#=> "-4.5e-05"

recv = info["details"].find { |d| d["category"] == "receive" }["amount"]
#=> "10.0"

確認用ブロックを 1 つ生成

先ほど送信したトランザクションは、現在 mempool(未承認トランザクションプール) に入っている状態です。
これをブロックに取り込み、confirmation = 1 の状態にするためには、新たに 1 ブロックを生成する必要があります。

IRB
$client.generatetoaddress(1, addr_mine, agg_wif)

このコマンドにより、未承認だったトランザクションが新しいブロックに取り込まれ、正式に承認(confirmation = 1) されます。

トランザクションの確認数を再チェックしてみます。

IRB
$client.gettransaction(txid).fetch("confirmations")
#=> 1

confirmations が 1 に増えていれば成功です。

これで、送金トランザクションがブロックチェーンに記録されたことが確認できました。

UTXO 状態の変化を確認

最後に、今回の送金によってウォレット内の UTXO がどのように変化したかを確認します。

送金前には「50 TPC のコインベース UTXO」が 1 つだけ存在していましたが、送金後は以下のように変化しているはずです。

  • 送金先アドレス(addr_recv)宛: 10 TPC の新しい UTXO
  • お釣りアドレス: 約 39.9999 TPC のお釣りUTXO(ウォレット内で自動生成)

入出力詳細を確認

IRB
$client.gettransaction(txid).fetch("details")
出力例
[{"address" => "mrgEDw3W3Ht75yF3Gdmp3G3CtxrSXs6Ukn",
  "category" => "send",
  "token" => "TPC",
  "amount" => "-10.0",
  "label" => "",
  "vout" => 0,
  "fee" => "-4.5e-05",
  "abandoned" => false},
 {"address" => "mrgEDw3W3Ht75yF3Gdmp3G3CtxrSXs6Ukn",
  "category" => "receive",
  "token" => "TPC",
  "amount" => "10.0",
  "label" => "",
  "vout" => 0}]

全体の UTXO 一覧を確認

まずは、現在ウォレット内に存在する全 UTXO を確認します。

IRB
$client.listunspent
出力例
[{"txid" => "fca123725a5cb57d11d415a9b9095b91ad3ac5687a4a0e3fb1a4ea660133220c",
  "vout" => 0,
  "address" => "mncZjvY8bw7GT9VVDs6KC1jomezPV2q9xC",
  "token" => "TPC",
  "amount" => "10.0",
  "label" => "",
  "scriptPubKey" => "76a9144dd88c95444cef495d832b6a38075081dc33912388ac",
  "confirmations" => 1,
  "spendable" => true,
  "solvable" => true,
  "safe" => true},
 {"txid" => "fca123725a5cb57d11d415a9b9095b91ad3ac5687a4a0e3fb1a4ea660133220c",
  "vout" => 1,
  "address" => "n3JX9XtYiWGQFHTvVCcpAMWu6fMypU575R",
  "token" => "TPC",
  "amount" => "39.999955",
  "scriptPubKey" => "76a914eef8c8bc1bdd90c64af786d5028adc44a9c530b988ac",
  "confirmations" => 1,
  "spendable" => true,
  "solvable" => true,
  "safe" => true},
 {"txid" => "c61da3212313bb612deb8ff7f365cac8823cad1b9eb5825c30fba2acd394b126",
  "vout" => 0,
  "address" => "mvASke4rE4zrfne3NGE8LoUFSCrpv58UJD",
  "token" => "TPC",
  "amount" => "50.000045",
  "label" => "",
  "scriptPubKey" => "76a914a0a8e84c693edfce761d2d8722ee344392a5306f88ac",
  "confirmations" => 1,
  "spendable" => true,
  "solvable" => true,
  "safe" => true}]

この中で、以下が確認できるはずです:

  • 送金先アドレス(addr_recv)宛の 10 TPC の新規 UTXO
  • ウォレット内で自動生成された change アドレス宛の 約 39.9999 TPC のお釣り UTXO
  • 未使用の他の UTXO(もしウォレットに他の残高があれば)

今回は同一ウォレット内で「自己送金」しているため、
送金先アドレスもお釣りアドレスも、どちらも自分のウォレットの UTXO として一覧に出てきます。

送金先アドレス側の UTXO

次に、送金先アドレス(addr_recv)で新たに受け取った 10 TPC の UTXO を確認します。

IRB
$client.listunspent.select { |u| u["address"] == addr_recv }
出力例
[{"txid" => "e9da62fb5200ace43f9ae1f70da082120b6c8d28dc7128f6ae660e56431dd8d4",
  "vout" => 0,
  "address" => "mrgEDw3W3Ht75yF3Gdmp3G3CtxrSXs6Ukn",
  "token" => "TPC",
  "amount" => "10.0",
  "label" => "",
  "scriptPubKey" => "76a9147a6a6c91069323f3f32e2fb6763dd47d995b24f388ac",
  "confirmations" => 1,
  "spendable" => true,
  "solvable" => true,
  "safe" => true}]

10 TPC の UTXO が新たに生成されているはずです。

トランザクションの構造確認

もしトランザクションの中身(入出力詳細)を確認したい場合は、以下のように生トランザクションをデコードできます。

IRB
raw = $client.getrawtransaction(txid)
$client.decoderawtransaction(raw)
出力例
{"txid" => "e9da62fb5200ace43f9ae1f70da082120b6c8d28dc7128f6ae660e56431dd8d4",
 "hash" => "948aec037bb3036b090a02e0601dbf140051328f1f3aefd4e98a5d483b3f924f",
 "features" => 1,
 "size" => 225,
 "locktime" => 0,
 "vin" =>
  [{"txid" => "09e260b9d1e5afe0a252e7cf1205c4c13a6278357d656f8258aeeda8d27b4a52",
    "vout" => 0,
    "scriptSig" =>
     {"asm" =>
       "3044022013e7bba19a3242c2ef2e213af417de62299e40c748f075d11b028b6396566a820220068a56995ee8a2574db80ce5c5c7d03c57b86c2990a4e489b8b9b82873bc2c99[ALL] 03b0499b7a7bb348b227cd5b10eea7b5174884c6cea27ab99847b92ed3af32362e",
      "hex" =>
       "473044022013e7bba19a3242c2ef2e213af417de62299e40c748f075d11b028b6396566a820220068a56995ee8a2574db80ce5c5c7d03c57b86c2990a4e489b8b9b82873bc2c99012103b0499b7a7bb348b227cd5b10eea7b5174884c6cea27ab99847b92ed3af32362e"},
    "sequence" => 4294967294}],
 "vout" =>
  [{"token" => "TPC",
    "value" => "10.0",
    "n" => 0,
    "scriptPubKey" =>
     {"asm" =>
       "OP_DUP OP_HASH160 7a6a6c91069323f3f32e2fb6763dd47d995b24f3 OP_EQUALVERIFY OP_CHECKSIG",
      "hex" => "76a9147a6a6c91069323f3f32e2fb6763dd47d995b24f388ac",
      "reqSigs" => 1,
      "type" => "pubkeyhash",
      "addresses" => ["mrgEDw3W3Ht75yF3Gdmp3G3CtxrSXs6Ukn"]}},
   {"token" => "TPC",
    "value" => "39.999955",
    "n" => 1,
    "scriptPubKey" =>
     {"asm" =>
       "OP_DUP OP_HASH160 dc6eda65c811bb975bde6462f8b63270a8c86948 OP_EQUALVERIFY OP_CHECKSIG",
      "hex" => "76a914dc6eda65c811bb975bde6462f8b63270a8c8694888ac",
      "reqSigs" => 1,
      "type" => "pubkeyhash",
      "addresses" => ["n1cVkTzb7JB35RDufBLsX6mo5FAc9WDSeS"]}}]}

おわりに

本記事では、Tapyrus dev モード環境で得たコインベース報酬から、別アドレスへの送金と UTXO 状態の変化を体験しました。

資料

8
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?