bitcoinrbでtaproot対応トランザクションを作成
bitcoinrbを使ってtaproot対応のトランザクションを作成し、署名し送金するところまで行ってみる。
code:送金先アドレスを作成
require 'bitcoin'
include Bitcoin::Opcodes
# testnetを使用
Bitcoin.chain_params = :testnet
# Create internal key
internal_key = Bitcoin::Key.new(priv_key: '98d2f0b8dfcaa7b29933bc78e8d82cd9d7c7a18ddc128ce2bc9dd143804f36f4')
# Create three locking scripts
key1 = Bitcoin::Key.new(priv_key: 'fd0137b05e26f40f8900697b690e11b2eba8abbd0f53c421148a22646b15f96f')
key2 = Bitcoin::Key.new(priv_key: '3b0ce9ef75031f5a1d6679f017fdd8d77460ecdcac1a24d482e1465e1768e22c')
key3 = Bitcoin::Key.new(priv_key: 'df94bce0533b3ff0c6b8ca16d6d2ce08b01350792cb350146cfaba056d5e4bfa')
leaf1 = Bitcoin::Taproot::LeafNode.new(Bitcoin::Script.new << key1.xonly_pubkey << OP_CHECKSIG)
leaf2 = Bitcoin::Taproot::LeafNode.new(Bitcoin::Script.new << key2.xonly_pubkey << OP_CHECKSIG)
leaf3 = Bitcoin::Taproot::LeafNode.new(Bitcoin::Script.new << key3.xonly_pubkey << OP_CHECKSIG)
# Build P2TR using internal public key and three locking scripts.
script_pubkey = builder.build
script_pubkey.to_addr
=> 'tb1p9uv58mst47h0r9zd8lm9hjlttcskq4wndxfceh8mjknd92mmflzspnsygf'
tb1p9uv58mst47h0r9zd8lm9hjlttcskq4wndxfceh8mjknd92mmflzspnsygf
このアドレスに対して送金する。今回は bitcoin core から 0.01 BTC 送金してみる。
key-pathとscript-pathを試したいので二回送金
まずは key-path のトランザクション作成
code:key-path
先ほど作成した internal_key をから署名に使う鍵を導出
key = builder.tweak_private_key(internal_key)
# Create Tx
tx = Bitcoin::Tx.new
tx.in << Bitcoin::TxIn.new(out_point: Bitcoin::OutPoint.from_txid('857790aa6b1c8427500a02b0b29813234fc9551a354e28a160285df39cde859a', 1))
tx.out << Bitcoin::TxOut.new(value: 990_000, script_pubkey: script_pubkey)
# Calculate sighash
sighash = tx.sighash_for_input(0, sig_version: :taproot, prevouts: prevouts, hash_type: Bitcoin::SIGHASH_TYPE:default) # Calculate schnorr signature
sig = key.sign(sighash, algo: :schnorr)
# Set signature to input(If hash_type is not default, hash_type must also be given at the end).
tx.in0.script_witness.stack << sig # Output tx payload.
tx.to_hex
=> 010000000001019a85de9cf35d2860a1284e351a55c94f231398b2b0020a5027841c6baa9077850100000000ffffffff01301b0f00000000002251202f1943ee0bafaef1944d3ff65bcbeb5e216055d369938cdcfb95a6d2ab7b4fc50140e0aef2fa21f420fdc87b8862024f70bcf5161140a71031ffb1461efad6057cd616ebaad820cf909a246ea9d18a6b1772ccf18f370fe24fd5da6c3364c6b6dd0f00000000
# bitcoin core でブロードキャストができなかった
decoderawtransaction 010000000001019a85de9cf35d2860a1284e351a55c94f231398b2b0020a5027841c6baa9077850100000000ffffffff01301b0f00000000002251202f1943ee0bafaef1944d3ff65bcbeb5e216055d369938cdcfb95a6d2ab7b4fc50140e0aef2fa21f420fdc87b8862024f70bcf5161140a71031ffb1461efad6057cd616ebaad820cf909a246ea9d18a6b1772ccf18f370fe24fd5da6c3364c6b6dd0f0000000
=> TX decode failed (code -22)
sendrawtransaction 010000000001019a85de9cf35d2860a1284e351a55c94f231398b2b0020a5027841c6baa9077850100000000ffffffff01301b0f00000000002251202f1943ee0bafaef1944d3ff65bcbeb5e216055d369938cdcfb95a6d2ab7b4fc50140e0aef2fa21f420fdc87b8862024f70bcf5161140a71031ffb1461efad6057cd616ebaad820cf909a246ea9d18a6b1772ccf18f370fe24fd5da6c3364c6b6dd0f0000000
=> TX decode failed. Make sure the tx has at least one input. (code -22)
# トランザクションに問題がないか確認してみる
checker = Bitcoin::TxChecker.new(tx: tx, input_index: 0, prevouts: prevouts)
i = Bitcoin::ScriptInterpreter.new(checker: checker)
i.verify_script(tx.in0.script_sig, prevouts0.script_pubkey, tx.in0.script_witness) => true
問題はなさそう
# 再度作り直すとうまくいった(原因は最後の0が一個消してしまっていることだった..)
decoderawtransaction 010000000001019a85de9cf35d2860a1284e351a55c94f231398b2b0020a5027841c6baa9077850100000000ffffffff01301b0f00000000002251202f1943ee0bafaef1944d3ff65bcbeb5e216055d369938cdcfb95a6d2ab7b4fc50140e0aef2fa21f420fdc87b8862024f70bcf5161140a71031ffb1461efad6057cd616ebaad820cf909a246ea9d18a6b1772ccf18f370fe24fd5da6c3364c6b6dd0f00000000
{
"txid": "909bba72b180fbaf15cb7edee8a1066800fc142cb2a709df0f980aadc1b4346c",
"hash": "9a112cb300c83970eaf0bebe9cefa052837873d74583ade5359bcecdd4e89b50",
"version": 1,
"size": 162,
"vsize": 111,
"weight": 444,
"locktime": 0,
"vin": [
{
"txid": "857790aa6b1c8427500a02b0b29813234fc9551a354e28a160285df39cde859a",
"vout": 1,
"scriptSig": {
"asm": "",
"hex": ""
},
"txinwitness": [
"e0aef2fa21f420fdc87b8862024f70bcf5161140a71031ffb1461efad6057cd616ebaad820cf909a246ea9d18a6b1772ccf18f370fe24fd5da6c3364c6b6dd0f"
],
"sequence": 4294967295
}
],
"vout": [
{
"value": 0.00990000,
"n": 0,
"scriptPubKey": {
"asm": "1 2f1943ee0bafaef1944d3ff65bcbeb5e216055d369938cdcfb95a6d2ab7b4fc5",
"desc": "addr(tb1p9uv58mst47h0r9zd8lm9hjlttcskq4wndxfceh8mjknd92mmflzspnsygf)#kqlcl5q2",
"hex": "51202f1943ee0bafaef1944d3ff65bcbeb5e216055d369938cdcfb95a6d2ab7b4fc5",
"address": "tb1p9uv58mst47h0r9zd8lm9hjlttcskq4wndxfceh8mjknd92mmflzspnsygf",
"type": "witness_v1_taproot"
}
}
]
}
sendrawtransaction 010000000001019a85de9cf35d2860a1284e351a55c94f231398b2b0020a5027841c6baa9077850100000000ffffffff01301b0f00000000002251202f1943ee0bafaef1944d3ff65bcbeb5e216055d369938cdcfb95a6d2ab7b4fc50140e0aef2fa21f420fdc87b8862024f70bcf5161140a71031ffb1461efad6057cd616ebaad820cf909a246ea9d18a6b1772ccf18f370fe24fd5da6c3364c6b6dd0f00000000
-> 909bba72b180fbaf15cb7edee8a1066800fc142cb2a709df0f980aadc1b4346c
しょーもないミスだったが無事ブロードキャストできた
次は script-path の方を試してみる
code:script-path
# Create Tx
tx = Bitcoin::Tx.new
tx.in << Bitcoin::TxIn.new(out_point: Bitcoin::OutPoint.from_txid('c7fa98a73ba2993ea3a9af92e9d683b692b8114bb56bef76751ac5c2a6f3121b', 0))
tx.out << Bitcoin::TxOut.new(value: 990_000, script_pubkey: script_pubkey)
# Calculate sighash
opts = {leaf_hash: leaf2.leaf_hash} # script pathではleaf hashにもコミットするためオプションで渡す
sighash = tx.sighash_for_input(0, sig_version: :tapscript, prevouts: prevouts, hash_type: Bitcoin::SIGHASH_TYPE:default, opts: opts) # Calculate schnorr signature
sig = key2.sign(sighash, algo: :schnorr)
# Set items to need unlock to witness
## Set leaf2 unlock item(signature)
tx.in0.script_witness.stack << sig ## Set leaf2
tx.in0.script_witness.stack << leaf2.script.to_payload ## Set control block that prove leaf2 is included in the tree.
tx.in0.script_witness.stack << builder.control_block(leaf2).to_payload # Output tx payload.
tx.to_hex
decoderawtransaction 010000000001011b12f3a6c2c51a7576ef6bb54b11b892b683d6e992afa9a33e99a23ba798fac70000000000ffffffff01301b0f00000000002251202f1943ee0bafaef1944d3ff65bcbeb5e216055d369938cdcfb95a6d2ab7b4fc5034013303ef9282d747ff7a7cb22b9dcd3b6d320168ae84506e86a01d77de273f2ff30e070efe880a9e088f24934782d16cf5a9ad7bd8bac61d1f265a69023da3c1022204582dc979ec028044d80e911fb992d37801163cec6082b9807746d450b8ef773ac61c09b1e61ad40f333999250340eebb2257c0214e69ab3125022c1df50f6f5d0ebe3e13ebd0cd00421ea7d47f0b9270bf5c0677545a749189b7bbc2eb41faeb23145e2884fd612cee77b7f30b9bfaba55a48fa5ee74534b6e37326e7684cd54911cf00000000
{
"txid": "e84578a0d88f36b81085929401cdcc92e6aabf539b33de7b725ae1a67c745f8d",
"hash": "1ac65a36aace1b2f435bab98f6b24455c95373d3109ee40b17b6cc6ebf795208",
"version": 1,
"size": 295,
"vsize": 145,
"weight": 577,
"locktime": 0,
"vin": [
{
"txid": "c7fa98a73ba2993ea3a9af92e9d683b692b8114bb56bef76751ac5c2a6f3121b",
"vout": 0,
"scriptSig": {
"asm": "",
"hex": ""
},
"txinwitness": [
"13303ef9282d747ff7a7cb22b9dcd3b6d320168ae84506e86a01d77de273f2ff30e070efe880a9e088f24934782d16cf5a9ad7bd8bac61d1f265a69023da3c10",
"204582dc979ec028044d80e911fb992d37801163cec6082b9807746d450b8ef773ac",
"c09b1e61ad40f333999250340eebb2257c0214e69ab3125022c1df50f6f5d0ebe3e13ebd0cd00421ea7d47f0b9270bf5c0677545a749189b7bbc2eb41faeb23145e2884fd612cee77b7f30b9bfaba55a48fa5ee74534b6e37326e7684cd54911cf"
],
"sequence": 4294967295
}
],
"vout": [
{
"value": 0.00990000,
"n": 0,
"scriptPubKey": {
"asm": "1 2f1943ee0bafaef1944d3ff65bcbeb5e216055d369938cdcfb95a6d2ab7b4fc5",
"desc": "addr(tb1p9uv58mst47h0r9zd8lm9hjlttcskq4wndxfceh8mjknd92mmflzspnsygf)#kqlcl5q2",
"hex": "51202f1943ee0bafaef1944d3ff65bcbeb5e216055d369938cdcfb95a6d2ab7b4fc5",
"address": "tb1p9uv58mst47h0r9zd8lm9hjlttcskq4wndxfceh8mjknd92mmflzspnsygf",
"type": "witness_v1_taproot"
}
}
]
}
sendrawtransaction 010000000001011b12f3a6c2c51a7576ef6bb54b11b892b683d6e992afa9a33e99a23ba798fac70000000000ffffffff01301b0f00000000002251202f1943ee0bafaef1944d3ff65bcbeb5e216055d369938cdcfb95a6d2ab7b4fc5034013303ef9282d747ff7a7cb22b9dcd3b6d320168ae84506e86a01d77de273f2ff30e070efe880a9e088f24934782d16cf5a9ad7bd8bac61d1f265a69023da3c1022204582dc979ec028044d80e911fb992d37801163cec6082b9807746d450b8ef773ac61c09b1e61ad40f333999250340eebb2257c0214e69ab3125022c1df50f6f5d0ebe3e13ebd0cd00421ea7d47f0b9270bf5c0677545a749189b7bbc2eb41faeb23145e2884fd612cee77b7f30b9bfaba55a48fa5ee74534b6e37326e7684cd54911cf00000000
-> e84578a0d88f36b81085929401cdcc92e6aabf539b33de7b725ae1a67c745f8d
script-pathでもブロードキャストできた
--------------------------------
code:gbec
$ irb
require 'bitcoin'
include Bitcoin::Opcodes
# regtestを使用
Bitcoin.chain_params = :regtest
# key-pathの条件
# 内部鍵(Internal Key)というP2PKH, P2WPKHなどと同様に一般的な送金に使用する鍵を作成
internal_key = Bitcoin::Key.new(priv_key: '98d2f0b8dfcaa7b29933bc78e8d82cd9d7c7a18ddc128ce2bc9dd143804f36f4')
----------------
# script-pathの条件
# Taprootから鍵のx軸の値だけいれるようになり、y軸の正負は後ほど提示する
key1 = Bitcoin::Key.new(priv_key: 'fd0137b05e26f40f8900697b690e11b2eba8abbd0f53c421148a22646b15f96f')
key2 = Bitcoin::Key.new(priv_key: '3b0ce9ef75031f5a1d6679f017fdd8d77460ecdcac1a24d482e1465e1768e22c')
key3 = Bitcoin::Key.new(priv_key: 'df94bce0533b3ff0c6b8ca16d6d2ce08b01350792cb350146cfaba056d5e4bfa')
leaf1 = Bitcoin::Taproot::LeafNode.new(Bitcoin::Script.new << key1.xonly_pubkey << OP_CHECKSIG)
leaf2 = Bitcoin::Taproot::LeafNode.new(Bitcoin::Script.new << key2.xonly_pubkey << OP_CHECKSIG)
leaf3 = Bitcoin::Taproot::LeafNode.new(Bitcoin::Script.new << key3.xonly_pubkey << OP_CHECKSIG)
# リーフからマークルツリーを作成し、内部鍵と合わせてP2TRアドレスを作成
script_pubkey = builder.build
script_pubkey.to_addr
=> 'bcrt1p9uv58mst47h0r9zd8lm9hjlttcskq4wndxfceh8mjknd92mmflzsv26zan'
# bitcoin core で P2TR アドレスに送金
------------------------
# 先ほど作成した internal_key をから署名に使う鍵を導出
key = builder.tweak_private_key(internal_key)
# トランザクションの作成
tx = Bitcoin::Tx.new
tx.in << Bitcoin::TxIn.new(out_point: Bitcoin::OutPoint.from_txid('txid', vout))
tx.out << Bitcoin::TxOut.new(value: 990_000, script_pubkey: script_pubkey)
--------------------------
# 署名作成に必要な sighash を計算
sighash = tx.sighash_for_input(0, sig_version: :taproot, prevouts: prevouts, hash_type: Bitcoin::SIGHASH_TYPE:default) # シュノア署名による署名を作成
sig = key.sign(sighash, algo: :schnorr)
# 署名をスタックにセット
tx.in0.script_witness.stack << sig # トランザクションの情報を出力
tx.to_hex
# bitcoin coreでブロードキャスト
-----------------------------
# トランザクションを作成
tx = Bitcoin::Tx.new
tx.in << Bitcoin::TxIn.new(out_point: Bitcoin::OutPoint.from_txid('txid', vout))
tx.out << Bitcoin::TxOut.new(value: 990_000, script_pubkey: script_pubkey)
# 署名に必要な sighash を計算
opts = {leaf_hash: leaf2.leaf_hash} # script pathではleaf hashにもコミットするためオプションで渡す
sighash = tx.sighash_for_input(0, sig_version: :tapscript, prevouts: prevouts, hash_type: Bitcoin::SIGHASH_TYPE:default, opts: opts) -----------------------------
# シュノア署名を作成
sig = key2.sign(sighash, algo: :schnorr)
# leaf2 の署名をスタックにセット
tx.in0.script_witness.stack << sig # leaf2 のスクリプト情報をスタックにセット
tx.in0.script_witness.stack << leaf2.script.to_payload # leaf2 がツリーに含まれている証明のプルーフをセット
tx.in0.script_witness.stack << builder.control_block(leaf2).to_payload # トランザクションのローデータを出力
tx.to_hex
# bitcoin core でブロードキャスト