アルゴリズムとかオーダーとか

仕事で勉強したことなどをまとめてます

bitcoin-rubyで送金する

今回は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自体を拡張するのもいいかもです。