Manually sign a raw transaction

+2 votes
Hi, i'm trying to create a node library that can handle addresses for a Multichain blockchain (node/browser wallet). Generating the addresses works, but now i also want to handle signing inside this library.

For this i only want the actual signing to happen inside the library, so the node can create the raw transaction but i don't want to give my private key to the node ... so signing the raw transaction should happen locally (in the library). Is there any documentation on how to sign a raw transaction? Where are the differences compared to signing a raw transaction for the bitcoin blockchain (because this also takes the network into account)?

kind regards,

Daan
asked Jun 1, 2016 by Daan

1 Answer

+1 vote
It works exactly the same as for bitcoin, and the transaction format is compatible. So whatever external process you would use to sign your transaction with a bitcoin private key can be used to sign a MultiChain transaction.

You should be aware however that MultiChain addresses can be a little different from bitcoin addresses, although you can easily make them fully compatible if you wish to. More information here:

http://www.multichain.com/developers/address-format/
answered Jun 1, 2016 by MultiChain
Thanks, generating addresses with different configurations seems to work.
Thanks, generating addresses with different configurations is working and they validate correctly on my multichain blockchain.

But signing my transaction doesn't seem to work, i'm unable to generate the correct scriptSig (even when i'm using the bitcoinjs-lib).

I created a gist explaining what i'm doing:
https://gist.github.com/daanporon/28087f042fb83507f5dc9389119a0508

This gives me exactly the same result as the bitcoinjs-lib is giving me. But when send this transaction to my blockchain i'm getting the following error:

{ code: -26, message: '16: ConnectInputs failed' }

any idea what's wrong?
First, have you checked that the inputs for that transaction are unspent (use gettxout in the MultiChain API). And second, are you pushing the privateKey correctly, i.e. have you checked the same code works with bitcoin transactions?
Sorry for the late response, but i just had some time to look at this again. The configuration of the blockchain i am using is based on the one from bitcoin. I am also testing the signing with the bitcoinjs-library (http://bitcoinjs.org/) and this is producing the same results i am.

When i take a closer look to my result and compare this to the one i am getting from the node i see this:

=======
manually signed / bitcoinjs:
01000000019b8f7b538e340b086bf962522d54286f5770493cd802faea8b5bb50ce5aa05a6000000006b48

3045022100bccb33ec59a0c1e29a72584ebe0c9db5adcfea3781cc31abcc25d2bdcbc2140f0220508e82fa6a22bf4c16148ac38f9b2905d04432cfe1d905192b0cb6d60c93e46d // Signature_DER

01 // SIGHASH_ALL
21 // 33 bytes
0246b0881504fa33ab076258731cf67327b81cf7fe9cc9b8d5ad280505c5757ef8 // public key

ffffffff0200000000000000003176a9146f7a46827ef5d30917ad65cc20279fb47697ffce88ac1673706b71790400000b010000a60501000000000000007500000000000000003176a914dbc9f86d4908e4ce375368696a71869995865a7388ac1673706b71790400000b010000a60563000000000000007500000000
=======
Multichain node:
01000000019b8f7b538e340b086bf962522d54286f5770493cd802faea8b5bb50ce5aa05a6000000006b48

30450221009e4e6e33266e317d65c24495e542f74dbc268df492daabbc09fb531151d1f4be02206df90ced9122d97cdf49e4be2a965799771c88b110874a15a27068f339cb2bf9 // Signature_DER

01 // SIGHASH_ALL
21 // 33 bytes
0246b0881504fa33ab076258731cf67327b81cf7fe9cc9b8d5ad280505c5757ef8 // public key

ffffffff0200000000000000003176a9146f7a46827ef5d30917ad65cc20279fb47697ffce88ac1673706b71790400000b010000a60501000000000000007500000000000000003176a914dbc9f86d4908e4ce375368696a71869995865a7388ac1673706b71790400000b010000a60563000000000000007500000000
=======

As you can see the only difference is the Signature DER. But the weird thing is the first part is the same (3045022100).

Is multichain also using the pubkeyhash scriptType? (OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG) Or should there be something extra in the signatureScript because i'm transfering assets?

kind regards,
Daan
The script for outputs containing assets also contain an OP_DROP with metadata describing those assets. So it's possible this is tripping up BitcoinJS, if it won't recognize those types of scripts. But everything is still represented using bitcoin's transaction format, so if that is the issue, it shouldn't be too hard to fix inside BitcoinJS.

http://www.multichain.com/developers/native-assets/
I think the problem isn't related to the outputs in the transaction, the output scripts isn't something i did myself (this is done by the Multichain node) and they seem to be correct. Also if it had something to do with the outputs then the last part of my hex string wouldn't match.

The problem has something to do with the input script ... but i can't see what ... everything seems to be correct for as far as i can see.
OK, I'll forward this on to the right person and see if they can help.
It looks like the error is in the hashForSignature calculation. It is not just transaction hash. Complete documentation can be found here: https://en.bitcoin.it/wiki/OP_CHECKSIG

Please notice steps 7 and 8: to calculate hash-for-signature for K-th input you have to create modified transaction:
7. Remove all inputs scripts (replace by 1 byte 0x00), except K-th
8. Replace K-th input script with script of the spent output
hashForSignature is the hash of this modified transaction

To calculate this, you have to know output scripts you are spending (and pass them to your signTransaction function).
I just noticed you are probably building this modified transaction. But you cannot just construct the script like "OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG", asset and permission information is added to the output script (see the binary string followed by OP_DROP in your own example). You need full script, not just its "address" section.
I'm only interested in signing the transaction ... the output transactions are still generated on the node:

+ i created a raw transaction using the api (createrawtransaction)
+ added the raw change (appendrawchange)
+ and then i decoded this transaction (decoderawtransaction)

This decoded transaction is the one i'm using as an input for my signing function. As you can see on line 75, the output transaction already has the asset and permission information appended before the OP_DROP.
https://gist.github.com/daanporon/28087f042fb83507f5dc9389119a0508#file-sign-js-L75

Do i also have to add something like this for the input scripts? Or is it ok to only do this for the input script: "OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG"? In my example there is only one input script, so clearing other input scripts shouldn't be the problem.
If there is only one input, cleaning input scripts is indeed not a problem.

But to calculate hashForSignature you have to use full script of the output you are spending, i.e.   {'txid': 'ea8eb0e06ab835689a3198cdcb30be426ece2e3d85e21db9e3c0b9fb2d395da8','vout': 1}

It also has structure OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG <asset information> OP_DROP

You can retrieve it using decoderawtransaction ea8eb0e06ab835689a3198cdcb30be426ece2e3d85e21db9e3c0b9fb2d395da8
ok thanks now it finally seems to work :) didn't know i had to include the entire script from the previous transaction ... was doing it the same way bitcoin was doing it with "OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG" as hashForSignature, but apparently the input scriptSig also needs the asset information? thought it was enough if the asset information was included in the vout scripts.
Great!

It is how bitcoin signature works - entire output script should be taken for hash calculation. It may be more complicated than simple pay-to-pubkeyhash script. It may be pay-to-scripthash or any other script from https://en.bitcoin.it/wiki/Script
Hi Daan,
I am also trying to do external signing, would it possible for you to share the working example. I am also getting a "ConnectInputs failed" message.
i got it to work by signing the script generated by the nodes in stead of trying to create the script myself. This because the node will add some extra stuff behind the OP_DROP statement (http://www.multichain.com/developers/native-assets/) ... and for me it was ok to let the node handle this. The only thing i wanted, was to keep the private key away from the node. I can share my findings if you want? Just need to retest it and clean it up a bit :)
Yes, this is the right pattern. Use createrawtransaction to prepare the unsigned transaction, then sign it externally, then pass back to sendrawtransaction.
I uploaded the code i have right now (needs some cleaning) ... it can be found here: https://github.com/Kunstmaan/multichain-address-node
Hi Guys

We are just coming to trying to sign transactions outside of the multichain node and found this thread very informative, but to clarify the process, is it:

1 - Generate the raw transaction using the multichain api
2 - Decode the raw transaction (the scriptSigs will be empty)
3 - For each input get the scriptPubKey for the input and insert that into the decoded transaction
4 - Hash and sign the entire transaction message to create the scriptSig
5 - Repeat for any additional inputs using the original decoded transaction message
6 - Once done, set the scriptSigs for all inputs in the decoded transaction
7 - Encode the signed transaction


Is that the flow at a high level?  The code on github has been a fantastic help, but we're not quite there yet and hence want to ensure we're clear on the steps  involved.   

A couple of followup questions in this:

1) What is the structure of the encoded raw transaction you get back from createrawtransaction, I tried to decode it thinking it was just hex, but there is more encoding involved?

2) The scriptSig has both asm and hex properties, what's the difference between them, the characters at the start and in place of the OP_DROP?

Thanks

Marty
1. The raw transaction structure is the same as bitcoin's, and it's indeed encoded as hexadecimal. See: https://en.bitcoin.it/wiki/Transaction#general_format_of_a_Bitcoin_transaction_.28inside_a_block.29

2. The asm and hex properties of a script are just different representations of the same information. The hex is just the raw hex of the script in its entirety. The asm is a breakdown of that script into bitcoin script opcodes and individual pieces of data, along these lines: https://en.bitcoin.it/wiki/Script
Hi,

We are signing transactions outside of multichain and have managed to get a working version of this with a little help from this thread - thanks for that!  Flow we are using:

1. Use multichain node to createrawtransaction
2. Use multichain to decode the transacion hex
3. Add output scriptPubKeys into the corresponding input scripts in the decoded transaction
4. Hash the data
5. Sign the hashed data using bitcore.crypto.ECDSA.sign(dataToSign, key)
6. Once done, set the scriptSigs for all the inputs in the decoded transaction
7. Encode and sendrawtransaction

We had hoped to use a single signing module that we currently use for signing bitcoin transactions (in Step 5), this module uses bitcore.crypto.ECDSA.sign(dataToSign, key, 'little') which gives us a signature in little endian format.  This however does not seem to be compatable with multichain.  Does multichain support little endian format??  And if so is there a setting that we require on startup of the chain?

Thanks,
Emer
All the byte ordering of transaction fields is the same in MultiChain as in bitcoin, so I don't think that's your problem. Please check this entire workflow works with bitcoin transactions and Bitcoin Core (on testnet), because the same workflow should be fine with MultiChain.
We have tested the entire flow against bitcoin as we had implemented this first.  The variance we have found is when we tried to use our bitcoin stack to sign a multichain transaction.  The only difference we can see between the two is that our bitcoin stack uses multisig addresses and our tests against multichain have been with non-multisig addresses.

To get the multichain transactions accepted by the node we need to drop the 'little' from the call to the bitcore signing function.  We were looking to understand if there was a difference in the signatures between the two blockchains.  Appreciate you are saying there is not, but can you think of any reason for the need to change the call to the sign function from little to big endian?

Cheers

Marty
Hi, i added an example to my github repo where i sign a transaction of the node. Even without uploading the private key to the node. I only added the addresses as a watch only address. Tested this on my setup and it works ... the manually generated transaction gets confirmed by the blockchain.

https://github.com/Kunstmaan/multichain-address-node/blob/master/examples/sign_transaction/sign_transaction.js
Regarding signature endianness.

We didn't make any changes in signature format, including endianness.

MultiChain signature is fully compatible with Bitcoin Core's, i.e. signature created by MultiChain is accepted by Bitcoin network and vice versa, signature created by "bitcoin-cli signrawtransaction" is accepted by MultiChain
SOLVED :  Thanks for the help on this, Daan your code was most helpful.  sha256 always returns in big endian format.  Once we flipped this to little endian format using

new bitcore.encoding.BufferReader(hash).readReverse()

we were able to sign with our existing module that uses

 bitcore.crypto.ECDSA.sign(dataToSign, key, 'little')

and multichain then accepted the transaction.

Thanks
Emer
Glad you got this sorted and thanks daan for your help.
What is the format for the public and private keys passed into Daan's signTransaction function? In his sign.js example the key inputs were  Buffer.from('02fdcee0919d98a318fb34318ab6645420252eadf5cbfe09f5c4cc07d58f22c628', 'hex'), Buffer.from('825119fcc0c096916d192a8b62f69a78996cc3c7a73fe8665b74b7d7fe6d9a98', 'hex'));
How do you get the keys in that format?
Also, where does the sign function in Daan's address.js (https://github.com/Kunstmaan/multichain-address-node/blob/master/lib/address.js) get the public and private keys? From the redeemScript? If so, how would you get the redeemScript from an already created transaction (created using createrawtransaction) How do you format it to pass into the function?
'It also has structure OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG <asset information> OP_DROP'
How do you format the asset information in this if you get it from decoderawtransaction?
Not sure what you mean with the format of the public and the private key

both are generated here:
https://github.com/Kunstmaan/multichain-address-node/blob/master/lib/address.js#L19
this is based on:
https://www.multichain.com/developers/address-key-format/

The configuration of the blockchain can be found here:
https://github.com/Kunstmaan/multichain-address-node/blob/master/examples/sign_transaction/mcbitcoin/docker-compose.yml

The Public and private keys in the sign function are inside the Address object. As you can see here:
https://github.com/Kunstmaan/multichain-address-node/blob/master/examples/sign_transaction/sign_transaction.js#L246
we are using issuer.sign ... and issuer contains his pub and priv key.

The redeem script is the scriptPubKey from the UTXO of the input, it is in this format:
76a9142b31915c800e9240f4b0238defebc5910e3031b388ac0c73706b67320000000000000075

But you can run the example .. there are docker files included. When you run it you can see exactly what is happening ...
when I try to run the example, I get this error:
{ [Error: getaddrinfo ENOTFOUND master.mcbitcoin.docker master.mcbitcoin.docker:80]
  code: 'ENOTFOUND',
  errno: 'ENOTFOUND',
  syscall: 'getaddrinfo',
  hostname: 'master.mcbitcoin.docker',
  host: 'master.mcbitcoin.docker',
  port: 80 }
Is that a docker error? What should I do? Thanks.
got it to work, just connected it to my own chain. Thanks for all the help!
Could you write a function with input is hexdata (result of createrawsendfrom) and return signedtransaction
...