Bitcoin Forum
November 10, 2024, 10:49:37 PM *
News: Latest Bitcoin Core release: 28.0 [Torrent]
 
   Home   Help Search Login Register More  
Pages: [1]
  Print  
Author Topic: Difficulty signing a transaction with JSON-RPC and Ruby  (Read 1854 times)
sjors (OP)
Newbie
*
Offline Offline

Activity: 13
Merit: 16


View Profile
May 11, 2013, 05:03:36 PM
 #1

This is sort of a cross-post of my question on StackExchange, but with a different example.

I have the following unsigned raw transaction taken from this example:

Code:
01000000
01
eccf7e3034189b851985d871f91384b8ee357cd47c3024736e5676eb2debb3f2
01000000
19
76a914010966776006953d5567439e5e39f86a0d273bee88ac
ffffffff
01
605af40500000000
19
76a914097072524438d003d23a2f23edb65aae1bb3e46988ac
00000000
01000000

I can parse it into human readable form with the JSON-RPC api:

Code:
curl --user USERNAME --data-binary '{"id":"t0", "method": "decoderawtransaction", "params": [
"0100000001eccf7e3034189b851985d871f91384b8ee357cd47c3024736e5676eb2debb3f2010000001976a914010966776006953d5567439e5e39f86a0d273bee88acffffffff01605af405000000001976a914097072524438d003d23a2f23edb65aae1bb3e46988ac0000000001000000"]

Code:
{"result":{"txid":"7584a4e1f3b46ea7af8230b761711dd4f03494f26d5da86995aa80c5b54e6a59","version":1,"locktime":0,"vin":[{"txid":"f2b3eb2deb76566e7324307cd47c35eeb88413f971d88519859b1834307ecfec","vout":1,"scriptSig":{"asm":"OP_DUP OP_HASH160 010966776006953d5567439e5e39f86a0d273bee OP_EQUALVERIFY OP_CHECKSIG","hex":"76a914010966776006953d5567439e5e39f86a0d273bee88ac"},"sequence":4294967295}],"vout":[{"value":0.99900000,"n":0,"scriptPubKey":{"asm":"OP_DUP OP_HASH160 097072524438d003d23a2f23edb65aae1bb3e469 OP_EQUALVERIFY OP_CHECKSIG","hex":"76a914097072524438d003d23a2f23edb65aae1bb3e46988ac","reqSigs":1,"type":"pubkeyhash","addresses":["1runeksijzfVxyrpiyCY2LCBvYsSiFsCm"]}}]},"error":null,"id":"t0"}

But I can't sign it, not even when I add the corresponding previous transaction and private key (it's not a real transaction though).

Code:
curl --user USERNAME --data-binary '{"id":"t0", "method": "signrawtransaction", "params": [
"0100000001eccf7e3034189b851985d871f91384b8ee357cd47c3024736e5676eb2debb3f2010000001976a914010966776006953d5567439e5e39f86a0d273bee88acffffffff01605af405000000001976a914097072524438d003d23a2f23edb65aae1bb3e46988ac0000000001000000",
["f2b3eb2deb76566e7324307cd47c35eeb88413f971d88519859b1834307ecfec"],
["2g82vgrZTviKG5sN1g2VM7FHgHTm16ej4gmr8ECMzab6"]
]}' http://127.0.0.1:8332/

Code:
{"result":null,"error":{"code":-22,"message":"TX decode failed"},"id":"t0"}

I also think it's strange that I get a TX decode error here and I acutally suspect is not the correct message, see my Github ticket.

This makes it hard for me to figure out what I'm doing wrong in my attempt to sign a transaction in ruby.

Code:
@keypair = OpenSSL::PKey::EC.new("secp256k1")
@keypair.private_key = @YourPrivateKey.hex
@keypair.public_key = ::OpenSSL::PKey::EC::Point.from_hex(@keypair.group, @YourPublicKey)


transaction_hash_bytes = [sha_second].pack("H*")
signature_bytes = @keypair.dsa_sign_asn1(transaction_hash_bytes)
signature = signature_bytes.unpack("H*").first

The pack("H*") and unpack are just guesses, but most similar to what I did with SHA. The result is a hex of varying length (70-72 bytes).

Each time you make a signature with secp256k1 it's different, so I can't use the signature that's provided in the example as a reference point. The only way I know how to test it is to sign a real transaction and try to send it through Blockchain.info's push transaction page.

So I tried this with a real transaction of my own, but then I get a "signature invalid" message. I'm assuming that message means what it says.

If I try to sendrawtransaction through JSON-RPC I just get {"result":null,"error":{"code":-22,"message":"TX rejected"},"id":"t0"}. I'm able to perform decoderawtransaction on both my unsigned and signed real transaction. But when I try to sign it using the JSON-RPC I get the same error as I showed above.

The signature is applied to the sha-256^2 hash of the unsigned transaction. I'm able to reproduce this hash in ruby using the example's input parameters, therefore I assume my script is correct up to that point.

I suspect that I'm doing something wrong related to encoding when I sign the hash. The sha-256 methods in ruby expect a Bignum ("10".hex => 16  ) as input and produce a string as output with human readable hex values, which I can then convert to a Bignum again. However it's less clear what the methods for signing using secp256k1 expect for input and provide as output. It just takes a string and returns a string, regardless of what's in it.  It contains a method that allows you to check a signature, which works when I feed the output back into it, but that doesn't say much.

As far as I know nobody has (publicly) signed a bitcoin transaction using Ruby before, so I can't rule out the possibility of a bug.

Is there a way to verify a signature without trying to actually send the transaction?
sjors (OP)
Newbie
*
Offline Offline

Activity: 13
Merit: 16


View Profile
May 11, 2013, 07:59:02 PM
 #2

It turns out that I needed to remove the final 01 00 00 00 from the unsigned transaction when using signrawtransaction in order to successfully
sign my own real transaction. The one in the example throws an invalid key error, but I can live with that.

So that only leaves the ruby issue.
sjors (OP)
Newbie
*
Offline Offline

Activity: 13
Merit: 16


View Profile
May 13, 2013, 05:31:23 PM
 #3

I made some progress, but I still get a "Signature Invalid" error. I updated my question on Stack Exchange to reflect my progress.

What I learned by studying the original C code of the Bitcoin client, is that it stores the digest as a uint256, which is cast to a char before signing. I think this means that I need to convert my hash in ruby from big endian to little endian. The signature itself is a char and doesn't get cast to anything else as far as I can see.
sjors (OP)
Newbie
*
Offline Offline

Activity: 13
Merit: 16


View Profile
May 13, 2013, 09:00:25 PM
 #4

Yes! I found the golden tip that should help me figure out this problem. I'll post the answer on StackExchange later.
Pages: [1]
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.19 | SMF © 2006-2009, Simple Machines Valid XHTML 1.0! Valid CSS!