bitcoin-cliのsendtoaddressなどの標準送金コマンドで物足りない方向けです。
特定のUTXOから送金したいときに便利です。
ゆくゆくは、P2SH(スクリプトハッシュ)宛の送金を狙いたいのですが、まずは学習のためにP2PKH(公開鍵ハッシュ)宛のUTXOを使ってみます。
環境
- Bitcoind: 0.12.1
- Openassets-ruby: 0.4.8
- Ruby: 2.2.2
Openassets-rubyは、bitcoinのapiコールのために使っています。直接bitcoin-apiを使う方は不要です。
参考 → http://qiita.com/osada/items/50fc76959e6730603417
作りたいトランザクション
Aさん(1BTC保有)、Bさん(1BTC保有)の2名から、Cさんに送金(1.999BTC)するトランザクション。
なお、Aさん、Bさんの秘密鍵は同一のマシンにあるものとする。
初期設定
必要なモジュールをロードして、bitcoindに接続するAPIオブジェクトを作成。
※以降、irbで実行することを想定しています。
require 'bitcoin'
require 'openassets'
require 'pp'
# testnetのおまじない
Bitcoin.network = :testnet3
### use zero confirmation
oa_api = OpenAssets::Api.new({:network => 'testnet',
:provider => 'bitcoind', :cache => 'testnet.db',
:dust_limit => 600, :default_fees => 10000,
:min_confirmation => 0, :max_confirmation => 9999999,
:rpc => {:user => 'osada', :password => 'password', :schema => 'http',
:port => 18332, :host => 'localhost'}})
送金に使うUTXO指定
送金に使用するUTXOを指定。
# 送信者AさんのUTXO # 1 BTC
A_addr = 'mt22rKhAonbqfvyqSW5qRFnZwi1WyYFczv'
# UTXOを指定するためのトランザクションIDと出力部(vout)の番号
A_prev_txid = 'b1bdba74d2bcbfe8f9701949af1faa817ed48010751ade23e38b351be723eb56'
A_prev_out_index = 0
# 送信者BさんのUTXO # 1 BTC
B_addr = 'mhARub16mC2cP8si8PprPM8Z8UTvYdGDLr'
B_prev_txid = 'b971714785ec66f852b79f0a5d2784f73110d6ff5a331fcdd58109463ecdaaff'
B_prev_out_index = 0
受信者の指定
# Cさんのアドレス
C_addr = 'mt22rKhAonbqfvyqSW5qRFnZwi1WyYFczv'
秘密鍵の入手(vin検証&署名用)
# Aさんの秘密鍵(bitcoin-apiから入手)
A_priv_key = oa_api.provider.dumpprivkey(A_addr)
# => "cSkArVdNo1X9roQrCmnhZ3ifhzs7zF3cbhUHPCvjLoQXncNuBNYZ"
# Keyオブジェクトに変換
A_key_obj = Bitcoin::Key.from_base58(A_priv_key)
# => #<Bitcoin::Key:0x007ff8fda7f0a0 @key=#<OpenSSL::PKey::EC:0x007ff8fda7f050 @group=nil>, @pubkey_compressed=true>
# Bさんの秘密鍵(bitcoin-apiから入手
B_priv_key = oa_api.provider.dumpprivkey(B_addr)
# => "cTvh2ijnwt79QSfZKrYmDvAhByamNKCppG6PeBM38fYrgj67Qmai"
# 同様に、Keyオブジェクトに変換
B_key_obj = Bitcoin::Key.from_base58(B_priv_key)
# => #<Bitcoin::Key:0x007ff8fe9b1410 @key=#<OpenSSL::PKey::EC:0x007ff8fe9b13c0 @group=nil>, @pubkey_compressed=true>
指定したUTXOを含むトランザクションを入手
# 上で指定したtxidから、hex形式で入手
A_prev_tx_bin = oa_api.provider.getrawtransaction(A_prev_txid)
# => "01000000019c30b660df70bc4e327152e97073197621084967e1b1441a3c1641bfb2f3c867010000006a47304402207e05c734e8396c820779f292f475859342fccc3cd2f76f34efa32d02229738a402201f667162b42eb200e78d8c770cff9ce03433e01c1ae5d428a9123efe2eaa2ee7012103175ad13ad982e37c4aad5f426942d94c74ae2d4d85cc3bea13fef44c2acd3a01feffffff0200e1f505000000001976a91489216031ab89482495974a49f40a38f835d3739388ac6c0f1a1e010000001976a914deb8a31e71cf0d8af4e4270396408edb58091bcf88ac4a000000"
# hex形式からオブジェクトを作成
A_prev_tx = Bitcoin::Protocol::Tx.new(A_prev_tx_bin.htb)
# => #<Bitcoin::Protocol::Tx:0x007ff8fe9c98a8 @scripts=[], @out=[#<Bitcoin::Protocol::TxOut:0x007ff8fe9c93d0 @value=100000000, @pk_script_length=25, @pk_script="v\xA9\x14\x89!`1\xAB\x89H$\x95\x97JI\xF4\n8\xF85\xD3s\x93\x88\xAC">, #<Bitcoin::Protocol::TxOut:0x007ff8fe9c9240 @value=4799991660, @pk_script_length=25, @pk_script="v\xA9\x14\xDE\xB8\xA3\x1Eq\xCF\r\x8A\xF4\xE4'\x03\x96@\x8E\xDBX\t\e\xCF\x88\xAC">], @in=[#<Bitcoin::Protocol::TxIn:0x007ff8fe9c9678 @prev_out_hash="\x9C0\xB6`\xDFp\xBCN2qR\xE9ps\x19v!\bIg\xE1\xB1D\x1A<\x16A\xBF\xB2\xF3\xC8g", @prev_out_index=1, @script_sig_length=106, @script_sig="G0D\x02 ~\x05\xC74\xE89l\x82\ay\xF2\x92\xF4u\x85\x93B\xFC\xCC<\xD2\xF7o4\xEF\xA3-\x02\"\x978\xA4\x02 \x1Ffqb\xB4.\xB2\x00\xE7\x8D\x8Cw\f\xFF\x9C\xE043\xE0\x1C\x1A\xE5\xD4(\xA9\x12>\xFE.\xAA.\xE7\x01!\x03\x17Z\xD1:\xD9\x82\xE3|J\xAD_BiB\xD9Lt\xAE-M\x85\xCC;\xEA\x13\xFE\xF4L*\xCD:\x01", @sequence="\xFE\xFF\xFF\xFF">], @lock_time=74, @ver=1, @enable_bitcoinconsensus=false, @payload="\x01\x00\x00\x00\x01\x9C0\xB6`\xDFp\xBCN2qR\xE9ps\x19v!\bIg\xE1\xB1D\x1A<\x16A\xBF\xB2\xF3\xC8g\x01\x00\x00\x00jG0D\x02 ~\x05\xC74\xE89l\x82\ay\xF2\x92\xF4u\x85\x93B\xFC\xCC<\xD2\xF7o4\xEF\xA3-\x02\"\x978\xA4\x02 \x1Ffqb\xB4.\xB2\x00\xE7\x8D\x8Cw\f\xFF\x9C\xE043\xE0\x1C\x1A\xE5\xD4(\xA9\x12>\xFE.\xAA.\xE7\x01!\x03\x17Z\xD1:\xD9\x82\xE3|J\xAD_BiB\xD9Lt\xAE-M\x85\xCC;\xEA\x13\xFE\xF4L*\xCD:\x01\xFE\xFF\xFF\xFF\x02\x00\xE1\xF5\x05\x00\x00\x00\x00\x19v\xA9\x14\x89!`1\xAB\x89H$\x95\x97JI\xF4\n8\xF85\xD3s\x93\x88\xACl\x0F\x1A\x1E\x01\x00\x00\x00\x19v\xA9\x14\xDE\xB8\xA3\x1Eq\xCF\r\x8A\xF4\xE4'\x03\x96@\x8E\xDBX\t\e\xCF\x88\xACJ\x00\x00\x00", @hash="b1bdba74d2bcbfe8f9701949af1faa817ed48010751ade23e38b351be723eb56">
# 同様にBさんのトランザクション
B_prev_tx_bin = oa_api.provider.getrawtransaction(B_prev_txid)
# => "01000000015eaff9fe4026c3c0b278b8bcebdb0d0d056c81c17dc37bbc628a1fd8f40881b6010000006b483045022100c5eda7ce4189f678a0498e15bc629e7088824dafdb0b8dfabf524816c4dacebb022008db321cf7652ce0b1f77e00296727075ca392dce54beb219dcd02b23964ce8a01210351c046666efaf58a6255994c66ffc2b8dcae8b0cd31f13166c39f5eceb41f783feffffff0200e1f505000000001976a914120e90f5177de616964c2cbb5d40311547d419e888ac1c2a2e12010000001976a914e310b8e8fef8506c6cfd34169f6904e6d0c9cd5688ac77000000"
# オブジェクト化
B_prev_tx = Bitcoin::Protocol::Tx.new(B_prev_tx_bin.htb)
# => #<Bitcoin::Protocol::Tx:0x007ff8fb8ffe98 @scripts=[], @out=[#<Bitcoin::Protocol::TxOut:0x007ff8fb8ff9c0 @value=100000000, @pk_script_length=25, @pk_script="v\xA9\x14\x12\x0E\x90\xF5\x17}\xE6\x16\x96L,\xBB]@1\x15G\xD4\x19\xE8\x88\xAC">, #<Bitcoin::Protocol::TxOut:0x007ff8fb8ff830 @value=4599982620, @pk_script_length=25, @pk_script="v\xA9\x14\xE3\x10\xB8\xE8\xFE\xF8Pll\xFD4\x16\x9Fi\x04\xE6\xD0\xC9\xCDV\x88\xAC">], @in=[#<Bitcoin::Protocol::TxIn:0x007ff8fb8ffc90 @prev_out_hash="^\xAF\xF9\xFE@&\xC3\xC0\xB2x\xB8\xBC\xEB\xDB\r\r\x05l\x81\xC1}\xC3{\xBCb\x8A\x1F\xD8\xF4\b\x81\xB6", @prev_out_index=1, @script_sig_length=107, @script_sig="H0E\x02!\x00\xC5\xED\xA7\xCEA\x89\xF6x\xA0I\x8E\x15\xBCb\x9Ep\x88\x82M\xAF\xDB\v\x8D\xFA\xBFRH\x16\xC4\xDA\xCE\xBB\x02 \b\xDB2\x1C\xF7e,\xE0\xB1\xF7~\x00)g'\a\\\xA3\x92\xDC\xE5K\xEB!\x9D\xCD\x02\xB29d\xCE\x8A\x01!\x03Q\xC0Ffn\xFA\xF5\x8AbU\x99Lf\xFF\xC2\xB8\xDC\xAE\x8B\f\xD3\x1F\x13\x16l9\xF5\xEC\xEBA\xF7\x83", @sequence="\xFE\xFF\xFF\xFF">], @lock_time=119, @ver=1, @enable_bitcoinconsensus=false, @payload="\x01\x00\x00\x00\x01^\xAF\xF9\xFE@&\xC3\xC0\xB2x\xB8\xBC\xEB\xDB\r\r\x05l\x81\xC1}\xC3{\xBCb\x8A\x1F\xD8\xF4\b\x81\xB6\x01\x00\x00\x00kH0E\x02!\x00\xC5\xED\xA7\xCEA\x89\xF6x\xA0I\x8E\x15\xBCb\x9Ep\x88\x82M\xAF\xDB\v\x8D\xFA\xBFRH\x16\xC4\xDA\xCE\xBB\x02 \b\xDB2\x1C\xF7e,\xE0\xB1\xF7~\x00)g'\a\\\xA3\x92\xDC\xE5K\xEB!\x9D\xCD\x02\xB29d\xCE\x8A\x01!\x03Q\xC0Ffn\xFA\xF5\x8AbU\x99Lf\xFF\xC2\xB8\xDC\xAE\x8B\f\xD3\x1F\x13\x16l9\xF5\xEC\xEBA\xF7\x83\xFE\xFF\xFF\xFF\x02\x00\xE1\xF5\x05\x00\x00\x00\x00\x19v\xA9\x14\x12\x0E\x90\xF5\x17}\xE6\x16\x96L,\xBB]@1\x15G\xD4\x19\xE8\x88\xAC\x1C*.\x12\x01\x00\x00\x00\x19v\xA9\x14\xE3\x10\xB8\xE8\xFE\xF8Pll\xFD4\x16\x9Fi\x04\xE6\xD0\xC9\xCDV\x88\xACw\x00\x00\x00", @hash="b971714785ec66f852b79f0a5d2784f73110d6ff5a331fcdd58109463ecdaaff">
トランザクション構築
Builderクラスを使うと便利。
# Builderオブジェクト作成
tx_bldr = Bitcoin::Builder::TxBuilder.new
# => #<Bitcoin::Builder::TxBuilder:0x007ff8fe9a19e8 @tx=#<Bitcoin::Protocol::Tx:0x007ff8fe9a19c0 @scripts=[], @out=[], @in=[], @lock_time=0, @ver=1, @enable_bitcoinconsensus=false>, @ins=[], @outs=[]>
入力部(vin)作成
# Aさんのvin。以下3つのブロックをinputメソッドに引数渡しして、登録する。
tx_bldr.input do |i|
i.prev_out A_prev_tx
i.prev_out_index A_prev_out_index
i.signature_key A_key_obj
end
# => [#<Bitcoin::Builder::TxInBuilder:0x007ff8fb8edbf8 @txin=#<Bitcoin::Protocol::TxIn:0x007ff8fb8edbd0 @prev_out_hash=nil, @prev_out_index=nil, @script_sig_length=0, @script_sig="", @sequence="\xFF\xFF\xFF\xFF">, @prev_out_hash="V\xEB#\xE7\e5\x8B\xE3#\xDE\x1Au\x10\x80\xD4~\x81\xAA\x1F\xAFI\x19p\xF9\xE8\xBF\xBC\xD2t\xBA\xBD\xB1", @prev_out_index=0, @prev_tx=#<Bitcoin::Protocol::Tx:0x007ff8fe9c98a8 @scripts=[], @out=[#<Bitcoin::Protocol::TxOut:0x007ff8fe9c93d0 @value=100000000, @pk_script_length=25, @pk_script="v\xA9\x14\x89!`1\xAB\x89H$\x95\x97JI\xF4\n8\xF85\xD3s\x93\x88\xAC">, #<Bitcoin::Protocol::TxOut:0x007ff8fe9c9240 @value=4799991660, @pk_script_length=25, @pk_script="v\xA9\x14\xDE\xB8\xA3\x1Eq\xCF\r\x8A\xF4\xE4'\x03\x96@\x8E\xDBX\t\e\xCF\x88\xAC">], @in=[#<Bitcoin::Protocol::TxIn:0x007ff8fe9c9678 @prev_out_hash="\x9C0\xB6`\xDFp\xBCN2qR\xE9ps\x19v!\bIg\xE1\xB1D\x1A<\x16A\xBF\xB2\xF3\xC8g", @prev_out_index=1, @script_sig_length=106, @script_sig="G0D\x02 ~\x05\xC74\xE89l\x82\ay\xF2\x92\xF4u\x85\x93B\xFC\xCC<\xD2\xF7o4\xEF\xA3-\x02\"\x978\xA4\x02 \x1Ffqb\xB4.\xB2\x00\xE7\x8D\x8Cw\f\xFF\x9C\xE043\xE0\x1C\x1A\xE5\xD4(\xA9\x12>\xFE.\xAA.\xE7\x01!\x03\x17Z\xD1:\xD9\x82\xE3|J\xAD_BiB\xD9Lt\xAE-M\x85\xCC;\xEA\x13\xFE\xF4L*\xCD:\x01", @sequence="\xFE\xFF\xFF\xFF">], @lock_time=74, @ver=1, @enable_bitcoinconsensus=false, @payload="\x01\x00\x00\x00\x01\x9C0\xB6`\xDFp\xBCN2qR\xE9ps\x19v!\bIg\xE1\xB1D\x1A<\x16A\xBF\xB2\xF3\xC8g\x01\x00\x00\x00jG0D\x02 ~\x05\xC74\xE89l\x82\ay\xF2\x92\xF4u\x85\x93B\xFC\xCC<\xD2\xF7o4\xEF\xA3-\x02\"\x978\xA4\x02 \x1Ffqb\xB4.\xB2\x00\xE7\x8D\x8Cw\f\xFF\x9C\xE043\xE0\x1C\x1A\xE5\xD4(\xA9\x12>\xFE.\xAA.\xE7\x01!\x03\x17Z\xD1:\xD9\x82\xE3|J\xAD_BiB\xD9Lt\xAE-M\x85\xCC;\xEA\x13\xFE\xF4L*\xCD:\x01\xFE\xFF\xFF\xFF\x02\x00\xE1\xF5\x05\x00\x00\x00\x00\x19v\xA9\x14\x89!`1\xAB\x89H$\x95\x97JI\xF4\n8\xF85\xD3s\x93\x88\xACl\x0F\x1A\x1E\x01\x00\x00\x00\x19v\xA9\x14\xDE\xB8\xA3\x1Eq\xCF\r\x8A\xF4\xE4'\x03\x96@\x8E\xDBX\t\e\xCF\x88\xACJ\x00\x00\x00", @hash="b1bdba74d2bcbfe8f9701949af1faa817ed48010751ade23e38b351be723eb56", @binary_hash="V\xEB#\xE7\e5\x8B\xE3#\xDE\x1Au\x10\x80\xD4~\x81\xAA\x1F\xAFI\x19p\xF9\xE8\xBF\xBC\xD2t\xBA\xBD\xB1">, @prev_out_script="v\xA9\x14\x89!`1\xAB\x89H$\x95\x97JI\xF4\n8\xF85\xD3s\x93\x88\xAC", @key=#<Bitcoin::Key:0x007ff8fda7f0a0 @key=#<OpenSSL::PKey::EC:0x007ff8fda7f050 @group=nil>, @pubkey_compressed=true>>]
# Bさんのvin
tx_bldr.input do |i|
i.prev_out B_prev_tx
i.prev_out_index B_prev_out_index
i.signature_key B_key_obj
end
# => [#<Bitcoin::Builder::TxInBuilder:0x007ff8fb8edbf8 @txin=#<Bitcoin::Protocol::TxIn:0x007ff8fb8edbd0 @prev_out_hash=nil, @prev_out_index=nil, @script_sig_length=0, @script_sig="", @sequence="\xFF\xFF\xFF\xFF">, @prev_out_hash="V\xEB#\xE7\e5\x8B\xE3#\xDE\x1Au\x10\x80\xD4~\x81\xAA\x1F\xAFI\x19p\xF9\xE8\xBF\xBC\xD2t\xBA\xBD\xB1", @prev_out_index=0, @prev_tx=#<Bitcoin::Protocol::Tx:0x007ff8fe9c98a8 @scripts=[], @out=[#<Bitcoin::Protocol::TxOut:0x007ff8fe9c93d0 @value=100000000, @pk_script_length=25, @pk_script="v\xA9\x14\x89!`1\xAB\x89H$\x95\x97JI\xF4\n8\xF85\xD3s\x93\x88\xAC">, #<Bitcoin::Protocol::TxOut:0x007ff8fe9c9240 @value=4799991660, @pk_script_length=25, @pk_script="v\xA9\x14\xDE\xB8\xA3\x1Eq\xCF\r\x8A\xF4\xE4'\x03\x96@\x8E\xDBX\t\e\xCF\x88\xAC">], @in=[#<Bitcoin::Protocol::TxIn:0x007ff8fe9c9678 @prev_out_hash="\x9C0\xB6`\xDFp\xBCN2qR\xE9ps\x19v!\bIg\xE1\xB1D\x1A<\x16A\xBF\xB2\xF3\xC8g", @prev_out_index=1, @script_sig_length=106, @script_sig="G0D\x02 ~\x05\xC74\xE89l\x82\ay\xF2\x92\xF4u\x85\x93B\xFC\xCC<\xD2\xF7o4\xEF\xA3-\x02\"\x978\xA4\x02 \x1Ffqb\xB4.\xB2\x00\xE7\x8D\x8Cw\f\xFF\x9C\xE043\xE0\x1C\x1A\xE5\xD4(\xA9\x12>\xFE.\xAA.\xE7\x01!\x03\x17Z\xD1:\xD9\x82\xE3|J\xAD_BiB\xD9Lt\xAE-M\x85\xCC;\xEA\x13\xFE\xF4L*\xCD:\x01", @sequence="\xFE\xFF\xFF\xFF">], @lock_time=74, @ver=1, @enable_bitcoinconsensus=false, @payload="\x01\x00\x00\x00\x01\x9C0\xB6`\xDFp\xBCN2qR\xE9ps\x19v!\bIg\xE1\xB1D\x1A<\x16A\xBF\xB2\xF3\xC8g\x01\x00\x00\x00jG0D\x02 ~\x05\xC74\xE89l\x82\ay\xF2\x92\xF4u\x85\x93B\xFC\xCC<\xD2\xF7o4\xEF\xA3-\x02\"\x978\xA4\x02 \x1Ffqb\xB4.\xB2\x00\xE7\x8D\x8Cw\f\xFF\x9C\xE043\xE0\x1C\x1A\xE5\xD4(\xA9\x12>\xFE.\xAA.\xE7\x01!\x03\x17Z\xD1:\xD9\x82\xE3|J\xAD_BiB\xD9Lt\xAE-M\x85\xCC;\xEA\x13\xFE\xF4L*\xCD:\x01\xFE\xFF\xFF\xFF\x02\x00\xE1\xF5\x05\x00\x00\x00\x00\x19v\xA9\x14\x89!`1\xAB\x89H$\x95\x97JI\xF4\n8\xF85\xD3s\x93\x88\xACl\x0F\x1A\x1E\x01\x00\x00\x00\x19v\xA9\x14\xDE\xB8\xA3\x1Eq\xCF\r\x8A\xF4\xE4'\x03\x96@\x8E\xDBX\t\e\xCF\x88\xACJ\x00\x00\x00", @hash="b1bdba74d2bcbfe8f9701949af1faa817ed48010751ade23e38b351be723eb56", @binary_hash="V\xEB#\xE7\e5\x8B\xE3#\xDE\x1Au\x10\x80\xD4~\x81\xAA\x1F\xAFI\x19p\xF9\xE8\xBF\xBC\xD2t\xBA\xBD\xB1">, @prev_out_script="v\xA9\x14\x89!`1\xAB\x89H$\x95\x97JI\xF4\n8\xF85\xD3s\x93\x88\xAC", @key=#<Bitcoin::Key:0x007ff8fda7f0a0 @key=#<OpenSSL::PKey::EC:0x007ff8fda7f050 @group=nil>, @pubkey_compressed=true>>, #<Bitcoin::Builder::TxInBuilder:0x007ff8fda55cf0 @txin=#<Bitcoin::Protocol::TxIn:0x007ff8fda55cc8 @prev_out_hash=nil, @prev_out_index=nil, @script_sig_length=0, @script_sig="", @sequence="\xFF\xFF\xFF\xFF">, @prev_out_hash="\xFF\xAA\xCD>F\t\x81\xD5\xCD\x1F3Z\xFF\xD6\x101\xF7\x84']\n\x9F\xB7R\xF8f\xEC\x85Gqq\xB9", @prev_out_index=0, @prev_tx=#<Bitcoin::Protocol::Tx:0x007ff8fb8ffe98 @scripts=[], @out=[#<Bitcoin::Protocol::TxOut:0x007ff8fb8ff9c0 @value=100000000, @pk_script_length=25, @pk_script="v\xA9\x14\x12\x0E\x90\xF5\x17}\xE6\x16\x96L,\xBB]@1\x15G\xD4\x19\xE8\x88\xAC">, #<Bitcoin::Protocol::TxOut:0x007ff8fb8ff830 @value=4599982620, @pk_script_length=25, @pk_script="v\xA9\x14\xE3\x10\xB8\xE8\xFE\xF8Pll\xFD4\x16\x9Fi\x04\xE6\xD0\xC9\xCDV\x88\xAC">], @in=[#<Bitcoin::Protocol::TxIn:0x007ff8fb8ffc90 @prev_out_hash="^\xAF\xF9\xFE@&\xC3\xC0\xB2x\xB8\xBC\xEB\xDB\r\r\x05l\x81\xC1}\xC3{\xBCb\x8A\x1F\xD8\xF4\b\x81\xB6", @prev_out_index=1, @script_sig_length=107, @script_sig="H0E\x02!\x00\xC5\xED\xA7\xCEA\x89\xF6x\xA0I\x8E\x15\xBCb\x9Ep\x88\x82M\xAF\xDB\v\x8D\xFA\xBFRH\x16\xC4\xDA\xCE\xBB\x02 \b\xDB2\x1C\xF7e,\xE0\xB1\xF7~\x00)g'\a\\\xA3\x92\xDC\xE5K\xEB!\x9D\xCD\x02\xB29d\xCE\x8A\x01!\x03Q\xC0Ffn\xFA\xF5\x8AbU\x99Lf\xFF\xC2\xB8\xDC\xAE\x8B\f\xD3\x1F\x13\x16l9\xF5\xEC\xEBA\xF7\x83", @sequence="\xFE\xFF\xFF\xFF">], @lock_time=119, @ver=1, @enable_bitcoinconsensus=false, @payload="\x01\x00\x00\x00\x01^\xAF\xF9\xFE@&\xC3\xC0\xB2x\xB8\xBC\xEB\xDB\r\r\x05l\x81\xC1}\xC3{\xBCb\x8A\x1F\xD8\xF4\b\x81\xB6\x01\x00\x00\x00kH0E\x02!\x00\xC5\xED\xA7\xCEA\x89\xF6x\xA0I\x8E\x15\xBCb\x9Ep\x88\x82M\xAF\xDB\v\x8D\xFA\xBFRH\x16\xC4\xDA\xCE\xBB\x02 \b\xDB2\x1C\xF7e,\xE0\xB1\xF7~\x00)g'\a\\\xA3\x92\xDC\xE5K\xEB!\x9D\xCD\x02\xB29d\xCE\x8A\x01!\x03Q\xC0Ffn\xFA\xF5\x8AbU\x99Lf\xFF\xC2\xB8\xDC\xAE\x8B\f\xD3\x1F\x13\x16l9\xF5\xEC\xEBA\xF7\x83\xFE\xFF\xFF\xFF\x02\x00\xE1\xF5\x05\x00\x00\x00\x00\x19v\xA9\x14\x12\x0E\x90\xF5\x17}\xE6\x16\x96L,\xBB]@1\x15G\xD4\x19\xE8\x88\xAC\x1C*.\x12\x01\x00\x00\x00\x19v\xA9\x14\xE3\x10\xB8\xE8\xFE\xF8Pll\xFD4\x16\x9Fi\x04\xE6\xD0\xC9\xCDV\x88\xACw\x00\x00\x00", @hash="b971714785ec66f852b79f0a5d2784f73110d6ff5a331fcdd58109463ecdaaff", @binary_hash="\xFF\xAA\xCD>F\t\x81\xD5\xCD\x1F3Z\xFF\xD6\x101\xF7\x84']\n\x9F\xB7R\xF8f\xEC\x85Gqq\xB9">, @prev_out_script="v\xA9\x14\x12\x0E\x90\xF5\x17}\xE6\x16\x96L,\xBB]@1\x15G\xD4\x19\xE8\x88\xAC", @key=#<Bitcoin::Key:0x007ff8fe9b1410 @key=#<OpenSSL::PKey::EC:0x007ff8fe9b13c0 @group=nil>, @pubkey_compressed=true>>]
出力部(vout)作成
tx_bldr.output do |o|
o.value 199900000 # 1.999 BTC in satoshis
o.script {|s| s.recipient C_addr }
end
# => [#<Bitcoin::Builder::TxOutBuilder:0x007ff8fda35b80 @txout=#<Bitcoin::Protocol::TxOut:0x007ff8fda35b58 @value=199900000, @pk_script_length=25, @pk_script="v\xA9\x14\x89!`1\xAB\x89H$\x95\x97JI\xF4\n8\xF85\xD3s\x93\x88\xAC", @redeem_script=nil>>]
トランザクション生成
送信するトランザクションをBuilderから生成する
# bitcoin-apiを通して送信できるように、Txオブジェクトをシリアライズする(16進数表示)
serialized_tx = tx_bldr.tx.to_payload.bth
# => "010000000256eb23e71b358be323de1a751080d47e81aa1faf491970f9e8bfbcd274babdb1000000006a4730440220119b4da202ef11446671d0b518b9b5d269aadcff77e16c277da93d5a0d8370cd02200377b8306d44b4cd3503a5f65b0db125c8864412cdba8981d9b258e334529c0c012103ad69651a245138ca3c528e37c31d87bed1ef56e203017fbebbedb368e0a96e36ffffffffffaacd3e460981d5cd1f335affd61031f784275d0a9fb752f866ec85477171b9000000006a47304402205c473c35077ed55eab402fee296cbcc450805719b094f3b4f8d3733c681fbb2e02206ec3aa738026d0463d9b1558bbfab404226480a80a08936741c0fe97d5eab0df01210274dbebf96a69dceb21967a98ae2a21df20b7075ddf5ab3d43fe5c4f06ce0c105ffffffff01603bea0b000000001976a91489216031ab89482495974a49f40a38f835d3739388ac00000000"
# 念のため、中身を見ると、2つのvinと1つのvoutが存在することが確認できる
pp tx_bldr.tx.to_hash
# =>
{"hash"=>"2b68e73759e1857c3771b4a138f34202d621073d88ec8542ab1e143a527a7241",
"ver"=>1,
"vin_sz"=>2,
"vout_sz"=>1,
"lock_time"=>0,
"size"=>338,
"in"=>
[{"prev_out"=>
{"hash"=>
"b1bdba74d2bcbfe8f9701949af1faa817ed48010751ade23e38b351be723eb56",
"n"=>0},
"scriptSig"=>
"30440220119b4da202ef11446671d0b518b9b5d269aadcff77e16c277da93d5a0d8370cd02200377b8306d44b4cd3503a5f65b0db125c8864412cdba8981d9b258e334529c0c01 03ad69651a245138ca3c528e37c31d87bed1ef56e203017fbebbedb368e0a96e36"},
{"prev_out"=>
{"hash"=>
"b971714785ec66f852b79f0a5d2784f73110d6ff5a331fcdd58109463ecdaaff",
"n"=>0},
"scriptSig"=>
"304402205c473c35077ed55eab402fee296cbcc450805719b094f3b4f8d3733c681fbb2e02206ec3aa738026d0463d9b1558bbfab404226480a80a08936741c0fe97d5eab0df01 0274dbebf96a69dceb21967a98ae2a21df20b7075ddf5ab3d43fe5c4f06ce0c105"}],
"out"=>
[{"value"=>"1.99900000",
"scriptPubKey"=>
"OP_DUP OP_HASH160 89216031ab89482495974a49f40a38f835d37393 OP_EQUALVERIFY OP_CHECKSIG"}]}
送信実行
bitcoin-apiのsendrawtransactionを使って実行する
oa_api.provider.sendrawtransaction(serialized_tx)
既知のエラー
取扱い不可のUTXO
マイニングによって得られたUTXO(以下に例示)を使う時には、署名に失敗します。
(実務上ほとんど発生しないと思いますが、念のため。)
"vout": [
{
"value": 50.00000000,
"n": 0,
"scriptPubKey": {
"asm": "026fab0d63cf13d6bc57ace182033115a3acf91b6aa70ca70cd7ff753edc9f97de OP_CHECKSIG",
"hex": "21026fab0d63cf13d6bc57ace182033115a3acf91b6aa70ca70cd7ff753edc9f97deac",
"reqSigs": 1,
"type": "pubkey",
"addresses": [
"mpESE8kLTy3AjuydTCNPrfMT1m5WjucZbX"
]
}
}
]
scriptPubKeyを見ると、他のそれと違います。よって、Builderの署名機能を使った時に、署名エラーが発生。
tx = tx_bldr.tx
# =>RuntimeError: Signature error
P2SH
現在のバージョンでは、P2SHの署名はマルチシグのみサポート。マルチシグ以外のRedeemScriptを書いている場合は、P2SHと判定されずに、エラーになる。
RuntimeError: Script type must be hash160, pubkey or multisig
builder.rb:228:in `sig_hash_and_all_keys_exist?'
P2SHの署名は手動で作る必要あり。
あとがき
自分で署名を作りたいときや、P2SHを使うときの署名(検証スクリプトが、scriptSig + scriptPubKeyでない場合)をしたいときは、Builderでは対応できない。
次回(?)は、その場合の署名方法をご紹介します。
(2016-07-06更新) 次回作を書きました。
P2SHとP2PKH宛のUTXOを使った送金から学ぶBitcoinの署名の仕組み
http://qiita.com/osada/items/6d91ccff2b0cb4d34e29
参考
- P2SHを使いこなす
http://qiita.com/osada/items/fe970427474f4326e9ae - scriptSigとscriptPubKeyの違い
http://bitcoin.stackexchange.com/questions/8250/what-is-relation-between-scriptsig-and-scriptpubkey