今回はbitcoin-rubyを用いてbitcoinの送金transactionを生成して送金してみます。
環境は前回の記事からの続きとなります。
openassets-rubyをrailsに組み込んでみる - アルゴリズムとかオーダーとか
bitcoin-rubyのBlockとTxをbitcoinのデータから生成する - アルゴリズムとかオーダーとか
独自の送金処理などを実装したい場合や、bitcoinを拡張したい場合などは自前で送金transactionを構築必要があります。
いきなり独自のtransactionを作るのは大変なので、まずは基本となる通常のbitcoinの送金transactionをbitcoin-rubyを使って
構築してみます。
ここでは、未使用トランザクション(listunspent)の一覧から一番最初の未使用transactionだけをInTxとして送金transactionを生成しています。
bitcoinの送金の仕組みについては以下のページなどを参考にしてみてください。
Bitcoin概要 - Qiita
以下、コードです。
# 未使用トランザクションの最初の1つを入力にしてBitcoinを送金する def sendto(to_address, amount, fee = 10000) unspent = provider.listunspent.first prev_tx = gettransaction(unspent["txid"]) prev_out_index = unspent["vout"] prev_script = prev_tx.out[prev_out_index].parsed_script prev_key = Bitcoin::Key.from_base58(provider.dumpprivkey(prev_tx.out[prev_out_index].parsed_script.get_address)) in_value = prev_tx.out[0].value if in_value < amount + fee raise "not have enough coin! you have " + in_value.to_s + " satoshi" end tx = Bitcoin::Protocol::Tx.new tx.add_in(Bitcoin::Protocol::TxIn.from_hex_hash(prev_tx.hash, prev_out_index)) tx.add_out(Bitcoin::Protocol::TxOut.value_to_address(amount, to_address)) # おつり tx.add_out(Bitcoin::P::TxOut.value_to_address(in_value - amount - fee, prev_key.addr)) if prev_script.is_pubkey? # ScriptPubkey for coinbase transaction sig_hash = tx.signature_hash_for_input(0, prev_tx.out[prev_out_index].pk_script) tx.in[0].script_sig = Bitcoin::Script.to_pubkey_script_sig(prev_key.sign(sig_hash), nil) elsif prev_script.is_hash160? # ScriptP2PKH tx.in[0].add_signature_pubkey_script(sig, prev_key.pub) end if tx.verify_input_signature(0, prev_tx) provider.sendrawtransaction(tx.to_payload.hth) else false end end
処理の流れとしては、3~7行目で入力に使うTxのデータを収集して、14~18行目で送金トランザクションを生成しています。
それから20~25行目でTxInのlock scriptを解除するためのunlock scriptを生成して、28行目でブロードキャストしています。
27行目ではunlock scriptでInput transactionが正しく解除できるかの確認をしています。
20~22行目のunlock scriptは昔使われていたScriptPubkeyを解除するためのものです。regtestで動かしてる場合は、coinbaseトランザクションのlock scriptがScriptPubkeyになっちゃうのでそれを解除するために追加しました。基本的には24行目のP2PKHを解除するunlock scriptを使うことになると思います。
では上記のメソッドを以前作ったBitcoinUtilに組み込んで実行してみます。
irb(main):013:0> address = BitcoinUtil.provider.getnewaddress => "mq8G26L4kJse3vWka54dh8ZPkJh33VU1kv" irb(main):014:0> BitcoinUtil.sendto(address, 100000000) /Users/nakajo/work/bitcointest/bitcoin-ruby-sample/vendor/bundle/ruby/2.4.0/gems/bitcoin-ruby-0.0.12/lib/bitcoin/script.rb:281: warning: constant ::Fixnum is deprecated /Users/nakajo/work/bitcointest/bitcoin-ruby-sample/vendor/bundle/ruby/2.4.0/gems/bitcoin-ruby-0.0.12/lib/bitcoin/script.rb:455: warning: constant ::Fixnum is deprecated => "e3edc2791463718382ae7efc861f6ed4001149c232b75a4d6d4a4048c51a86a1" irb(main):015:0>
最初にgetnewaddressで送金先のアドレスを生成して、そのアドレス宛に100,000,000satoshi(1.0BTC)を送金しました。
では実際に送られているか確認するために、bitcoin-cli listunspentを実行してみます。
$ bitcoin-cli listunspent [ ]
トランザクションがまだblockに取り込まれていないのでlistunspentの結果はからです。なのでgenerateでブロックを生成します。
$ bitcoin-cli generate 1 [ "0b1dfdb4fc0fd7fd999d3917f445f193276cc6d51a873b61fd4ed1ae523934ac" ]
# regtest環境ではgenerateでブロックを生成すると勝手に未承認transactionを入れてくれるみたいです。
ブロックを生成したので、再度listunspentしてみます。
$ bitcoin-cli listunspent [ { "txid": "c585b2506ebe48f3ca33066fbc3c4eee9e9d7fd92a799daf578bf7007f6f1229", "vout": 0, "address": "mjqBLTT4qVohKeqzDmA2uorm8b6KpsPC8R", "scriptPubKey": "2103e535024e661b4421d55fa6a3642c7b12ef06f39ae946479e473cd3afa0ccbd28ac", "amount": 50.00000000, "confirmations": 101, "spendable": true, "solvable": true, "safe": true }, { "txid": "e3edc2791463718382ae7efc861f6ed4001149c232b75a4d6d4a4048c51a86a1", "vout": 0, "address": "mq8G26L4kJse3vWka54dh8ZPkJh33VU1kv", "account": "", "scriptPubKey": "76a91469667c945606ffde0506e3ff08958136247fc1e588ac", "amount": 1.00000000, "confirmations": 1, "spendable": true, "solvable": true, "safe": true }, { "txid": "e3edc2791463718382ae7efc861f6ed4001149c232b75a4d6d4a4048c51a86a1", "vout": 1, "address": "mjqBLTT4qVohKeqzDmA2uorm8b6KpsPC8R", "scriptPubKey": "76a9142f52e07b6979564045eaca964d7e586140bba7fe88ac", "amount": 48.99990000, "confirmations": 1, "spendable": true, "solvable": true, "safe": true } ]
無事 1.0BTCが送金できていることを確認できました。
#一番上の50BTCは先ほどブロックを生成した時にもらった報酬です。
bitcoin-rubyには比較的簡単にtransactionを生成するためのBitcoin::Builderというのも用意されています。
これを使ってtransactionを構築してから、必要な拡張を行うなり、Builder自体を拡張するのもいいかもです。