Taproot Assets Protocol を理解する(実践編 #1)

Hiroki Gondo
Nayuta エンジニアブログ
30 min readMay 8, 2023

Taproot Assets Protocol(旧 Taro、以下 TAP と称す)は Bitcoin ネットワークのオーバレイネットワークで Bitcoin とは異なるアセットを表現するためのプロトコルである。TAP の仕様の詳細については、以前の記事をご覧いただきたい。

この記事では実際に TAP デーモン(tapd)を実行し、Taproot Asset の生成と転送を行うことにより仕様の理解を深めることを目的にしている。基本的に Lightning Labs が開発している tapd 実装の github のフロントページ の手順で行うが、より詳細な説明や注意点を加えている。

注意!完全に tapd の実装を確認したわけではないのでこの記事の一部に推測を含みます。内容に誤りがある可能性があります :)

1. lnd を動かす

tapd を動かすためには、同じく Lightning Labs が開発している Lightning Network デーモンの lnd が必要になる。TAP のトランザクション発行は Bitcoin の 1st layer 上で行われるので Lightning Network(2nd layer) の機能は必要ないが(TAP の Lightning Network 対応は最も重要なマイルストーンであろう)、lnd を介して Bitcoin の 1st layer にアクセスする。ここでは Bitcoin のテストネットを使用する。

私のテスト環境は以下の通り。

$ sw_vers
ProductName: macOS
ProductVersion: 13.2.1
BuildVersion: 22D68
$ go version
go version go1.20.3 darwin/amd64

lnd をインストールし実行する。

$ git clone https://github.com/lightningnetwork/lnd.git
$ cd lnd
$ git log | head -1
commit 8c58730c34022cd9f3c8c7c408c0783a751f2404
$ make install tags="signrpc walletrpc chainrpc invoicesrpc"
$ lnd -V
lnd version 0.16.99-beta commit=v0.16.0-beta-332-g8c58730c3
$ lnd --bitcoin.active --bitcoin.testnet --bitcoin.node=neutrino

別のターミナルから専用のコマンドラインツール(lncli)で wallet を作成する。

$ lncli -network=testnet create
Input wallet password:
Confirm password:

Do you have an existing cipher seed mnemonic or extended master root key you want to use?
Enter 'y' to use an existing cipher seed mnemonic, 'x' to use an extended master root key
or 'n' to create a new seed (Enter y/x/n): n

Your cipher seed can optionally be encrypted.
Input your passphrase if you wish to encrypt it (or press enter to proceed without a cipher seed passphrase):

Generating fresh cipher seed...



lnd successfully initialized!

lnd を外部(tapd)からアクセスするためには wallet が unlock 状態でなければならない。wallet 作成直後は wallet が unlock 状態になっているが、もし lnd を再起動した場合には wallet を unlock する必要がある。

$ lncli --network=testnet unlock
Input wallet password:
lnd successfully unlocked!

チェーンの同期にしばらく時間が掛かるため状態が SERVER_ACTIVE になるまで待つ。これで lnd の準備ができた。

$ lncli state
{
"state": "RPC_ACTIVE"
}




$ lncli state
{
"state": "SERVER_ACTIVE"
}
$ lncli --network=testnet getinfo
{

"block_height": 2431595,

"synced_to_chain": true,

2. tapd を動かす

tapd をインストールし、実行する。

$ git clone https://github.com/lightninglabs/taproot-assets.git
$ cd taproot-assets
$ git log | head -1
commit 1a3f86c09ac044ff3b9e27cf90a314eecb2e0625
$ make install
$ tapd --network=testnet

3. Taproot Asset を発行する

別のターミナルから専用のコマンドラインツール(tapcli)でアセットを発行する。

$ tapcli assets mint --type normal --name fantasycoin --supply 100 --meta_bytes "fantastic money"
{
"batch_key": "0326764481f0945fea8cc696a52753d30409026d7dfd5652beb4904dfc44c238d1"
}
$ tapcli assets mint finalize
{
"batch_key": "0326764481f0945fea8cc696a52753d30409026d7dfd5652beb4904dfc44c238d1"
}

tapd でエラー発生!

2023-05-23 17:39:55.398 [ERR] GRDN: unable to advance state machine: unable to advance state machine: unable to fund psbt: unable to fund psbt: rpc error: code = Unknown desc = wallet couldn't fund PSBT: error creating funding TX: insufficient funds available to construct transaction

アセットを発行するためには下層の Bitcoin トランザクションを発行する必要がある。そのためには lnd の wallet にある程度の量の Bitcoin が必要になる。

lnd でアドレスを発行し、「どこか」からか「十分な量」の Bitcoin を入金する。。

$ lncli -network=testnet newaddress p2tr
{
"address": "tb1pvs0jjwxal4deezmvc0zk7w89n3d44m6f4a3fn3xfxne7as7a9qhq86gj0v"
}

Bitcoin(Testnet)は Bitcoin Testnet Faucet などの faucet から無料で貰える。アセットを含んだ Bitcoin トランザクションを発行するためには(現在の tapd の実装では)1000 sats の出力が必要になるが、トランザクション手数料がその 10 倍以上必要になる場合がある。

このぐらいの量があれば十分だが、集めるのは結構大変だろう ;(

$ lncli -network=testnet walletbalance
{
"total_balance": "100000",
...
}

tapd を再起動するとアセットがリストに表示される(まだ発行は完了していない)。

$ tapcli assets list
{
"assets": [
{
"version": 0,
"asset_genesis": {
"genesis_point": "ba779153a792a1d49433fd18e56311f8d212992e7d1405cb14af8dffb34e88ce:0",
"name": "fantasycoin",
"meta_hash": "04e552053fd4c8e2c01bc14cb9a0ce00f07d4ffdffff68fe455c70b934b22a43",
"asset_id": "20cecdb6626705bf26ad036084f7423082db6114f0de84046ebf2e84f6852874",
"output_index": 0,
"version": 0
},
"asset_type": "NORMAL",
"amount": "100",
"lock_time": 0,
"relative_lock_time": 0,
"script_version": 0,
"script_key": "02b4c71447e74672f8cd5b50a6b430fc73b3caff7866dc905a502fe8adefad3b31",
"script_key_is_local": true,
"asset_group": null,
"chain_anchor": {
"anchor_tx": "02000000000101ce884eb3ff8daf14cb05147d2e9912d2f81163e518fd3394d4a192a7539177ba0000000000ffffffff02e8030000000000002251203ad594d71881fe6b0e35d2e6e40dd9ee67eac616ee38e65aff7fe7e7283af03372640100000000002251206e83207da3b36b5f8e84a55171e2ac6f21d12c5eb0384a157a9bf927fd88bc6401409722a654cd08582fe39fcbe52be917b2bcdf281ad5244bf058562cea07155a7e75ee68dc4be21842296392c22cc7580a6e118d9b38944014667e69455efe10f200000000",
"anchor_txid": "ebe73fb60dfa99d191ed1e43a0509cc93c5223fa202656c469e01d6abfd66356",
"anchor_block_hash": "0000000000000000000000000000000000000000000000000000000000000000",
"anchor_outpoint": "ebe73fb60dfa99d191ed1e43a0509cc93c5223fa202656c469e01d6abfd66356:0",
"internal_key": "03d9f42daae1b7832d77d3ec83ddbb62e71266f6aedf6bcceb944e9672177c9301",
"merkle_root": "634ff6d86b8889f119f505a9bcba38fe4c6bda4b5a40a439fce37184badff63f",
"tapscript_sibling": null
},
"prev_witnesses": [
],
"is_spent": false
}
]
}

TAP の仕様を理解していれば、ここに表示されている要素のほとんどを理解することができるだろう。

このアセット発行で、消費された Bitcoin UTXO
ba779153a792a1d49433fd18e56311f8d212992e7d1405cb14af8dffb34e88ce:0
が、当該アセットを一意に識別するための genesis_point となる。

これを消費した Bitcoin トランザクション
ebe73fb60dfa99d191ed1e43a0509cc93c5223fa202656c469e01d6abfd66356
の新たな Bitcoin UTXO(anchor_outpoint)に発行されたアセットの UTXO が含まれる(アンカリングされている)。

このトランザクションがチェーン上で確認されるとアセットの発行が完了する。

4. TAP Asset を転送する

今回は送受信側いずれも同じ lnd / tapd を使用するので自分で自分に送ることになる。

先ほどの tapcli assets list の結果に含まれる asset_id を指定してアセットを 21 単位受信するためのアドレスを生成する。

$ tapcli addrs new --asset_id 20cecdb6626705bf26ad036084f7423082db6114f0de84046ebf2e84f6852874 --amt 21
{
"encoded": "taptb1qqqsqq3qyr8vmdnzvuzm7f4dqdsgfa6zxzpdkcg57r0ggprwhuhgfa599p6qgggzy56hls2gc3qlh4w9affnl4krx6rxj7t8588ccjfk9pug556lgv7qvggr8epd00wrc5tpctwygpn8kr60efxy5lfj6yl04sf2wxv5v5dsfr8qsqg4nd4u85",
"asset_id": "20cecdb6626705bf26ad036084f7423082db6114f0de84046ebf2e84f6852874",
"asset_type": "NORMAL",
"amount": "21",
"group_key": null,
"script_key": "0225357fc148c441fbd5c5ea533fd6c33686697967a1cf8c493628788a535f433c",
"internal_key": "033e42d7bdc3c5161c2dc440667b0f4fca4c4a7d32d13efac12a71994651b048ce",
"tapscript_sibling": null,
"taproot_output_key": "be0db94139918a132eb4821f834efdec4634261997b57d964753d60923be9dee"
}

アドレスを指定してアセットを送る。

$ tapcli assets send --addr taptb1qqqsqq3qyr8vmdnzvuzm7f4dqdsgfa6zxzpdkcg57r0ggprwhuhgfa599p6qgggzy56hls2gc3qlh4w9affnl4krx6rxj7t8588ccjfk9pug556lgv7qvggr8epd00wrc5tpctwygpn8kr60efxy5lfj6yl04sf2wxv5v5dsfr8qsqg4nd4u85
{
"transfer": {
"transfer_timestamp": "1684836471",
"anchor_tx_hash": "e4efa1c3272009193e961f383b29c1bc84cf6ed8eb0806bf94056a41387835b3",
"anchor_tx_height_hint": 2434958,
"anchor_tx_chain_fees": "12725",
"inputs": [
{
"anchor_point": "ebe73fb60dfa99d191ed1e43a0509cc93c5223fa202656c469e01d6abfd66356:0",
"asset_id": "20cecdb6626705bf26ad036084f7423082db6114f0de84046ebf2e84f6852874",
"script_key": "02b4c71447e74672f8cd5b50a6b430fc73b3caff7866dc905a502fe8adefad3b31",
"amount": "100"
}
],
"outputs": [
{
"anchor": {
"outpoint": "b3357838416a0594bf0608ebd86ecf84bcc1293b381f963e19092027c3a1efe4:0",
"value": "1000",
"internal_key": "024a3cb06616bb1545d3a25417a3fa5ccc70c5fbe9ceed8666410ed83745bbe968",
"taproot_asset_root": "42ac8c2338032a0b0ea9b96916da31a8798eef30cbef2a80b8c6d60249e4698d",
"merkle_root": "42ac8c2338032a0b0ea9b96916da31a8798eef30cbef2a80b8c6d60249e4698d",
"tapscript_sibling": null,
"num_passive_assets": 0
},
"script_key": "02258420ed4cf219965908102c6f8498da274c251a3463880763ba118c2d946c62",
"script_key_is_local": true,
"amount": "79",
"new_proof_blob": "00245663d6bf6a1de069c4562620fa23523cc99c50a0431eed91d199fa0db63fe7eb000000000150000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000096e88000000000000000002fd0163020000000001025663d6bf6a1de069c4562620fa23523cc99c50a0431eed91d199fa0db63fe7eb0100000000ffffffff5663d6bf6a1de069c4562620fa23523cc99c50a0431eed91d199fa0db63fe7eb00000000000000000003e803000000000000225120f036a7c3d36d4459fc4566875b255b80177fee26ea8cb27a1387e38dedee46f8e803000000000000225120be0db94139918a132eb4821f834efdec4634261997b57d964753d60923be9deed52e010000000000225120a0330b1c1bec571ac4e9004cae724380b3acf96a2f88332f4d6bd7136518932c0140db4f7d819f7fe90677c6e0d8355fe2b41f123934a5fb34117377841d3f1f305a82fbe7feea5fb51c2b8f598934027f455fb5d5f12ec6ba5b4538ef8cead916d801407fa2c953e2cbfd8ab9813b06f11919bfc3fba51ef9bac960ae522f9ea2b2f3acfd976dcc31bb49b9c4dc906952ddf7b79c7467a0c1f22ed1d845794adbe558e50000000003010004fd01600001000155ce884eb3ff8daf14cb05147d2e9912d2f81163e518fd3394d4a192a7539177ba000000000b66616e74617379636f696e04e552053fd4c8e2c01bc14cb9a0ce00f07d4ffdffff68fe455c70b934b22a43000000000002010003014f06ad01ab00655663d6bf6a1de069c4562620fa23523cc99c50a0431eed91d199fa0db63fe7eb0000000020cecdb6626705bf26ad036084f7423082db6114f0de84046ebf2e84f685287402b4c71447e74672f8cd5b50a6b430fc73b3caff7866dc905a502fe8adefad3b3101420140013a3f644a0428e3713e8f325b31734ce071fee2a55e3a64a4debd4ef384ba5b340f6b3b9163c5ae37f97aa3a287fd4f1850a3f2a08166568d1a24b2fbda4ece0728fdee0a27d560e5223b5e06b7a61d3df5b405942d21cf887fc96b16088874546a000000000000006408020000092102258420ed4cf219965908102c6f8498da274c251a3463880763ba118c2d946c62059f0004000000000121024a3cb06616bb1545d3a25417a3fa5ccc70c5fbe9ceed8666410ed83745bbe96802740049000100012020cecdb6626705bf26ad036084f7423082db6114f0de84046ebf2e84f685287402220000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff012700010001220000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff06f802c70004000000010121033e42d7bdc3c5161c2dc440667b0f4fca4c4a7d32d13efac12a71994651b048ce029c0071000100012020cecdb6626705bf26ad036084f7423082db6114f0de84046ebf2e84f6852874024a0001c37d7cb2bb159ad5843a204c210782c937765e0c54e1ac431f5519d0b02b95410000000000000015ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbf012700010001220000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2e0004000000020121026c93d33b5f69084411ffc540db8c0135300fdb4cdae2e5cd6ad1efd27a9d8cc20303020101",
"split_commit_root_hash": "fdee0a27d560e5223b5e06b7a61d3df5b405942d21cf887fc96b16088874546a",
"output_type": "OUTPUT_TYPE_SPLIT_ROOT"
},
{
"anchor": {
"outpoint": "b3357838416a0594bf0608ebd86ecf84bcc1293b381f963e19092027c3a1efe4:1",
"value": "1000",
"internal_key": "033e42d7bdc3c5161c2dc440667b0f4fca4c4a7d32d13efac12a71994651b048ce",
"taproot_asset_root": "8aada842f74c2b11e9b5a0a716baa0c453855ab6ea316222a8a28e7e79506f41",
"merkle_root": "8aada842f74c2b11e9b5a0a716baa0c453855ab6ea316222a8a28e7e79506f41",
"tapscript_sibling": null,
"num_passive_assets": 0
},
"script_key": "0225357fc148c441fbd5c5ea533fd6c33686697967a1cf8c493628788a535f433c",
"script_key_is_local": true,
"amount": "21",
"new_proof_blob": "00245663d6bf6a1de069c4562620fa23523cc99c50a0431eed91d199fa0db63fe7eb000000000150000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000096e88000000000000000002fd0163020000000001025663d6bf6a1de069c4562620fa23523cc99c50a0431eed91d199fa0db63fe7eb0100000000ffffffff5663d6bf6a1de069c4562620fa23523cc99c50a0431eed91d199fa0db63fe7eb00000000000000000003e803000000000000225120f036a7c3d36d4459fc4566875b255b80177fee26ea8cb27a1387e38dedee46f8e803000000000000225120be0db94139918a132eb4821f834efdec4634261997b57d964753d60923be9deed52e010000000000225120a0330b1c1bec571ac4e9004cae724380b3acf96a2f88332f4d6bd7136518932c0140db4f7d819f7fe90677c6e0d8355fe2b41f123934a5fb34117377841d3f1f305a82fbe7feea5fb51c2b8f598934027f455fb5d5f12ec6ba5b4538ef8cead916d801407fa2c953e2cbfd8ab9813b06f11919bfc3fba51ef9bac960ae522f9ea2b2f3acfd976dcc31bb49b9c4dc906952ddf7b79c7467a0c1f22ed1d845794adbe558e50000000003010004fd02a80001000155ce884eb3ff8daf14cb05147d2e9912d2f81163e518fd3394d4a192a7539177ba000000000b66616e74617379636f696e04e552053fd4c8e2c01bc14cb9a0ce00f07d4ffdffff68fe455c70b934b22a43000000000002010003011506fd021d01fd02190065000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002fd01ae4a00014d1b83c6f29417ec329b91f9cc95cb38facb112bf1d12d4ca3c45d5cd3765857000000000000004fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7ffd01600001000155ce884eb3ff8daf14cb05147d2e9912d2f81163e518fd3394d4a192a7539177ba000000000b66616e74617379636f696e04e552053fd4c8e2c01bc14cb9a0ce00f07d4ffdffff68fe455c70b934b22a43000000000002010003014f06ad01ab00655663d6bf6a1de069c4562620fa23523cc99c50a0431eed91d199fa0db63fe7eb0000000020cecdb6626705bf26ad036084f7423082db6114f0de84046ebf2e84f685287402b4c71447e74672f8cd5b50a6b430fc73b3caff7866dc905a502fe8adefad3b3101420140013a3f644a0428e3713e8f325b31734ce071fee2a55e3a64a4debd4ef384ba5b340f6b3b9163c5ae37f97aa3a287fd4f1850a3f2a08166568d1a24b2fbda4ece0728fdee0a27d560e5223b5e06b7a61d3df5b405942d21cf887fc96b16088874546a000000000000006408020000092102258420ed4cf219965908102c6f8498da274c251a3463880763ba118c2d946c620802000009210225357fc148c441fbd5c5ea533fd6c33686697967a1cf8c493628788a535f433c059f0004000000010121033e42d7bdc3c5161c2dc440667b0f4fca4c4a7d32d13efac12a71994651b048ce02740049000100012020cecdb6626705bf26ad036084f7423082db6114f0de84046ebf2e84f685287402220000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff012700010001220000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff06f802c70004000000000121024a3cb06616bb1545d3a25417a3fa5ccc70c5fbe9ceed8666410ed83745bbe968029c0071000100012020cecdb6626705bf26ad036084f7423082db6114f0de84046ebf2e84f6852874024a0001979a9f1a4fc2545215daab7876c5e47907a9ff6ed4cd0ca7dcedb6cf3eafc50c000000000000004fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbf012700010001220000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2e0004000000020121026c93d33b5f69084411ffc540db8c0135300fdb4cdae2e5cd6ad1efd27a9d8cc20303020101079f0004000000000121024a3cb06616bb1545d3a25417a3fa5ccc70c5fbe9ceed8666410ed83745bbe96802740049000100012020cecdb6626705bf26ad036084f7423082db6114f0de84046ebf2e84f685287402220000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff012700010001220000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"split_commit_root_hash": null,
"output_type": "OUTPUT_TYPE_SIMPLE"
}
]
}
}

生成されたアセット(100 単位)を含む先ほどの Bitcoin UTXO
ebe73fb60dfa99d191ed1e43a0509cc93c5223fa202656c469e01d6abfd66356:0
を消費する新たな Bitcoin トランザクション
b3357838416a0594bf0608ebd86ecf84bcc1293b381f963e19092027c3a1efe4
が発行された。

最後にBitcoin トランザクションとその UTXO、TAP Asset の UTXO の変化をまとめる。アセットは転送されても目減りせず転送前後で総量が維持される。Bitcoin(sats)はトランザクション手数料の分目減りしていく。

--

--