lian / bitcoin-ruby

bitcoin utils and protocol in ruby.
Other
923 stars 323 forks source link

Signing an unsigned tx #187

Closed assafshomer closed 7 years ago

assafshomer commented 8 years ago

In this example it is shown how to sign a previously unsigned tx. I think something is missing. Here is an example:

Start with signing a tx the standard way with TxBuilder:

require 'bitcoin'
require 'json'

Bitcoin.network = :testnet3
include Bitcoin::Builder
address = 'myqejYQjppJshpmyuMJ1B7TgUuj8HQs4JN'
wif = 'cPK6QLUjydnrvn5czXHyALAM7DrA7DwhsoLqKYwkMvXbeZTQE2Fg'
key = Bitcoin::Key.from_base58(wif)
p "key: [#{key.priv}], wif: [#{key.to_base58}], address: [#{key.addr}]"
data = "{\n  \"hash\":\"09c479df2c54f4c0b732ed761caba5aabddf0d1ed07435ed90fd5ee1a0739d7d\",\n  \"version\":1,\n  \"lock_time\":0,\n  \"size\":192,\n  \"inputs\":[\n    {\n      \"previous_transaction_hash\":\"bc79a5b2a96e0e67768b77f426d4ee629ab469d1897a0db50a5d65214a246892\",\n      \"output_index\":0,\n      \"amount\":5000000000,\n      \"script\":\"3045022100a9ebf3d21a89a57a546de9c5871ea0038f8b44c27f783331ffdc149ddfd3d89702205dd7702b1ac240558b28494eaa7e21fab79e5194d523efb6187a71170b8a8cf701\",\n      \"addresses\":[\n        \"myqejYQjppJshpmyuMJ1B7TgUuj8HQs4JN\"\n      ]\n    }\n  ],\n  \"outputs\":[\n    {\n      \"amount\":100000,\n      \"spent\":false,\n      \"script\":\"OP_DUP OP_HASH160 c8fb823210e9b8a790d9c56501e768db88706e53 OP_EQUALVERIFY OP_CHECKSIG\",\n      \"script_hex\":\"76a914c8fb823210e9b8a790d9c56501e768db88706e5388ac\",\n      \"script_type\":\"hash160\",\n      \"addresses\":[\n        \"myqejYQjppJshpmyuMJ1B7TgUuj8HQs4JN\"\n      ]\n    },\n    {\n      \"amount\":4999899808,\n      \"spent\":false,\n      \"script\":\"OP_DUP OP_HASH160 16a26aede3c3c07095d8bb4c5b410fb784b93246 OP_EQUALVERIFY OP_CHECKSIG\",\n      \"script_hex\":\"76a91416a26aede3c3c07095d8bb4c5b410fb784b9324688ac\",\n      \"script_type\":\"hash160\",\n      \"addresses\":[\n        \"mhadjFegcTnajogiJ2d6UUS1W6iJiaSxb5\"\n      ]\n    }\n  ],\n  \"amount\":4999999808,\n  \"fees\":192,\n  \"confirmations\":0,\n  \"pool\":\"memory\"\n}"

json = JSON.parse(data)
prev_tx = Bitcoin::P::Tx.from_json(json.to_json)
prev_tx_output_index = 0
value = 5000
# first do it normally with TxBuilder

tx = build_tx do |t|

  # add the input you picked out earlier
  t.input do |i|
    i.prev_out prev_tx
    i.prev_out_index prev_tx_output_index
    i.signature_key key
  end

  # add an output that sends some bitcoins to another address
  t.output do |o|
    o.value value 
    o.script {|s| s.recipient "mugwYJ1sKyr8EDDgXtoh8sdDQuNWKYNf88" }
  end

end

p "verified? #{tx.verify_input_signature(0, prev_tx)}"

We get

"key: [33a546cc5bc858605eaecefef3d772619632237ab24c4544d1a4cc69ecd1c046], wif: [cPK6QLUjydnrvn5czXHyALAM7DrA7DwhsoLqKYwkMvXbeZTQE2Fg], address: [myqejYQjppJshpmyuMJ1B7TgUuj8HQs4JN]"
"verified? true"

as one would expect.

Now let's try to do the same with the method described in the example:

require 'bitcoin'
require 'json'

Bitcoin.network = :testnet3
include Bitcoin::Builder
address = 'myqejYQjppJshpmyuMJ1B7TgUuj8HQs4JN'
wif = 'cPK6QLUjydnrvn5czXHyALAM7DrA7DwhsoLqKYwkMvXbeZTQE2Fg'
key = Bitcoin::Key.from_base58(wif)
p "key: [#{key.priv}], wif: [#{key.to_base58}], address: [#{key.addr}]"
data = "{\n  \"hash\":\"09c479df2c54f4c0b732ed761caba5aabddf0d1ed07435ed90fd5ee1a0739d7d\",\n  \"version\":1,\n  \"lock_time\":0,\n  \"size\":192,\n  \"inputs\":[\n    {\n      \"previous_transaction_hash\":\"bc79a5b2a96e0e67768b77f426d4ee629ab469d1897a0db50a5d65214a246892\",\n      \"output_index\":0,\n      \"amount\":5000000000,\n      \"script\":\"3045022100a9ebf3d21a89a57a546de9c5871ea0038f8b44c27f783331ffdc149ddfd3d89702205dd7702b1ac240558b28494eaa7e21fab79e5194d523efb6187a71170b8a8cf701\",\n      \"addresses\":[\n        \"myqejYQjppJshpmyuMJ1B7TgUuj8HQs4JN\"\n      ]\n    }\n  ],\n  \"outputs\":[\n    {\n      \"amount\":100000,\n      \"spent\":false,\n      \"script\":\"OP_DUP OP_HASH160 c8fb823210e9b8a790d9c56501e768db88706e53 OP_EQUALVERIFY OP_CHECKSIG\",\n      \"script_hex\":\"76a914c8fb823210e9b8a790d9c56501e768db88706e5388ac\",\n      \"script_type\":\"hash160\",\n      \"addresses\":[\n        \"myqejYQjppJshpmyuMJ1B7TgUuj8HQs4JN\"\n      ]\n    },\n    {\n      \"amount\":4999899808,\n      \"spent\":false,\n      \"script\":\"OP_DUP OP_HASH160 16a26aede3c3c07095d8bb4c5b410fb784b93246 OP_EQUALVERIFY OP_CHECKSIG\",\n      \"script_hex\":\"76a91416a26aede3c3c07095d8bb4c5b410fb784b9324688ac\",\n      \"script_type\":\"hash160\",\n      \"addresses\":[\n        \"mhadjFegcTnajogiJ2d6UUS1W6iJiaSxb5\"\n      ]\n    }\n  ],\n  \"amount\":4999999808,\n  \"fees\":192,\n  \"confirmations\":0,\n  \"pool\":\"memory\"\n}"

json = JSON.parse(data)
prev_tx = Bitcoin::P::Tx.from_json(json.to_json)
prev_tx_output_index = 0
value = 5000

tx = Bitcoin::Protocol::Tx.new
tx.add_in Bitcoin::Protocol::TxIn.new(prev_tx.binary_hash, prev_tx_output_index, 0)
tx.add_out Bitcoin::Protocol::TxOut.value_to_address(value, address)
ec_key = Bitcoin.open_key(wif) 
sig = Bitcoin.sign_data(ec_key, tx.signature_hash_for_input(0, prev_tx))
tx.in[0].script_sig = Bitcoin::Script.to_signature_pubkey_script(sig, [ec_key.public_key_hex].pack("H*"))

p "verified? #{tx.verify_input_signature(0, prev_tx)}"

This time the result is false. Changing the wif format doesn't seem to help either ec_key = Bitcoin.open_key(key.priv) What am I missing?

azuchi commented 7 years ago

Bitcoin.open_key(wif) is invalid. open_key dose not support wif format. And ec_key.public_key_hex is uncompressed pubkey, so verify_input_signature is fail.

If you write as follows you can sign correctly.

key = Bitcoin::Key.from_base58(wif)
sig = key.sign(tx.signature_hash_for_input(0, prev_tx))
tx.in[0].script_sig = Bitcoin::Script.to_signature_pubkey_script(sig, [key.pub].pack("H*"))