lian / bitcoin-ruby

bitcoin utils and protocol in ruby.
Other
922 stars 322 forks source link

Signature error #251

Closed juanfer closed 6 years ago

juanfer commented 6 years ago

I am trying to build an LTC transaction with this code:

include Bitcoin::Builder
Bitcoin.network = :litecoin
info_address = JSON.parse(RestClient.get "https://chain.so/api/v2/get_tx_unspent/LTC/LiQ9i8ASYP5qZjEBJ91STTA4s5vdnsz4n7")
prev_hash = info_address['data']['txs'][0]['txid']
prev_out_index = info_address['data']['txs'][0]['output_no']
prev_tx_json = JSON.parse((RestClient.get "https://chain.so/api/v2/get_tx/LTC/#{prev_hash}"))
prev_tx = Bitcoin::P::Tx.new([prev_tx_json['data']['tx_hex']].pack("H*"))
key = Bitcoin::Key.new(current_user.getPrivateKey)
new_tx = build_tx do |t|
    t.input do |i|
      i.prev_out prev_tx
      i.prev_out_index prev_out_index
      i.signature_key key
    end
    t.output do |o|
      o.value (3000000 - 1000000) #0.01LTC fee
      o.script {|s| s.recipient "LXzySD6tGEgtHTpoh7ksr3uQLdNLiFTta5"}
    end
end

current_user has current user object, and getPrivateKey gets its private key. Variable key is correctly created for sure

I'm getting "Signature error":

Signature error
Extracted source (around line #307):

#305             # double-check that the script_sig is valid to spend the given prev_script
#306             if @prev_script && !inc.prev_out_forkid && !@tx.verify_input_signature(i, @prev_script)
*307               raise "Signature error"
#308             end
#309           elsif inc.has_multiple_keys?
#310             raise "Keys missing for multisig signing"

Extracted source (around line #201):

#199 
#200         @ins.each_with_index do |inc, i|
*201           sign_input(i, inc)
#202         end
#203 
#204         # run our tx through an encode/decode cycle to make sure that the binary format is sane

Extracted source (around line #200):

#198         end
#199 
*200         @ins.each_with_index do |inc, i|
#201           sign_input(i, inc)
#202         end
#203 

What am I doing wrong? I'm pretty sure variable 'key' has a correct key to sign transaction.

lian commented 6 years ago

i'm pretty sure your key is wrong.

this makes your example code pass. meaning the only part where it fails is the op_hash160 check between your key's public-key hash160 and the prev_tx output script's hash160.

   def op_hash160
     buf = pop_string
-    @stack << Digest::RMD160.digest(Digest::SHA256.digest(buf))
+    #@stack << Digest::RMD160.digest(Digest::SHA256.digest(buf))
+    @stack << ["fe389b9fd4fa3f75d125cb76b03cf950a179da1a"].pack("H*")
   end

to proof the key is wrong run:

key = Bitcoin::Key.new(current_user.getPrivateKey)
p [key.addr, key.hash160, key.hash160 == "fe389b9fd4fa3f75d125cb76b03cf950a179da1a"]                
key.instance_eval{ @pubkey_compressed = false }                                                     
p [key.addr, key.hash160, key.hash160 == "fe389b9fd4fa3f75d125cb76b03cf950a179da1a"]

if both return false it's definitely the wrong key.

juanfer commented 6 years ago

Ok lian, thanks for taking the time to answer. As you predicted, the key was wrong. What I can't understand is why is it wrong: I guess it can be something related to the use of different versions of your gem. Let me explain: I generated the key that I was using with version 0.0.14, and saved it in the database some months ago. A few days ago I run

bundle update

and after that I had the key issue. Also I didn't try to create a Tx before that date. The difference is that in version 0.0.18 docs the method suggested to generate the key is different than the one suggested in version 0.0.14 docs:

old method (which I used in the first place)

key = Bitcoin::generate_key
key #=> [<privkey>, <pubkey>]
address = Bitcoin::pubkey_to_address(key[1])
address #=> <bitcoin address>

versus

new method

key = Bitcoin::Key.generate
key.priv
key.pub
key.addr

I've also noticed that all the addresses generated with the old method (saved in the database before the upgrade) are wrong. Fortunately I didn't make any important Tx to any of that addresses except for that 0.03LTC which I guess I lost for ever... Generating addresses and keys with the new method fixes the problem.

Maybe you should warn somewhere in the old documentation about this issue. Thanks

lian commented 6 years ago

yea i noticed the readme still said Bitcoin.generate_key which still exists for legacy reasons. keys generated with this are all valid, but use the uncompressed pubkey format. Bitcoin.generate_key == Bitcoin::Key.generate(compressed: false) so your old key shouldn't be wrong/broken/invalid. when importing your old key just use Bitcoin::Key.new(priv, nil, compressed: false)

for storing keys its best to use key.to_base58 and Bitcoin::Key.from_base58 since this stores the needed metadata like is_compressed and which network the key is for.

juanfer commented 6 years ago

Great! So my 0.03LTC are not lost :) But I really couldn't find that warning you are talking about. The documentation sources I've checked:

Anyway thank you very much for all your support and your great gem.