7
2
I'm trying to sign (using secp256k1) a transaction as explained here. I'm using my own private key and an unsigned transaction as the input.
I'm able to sign it using the JSON-RPC client, but when I sign it with my own ruby script and try to submit it using blockchain.info/pushTx it returns "Invalid signature".
@secret = PRIVATE_KEY_IN_HEX
@keypair_array = Bitcoin::OpenSSL_EC.regenerate_key(@secret)
@pubkey = @keypair_array[1]
@keypair = OpenSSL::PKey::EC.new("secp256k1")
@keypair.private_key = @keypair_array[0].hex
@keypair.public_key = ::OpenSSL::PKey::EC::Point.from_hex(@keypair.group, @pubkey)
sha_little_endian = SHA_256_HEX_STRING.rjust(64,"0").scan(/(..)/).reverse.join()
signature_binary = @keypair.dsa_sign_asn1([sha_little_endian].pack("H*"))
# Alternative (as in bitcoin-ruby)
# signature_binary = @keypair.dsa_sign_asn1([SHA_256_HEX_STRING].pack("H*"))
signature = signature_binary.unpack("H*").first
First I take the SHA hash, e.g. ABCD...001124 and convert it from big endian to little endian: 241100....CDAB. I do this because in the BitcoinQT client the hash is represented as an uint256 which is cast to a char array used as an input for signing. The result is just stored as a var so I assume I don't have to reverse it.
You'll need to download this OpenSSL_EC file and add the following code to reproduce the above steps.
require 'openssl'
require 'ffi'
module Bitcoin
def self.require_dependency(dummy1, dummy2)
end
autoload :OpenSSL_EC, "./openssl"
end
module ::OpenSSL
class BN
def self.from_hex(hex); new(hex, 16); end
end
class PKey::EC::Point
def self.from_hex(group, hex)
new(group, BN.from_hex(hex))
end
end
end
What am I missing?
Armory can be used to manually sign and verify messages, that should prove a useful debugging tool.
In addition BitcoinQT client can be configured to run a JSON-RPC server, which allows you to verify messages using the
verifymessagemethod.Experimenting with Armory I learned that unlike sha-256 hashes, every time you generate a secp256k1 signature it's different. I also found that the signature generated by my ruby code was not valid as far as Armory was concerned, even though the validate method in ruby did believe it to be correct. That strongly suggests an encoding issue. – Sjors Provoost – 2013-05-11T12:59:12.867
Actually, message verification is not the same as transaction verification. So don't use Armory or the
– Sjors Provoost – 2013-05-11T16:06:49.530verifymessagefor this! Instead, rely onsignrawtransaction. Unfortunately, I'm having issues with that as well.I'm able to sign my own real transaction using
signrawtransaction. The one in the example throws an invalid key error, but I can live with that. – Sjors Provoost – 2013-05-11T19:57:28.863I studied the original C code of the Bitcoin client. It stores the digest as a uint256, which is cast to a char before signing. The signature itself is a char and doesn't get cast to anything else as far as I can see. I modified my question to reflect that. – Sjors Provoost – 2013-05-13T17:28:41.037
Getting close now. I was able to successfully send a transaction using ruby-bitcoin. This should make it a lot easier to figure out what I'm doing wrong in my own script.
– Sjors Provoost – 2013-05-13T20:55:37.363