Signature concurrency issue

+2 votes

Hi,

I'm developing web application using Multichain's multisig. End user receives list of partially signed transactions and he able to sign them. Once he clicks "Sign" button on the UI my application performs signrawtransaction and sendrawtransaction

The problem is that if user clicks "Sign" multiple times in row (for different transactions), multiple requests start executing sendrawtransaction and some of them fail with RPC_TRANSACTION_ALREADY_IN_CHAIN error code.

I tried to re-execute signature for failed transactions via multichain-cli but got the same error RPC_TRANSACTION_ALREADY_IN_CHAIN 

Getting failed transaction by txid via getrawtransaction results in RPC_TX_NOT_FOUND .

If user keep some delay (confirmation delay I guess) between signatures then everything is fine.

So concurrency should be handled in application and Multichain doesn't do that for us, right?

 

 

 

asked Jan 29, 2018 by Dmitry
edited Jan 29, 2018 by Dmitry

1 Answer

+2 votes
 
Best answer
Alongside that RPC_TRANSACTION_ALREADY_IN_CHAIN error, can you please post the textual error explanation you see? Looking in the source code it seems like this may be the wrong error code for a "missing inputs" error. If so, the explanation is that some of the transaction inputs are missing. But this still looks like an error we should check into.
answered Jan 29, 2018 by MultiChain
selected Feb 6, 2018 by Dmitry
from multichain-cli:

error code: -27
error message:
transaction already in block chain

RPC API returns the same.
Any other clues how we can check this? Extended logging or something else?
I searched through the Multichain's source code and found that message "transaction already in block chain" used twice.
Thanks for your patience.

We've discussed this internally. As far as we can tell, the most likely cause is double spending against an existing transaction (i.e. both are trying to spend the same prior UTXO = unspent transaction output).

You would need to ensure that you're not creating multiple transactions that are spending the same outputs, and generally the way to do this is via UTXO locking. When using createrawtransaction or createrawsendfrom, pass the word 'lock' in the action parameter, than the node will not attempt to create any other transactions using the same UTXOs.

Every once in a while, when it's safe, you should use the lockunspent command to unlock all unspent UTXOs, in case the user never signed certain transactions.

We'll update this error message to be less misleading.
Just checked vin section of successful and failed transactions and noted that they have the same txid on input. Talking about UTXO you meant that, right?

>We'll update this error message to be less misleading.

Any chances to get this in 1.0.x version?
Yes, if two transactions are trying to spend the same txid and vout combination, then they are in conflict and only one can be accepted to the blockchain.

And yes, the fixed error message will also be in the 1.0.x series.
What would you recommend to do when all available UTXO for a multisig address will be out of stock?
Also noted that UTXO locking works for a particular node. If I lock UTXO for a multisig address on the first node, on the second node it's still available for spending.
UTXOs should only be kept locked for a short time, so you should have some kind of timeout in your application that unlocks them (via the lockunspent API) if the transaction is not actually signed by the user.

Your other comment is absolutely right - locking is a node-level event and not a blockchain-level one.
Hi. Thanks for your help on this. Could you point me to some documentation (perhaps bitcoin docs?) regarding this issue and how best to solve for it?
Please explain how you would like things to work, then perhaps we can provide some pointers.
Seems locks are kept in memory and not persistent. In case node goes down and then starts again all locked UTXO becomes available for spending. Is there any way to create transaction from multisig address specifying input txid ?
Found most viable solution - do not rely on existing inputs and always create new one.

1. Create transaction with zero assets

createrawsendfrom <local_node_address> '{"<multisig_address>": 0}' '[]' send

returns txid e.g. e88aa877600e418453e1efba870d3f237f061889ab98c75f7244bcf0456a7440

2. Create multisig transactions

createrawtransaction '[{"txid":"e88aa877600e418453e1efba870d3f237f061889ab98c75f7244bcf0456a7440", "vout":0}]' '{}' '[{"for":"multisig_stream", "key":"test_key", "data":"abcd"}]' sign

Transaction created on the first step contains two outputs in the following order:
  vout: 0 for a <multisig_address>
  vout: 1 for a <local_node_address>
  
Is it safe to rely on that order or it could be changed and we should lookup vout index by address?
What your thoughts about this approach?
Yes, this works absolutely fine if you're using transactions to write stream data and not to move assets. (The problem with doing this with assets is that the assets can only be in one place at once.) We were not aware you were using MultiChain in this way or we would have suggested this solution ourselves!

In any event you can rely on the output order being fixed because createrawsendfrom takes the order you give it, then adds a change output at the end.
...