環境
bitcoin-ruby v0.0.18
ruby v2.6.0(by rbenv)
bitcoind v0.17.0.0-5b47b8efd
bitcoin-cli v0.17.0.0-5b47b8efd
やること
bitcoin-rubyをつかってp2pkh形式のマルチシグスクリプトでロックされるトランザクションを作成する。
その他参考
bitcoin-rubyでp2pkhトランザクションを作成する - Qiita
サンプルコード/ 最終的なコードはこちらです。
マルチシグスクリプトとは
解除(unlock)するのに複数の署名を必要とするスクリプト。
multi signature scriptからマルチシグと呼ばれることが多い。
マルチシグスクリプトの構造
マルチシグスクリプトの構造は以下
M <Public Key 1> <Public Key 2> ... <Public Key N> N CHECKMULTISIG
(Mastering Bitcoinを参照されたい)
記号 | 説明 |
---|---|
M | スクリプトを解除するのに最低限必要な署名の数 |
N | 有効な署名を作成できる秘密鍵の数 |
例えば | |
2 <Public Key 1> <Public Key 2> ... <Public Key 5> 5 CHECKMULTISIG |
|
というスクリプトは、 | |
有効な署名を作成できる秘密鍵の数は5で、そのうち2つの秘密鍵による署名でスクリプトを解除することができるスクリプトである。 | |
2of5のマルチシグと呼ばれる。 |
トランザクションの作成
bitcoin-rubyのTxBuilderクラスを使用して2of3のマルチシグスクリプトでロックされたトランザクションを作成する。
input
TxBuilder
のくわしい使い方はこちら
inputで使用する項目は以下を仮定する。
項目名 | 値 |
---|---|
前トランザクション | c37e9046d2d6cf514f49bdf0d19b88b2ae91fc87f244b8144c111ed3247d6bfb |
送信者の秘密鍵 | cP9nfzGMqcJ3LrJtsTpWjEUyrybfiFXumzaAobZEBJEPWBtkzDv3 |
コードは以下
tx = build_tx do |t|
t.input do |i|
i.prev_out 'c37e9046d2d6cf514f49bdf0d19b88b2ae91fc87f244b8144c111ed3247d6bfb'
i.prev_out_index 1
i.prev_out_script '76a914f9aa5ee91d95360104f173892b916d01d6a485a188ac'.htb
i.signature_key Bitcoin::Key.from_base58('cP9nfzGMqcJ3LrJtsTpWjEUyrybfiFXumzaAobZEBJEPWBtkzDv3')
end
end
output
outputに使用する項目は以下を仮定。
項目名 | 値 |
---|---|
有効な署名を作成できる秘密鍵1 | cNBfQu3fAJ1QXMrLvd4PGNAd7RzjUj62kHbkL9owNF2zN2g3M4GB |
有効な署名を作成できる秘密鍵2 | cPJSC2SZqW3d9EWZTWtnDdGUQY7HFWcCH3cSm4kkkDpidJxp188N |
有効な署名を作成できる秘密鍵3 | cMujetBk2WXGwnyA1pWZHYLrGVwk1h5V4nYnCQhr9kJshWKPgNpZ |
最低限必要な署名の数 | 2 |
マルチシグスクリプトの構造は
M <Public Key 1> <Public Key 2> ... <Public Key N> N CHECKMULTISIG
であるから、上記の秘密鍵から公開鍵を生成し、今回作成されるスクリプトは以下のようになる。
2 02ec35d437a41da7ab7f8b8024377ff6b532b49fb95804a73c41af6510663eaf41 039976de4fc156fe8dc5282d89eaf7ddc5e26494877eb8d995d732cd87791a45f4 036647aa901188f4e784bd251e8ddb7802239b911ab66fa90165a3fe5320328ebf 3 OP_CHECKMULTISIG
以下、くわしくみていく。
outputの作り方
outputについても一般的なp2pkhトランザクションの場合と構造は同じ。
しかしTxOutBuilder::to
に渡す引数が異なる。
マルチシグの場合to
の第一引数には、最低限必要な署名の数と有効な署名を作成できる公開鍵すべてからなる配列を渡す。
第二引数には:multisig
を指定。
サンプルは以下。
privkeys = [
'cNBfQu3fAJ1QXMrLvd4PGNAd7RzjUj62kHbkL9owNF2zN2g3M4GB',
'cPJSC2SZqW3d9EWZTWtnDdGUQY7HFWcCH3cSm4kkkDpidJxp188N',
'cMujetBk2WXGwnyA1pWZHYLrGVwk1h5V4nYnCQhr9kJshWKPgNpZ'
]
recipients_pubkeys = privkeys.map do |private_key|
Bitcoin::Key.from_base58(private_key).pub
end
# recipients_pubkeys = [
# "02ec35d437a41da7ab7f8b8024377ff6b532b49fb95804a73c41af6510663eaf41",
# "039976de4fc156fe8dc5282d89eaf7ddc5e26494877eb8d995d732cd87791a45f4",
# "036647aa901188f4e784bd251e8ddb7802239b911ab66fa90165a3fe5320328ebf"
# ]
tx = build_tx do |t|
t.output do |o|
o.value 1999000000
o.to [2, *recipients_pubkeys], :multisig
end
end
to
に渡された引数は、:multisig
が指定されたことで、
Script::to_multisig_script
(Github)メソッドに渡される。
Script::to_multisig_script
がマルチシグスクリプト作成する。
def self.to_multisig_script(m, *pubkeys)
raise "invalid m-of-n number" unless [m, pubkeys.size].all?{|i| (0..20).include?(i) }
raise "invalid m-of-n number" if pubkeys.size < m
pubs = pubkeys.map{|pk| pack_pushdata([pk].pack("H*")) }
m = m > 16 ? pack_pushdata([m].pack("C")) : [80 + m.to_i].pack("C")
n = pubkeys.size > 16 ? pack_pushdata([pubkeys.size].pack("C")) : [80 + pubs.size].pack("C")
[ m, *pubs, n, [OP_CHECKMULTISIG].pack("C")].join
end
作成されたトランザクション
サンプルコードを実行するとtx
は署名済みトランザクションとなっている。
p tx.to_hash
# 以下作成されたトランザクションHash形式
{"hash"=>"181e9c8b40e6ea9185b44210fd178b05edb87c4680ca390925533bfa08514a3e",
"ver"=>1,
"vin_sz"=>1,
"vout_sz"=>1,
"lock_time"=>0,
"size"=>271,
"in"=>
[{"prev_out"=>
{"hash"=>
"c37e9046d2d6cf514f49bdf0d19b88b2ae91fc87f244b8144c111ed3247d6bfb",
"n"=>1},
"scriptSig"=>
"3044022071d9026597c699421bb2bf4bf30eec35e7fc8167ebbd141251fd75a66f7a49ec022040586df4eb3c230e363e68bf8787f2793b24e54ebff67b1e42e6db9fa443e3f701 02dcc5b4984fc69f13b4cf490a205ab4730bbdfeda0cc78f832a2f701955d36e96"}],
"out"=>
[{"value"=>"19.99000000",
"scriptPubKey"=>
"2 02ec35d437a41da7ab7f8b8024377ff6b532b49fb95804a73c41af6510663eaf41 039976de4fc156fe8dc5282d89eaf7ddc5e26494877eb8d995d732cd87791a45f4 036647aa901188f4e784bd251e8ddb7802239b911ab66fa90165a3fe5320328ebf 3 OP_CHECKMULTISIG"}]}
p tx.to_payload.unpack('H*')
# 以下作成されたトランザクションの16進数表記
["0100000001fb6b7d24d31e114c14b844f287fc91aeb2889bd1f0bd494f51cfd6d246907ec3010000006a473044022038c0d71a88bb4bedd311ea0e7390f6676123cac8c19daf504e1d08d8909cad3b02206813df8cb267ede803b4633a16291bd0ec20df5af0ea9e88ef23da9c236d6147012102dcc5b4984fc69f13b4cf490a205ab4730bbdfeda0cc78f832a2f701955d36e96ffffffff01c05126770000000069522102ec35d437a41da7ab7f8b8024377ff6b532b49fb95804a73c41af6510663eaf4121039976de4fc156fe8dc5282d89eaf7ddc5e26494877eb8d995d732cd87791a45f421036647aa901188f4e784bd251e8ddb7802239b911ab66fa90165a3fe5320328ebf53ae00000000"]
bitcoin-cli sendrawtransaction
でbitcoindで送金できる。