Errors when building/sending multisig transactions

1

Going by the example here: https://bitcoinj.github.io/working-with-contracts

I believe I've done everything correctly. Yet my program fails either to propagate correctly or because of a tx error and I cannot determine why

A few things I believe to be the reason

1) My peergroup.broadcastTransaction(spendTx) is not broadcasting properly 2) A malformed raw transaction or scriptsig ( I assume it is this, but again, no errors) 3) Generally not understanding the best method of linking outputs to inputs and signing them appropriately (for instance, do I use UTXO class, or TransactionSignature) 3) Trying to spend funds from a watched address. I have the necessary keys to unlock the UTXO's but I would like to see if I can spend the funds without the use of the wallet class 4) Multisig support in general for bitcoinj is lacking or incomplete. i hope it's not this as I would really like to get this working. But -- I don't think this is the biggest issue because I ran the unit tests and they still all check out clean.

A step through the code to be thorough:

Create 3 ECKeys for a 2-of-3 multisig contract, put them into a list, create a redeem script that sorts keys in lexicographical order and writes a m-of-n multisigoutputscript, then instantiate a Transaction object and assign it as the OUTPUT that I will be using as my INPUT (amount + redeemscript) AKA the UTXO that I will be spending (EDIT: Quick mention. The ECKeys I'm really using are hardcoded values that I made a while ago, these are for illustration purposes. I also should mention that this is a P2SH multisig, not the raw non-standard multisig.

ECKey keyA = new ECKey();
ECKey keyB = new ECKey();
ECKey keyC = new ECKey();

List<ECKey> keys = ImmutableList.of(key1, key2, key3);

Script script = ScriptBuilder.createRedeemScript(2, keys);
Script scriptPubKey = ScriptBuilder.createP2SHOutputScript(script);

Transaction contract = new Transaction(params);
TransactionOutput multiSigOutput = contract.addOutput(Coin.valueOf(50000), scriptPubKey);

Create a second transaction object that I will be using to assemble the output script AKA the (value + destination) address that i will be signing my UTXO over to. Also where the sigHash is signed by 2 of my keys and added to the scriptSig of the transaction

                        Transaction spendTx = new Transaction(params);
                        Address address = Address.fromBase58(params, "19EfMrs5WkcvtBBnuEqP6v1yppeWww61Kc");
                        Script outputScript = ScriptBuilder.createOutputScript(address);
                        spendTx.addOutput(multiSigOutput.getValue(), outputScript);
//                        System.out.println(spendTx.getOutputs());
                        TransactionInput input = spendTx.addInput(multiSigOutput);

now manually sign the inputs...(signatures are in same order as they are in script)

//sign transaction manually
Sha256Hash sigHash = spendTx.hashForSignature(0, script, Transaction.SigHash.ALL, false);
ECKey.ECDSASignature signature = list.get(0).sign(sigHash);
ECKey.ECDSASignature signature1 = list.get(1).sign(sigHash);
TransactionSignature txSig = new TransactionSignature(signature, Transaction.SigHash.ALL, false);
TransactionSignature txSig1 = new TransactionSignature(signature1, Transaction.SigHash.ALL, false);

...now create multisiginputscript, validate and broadcast tx

                        Script inputScript = ScriptBuilder.createP2SHMultiSigInputScript((ImmutableList.of(txSig, txSig1)));
//                        System.out.println(inputScript);
                        input.setScriptSig(inputScript);
                        input.verify(multiSigOutput);

                        peerGroup.broadcastTransaction(spendTx);

                        System.out.println(kit.peerGroup().getConnectedPeers());
                        System.out.println("transaction broadcasted");

ok...this doesn't propagate, but the verification checks out. and I don't even know if it's so much a network problem as it is a code problem because when I change the value in the output script to > the balance of the UTXO it doesn't throw a InsufficientMoneyException like I imagine it should. I'd show some error logs but there aren't any. The only error is when I change the createmultisiginputscript to createP2SHmultisiginputscript, the console puts out a non-null nulldummy error which I also have problems fixing, so I've never been able to know if changing to that method is the correct fix either. I think I set up the connection to the peergroup correctly as well

File chainFile = new File(this.getFilesDir(), "test.spvchain");
System.out.println("does chainfile exist?"  + chainFile.exists());
if(chainFile.exists()) {
    try {
        SPVBlockStore chainStore = new SPVBlockStore(params, chainFile);
        BlockChain chain = new BlockChain(params, chainStore);
        peerGroup = new PeerGroup(params, chain);
        peerGroup.addPeerDiscovery(new DnsDiscovery(params));
        peerGroup.startAsync();
    } catch (BlockStoreException e) {
        e.printStackTrace();
    }

So I don't really get it... I've come to the conclusion my understanding is off somehow. Sorry for the confusing long post. It's been days. Can anyone help?

EDIT: Here's the raw transaction

In hex: 0100000001d917c0a19e19fa1cf2314baf037fdf952877daa4da845ab35d44ef33b45838c300000000fc00473044022022b97b9372d35acaea70f3735dee290f5697cab7b5d6be6f7cdfe31139fba4f90220651091b83570843d4fe8afe02d81417194908b0f6af6f8f3b8f1ad020592409f01473044022036eff4f54b8bf3f834f7bb6e417a04a6a696047f5c75a896c6f9b624ad87680a02200bf2115b64b3b15647178b69151d4588d8a4ade16a4d66e375d0e1265c79fad6014c695221025ab78e076801b45ccb2172bce562103cce1714edbeb02ce6123ce1235eb08c762102d1b74577050b696d5886a7afa61d099ea7ab0a3797766f9819dbd72526b0ce512103facf04db5d9bee657151e30c21e839489c326a277891ebcf75b736ec1e17fc1f53aeffffffff0150c30000000000001976a9145a566f4eda18e818b8d5ca04ee7c5fa3cfbf0e0088ac00000000

In logs:

fbb119d8990cd3912a9ec0118fe3ad8ad61a8388e4a13dd342d9eb67aacfdc65

01-12 17:49:20.670 2507-2507/com.cryptoapp.app I/System.out: in 0[] PUSHDATA(71)[3044022022b97b9372d35acaea70f3735dee290f5697cab7b5d6be6f7cdfe31139fba4f90220651091b83570843d4fe8afe02d81417194908b0f6af6f8f3b8f1ad020592409f01] PUSHDATA(71)[3044022036eff4f54b8bf3f834f7bb6e417a04a6a696047f5c75a896c6f9b624ad87680a02200bf2115b64b3b15647178b69151d4588d8a4ade16a4d66e375d0e1265c79fad601] PUSHDATA1[5221025ab78e076801b45ccb2172bce562103cce1714edbeb02ce6123ce1235eb08c762102d1b74577050b696d5886a7afa61d099ea7ab0a3797766f9819dbd72526b0ce512103facf04db5d9bee657151e30c21e839489c326a277891ebcf75b736ec1e17fc1f53ae] 0.0005 BTC 01-12 17:49:20.670 2507-2507/com.cryptoapp.app I/System.out: outpoint:c33858b433ef445db35a84daa4da772895df7f03af4b31f21cfa199ea1c017d9:0 hash160:6d49586d7529626aaab49812bcd7839aee7e5800 01-12 17:49:20.670 2507-2507/com.cryptoapp.app I/System.out: out DUP HASH160 PUSHDATA(20)[5a566f4eda18e818b8d5ca04ee7c5fa3cfbf0e00] EQUALVERIFY CHECKSIG 0.0005 BTC 01-12 17:49:20.670 2507-2507/com.cryptoapp.app I/System.out: fee 0.00 BTC/kB, 0.00 BTC for 337 bytes 01-12 17:49:20.670 2507-2507/com.cryptoapp.app I/System.out: prps UNKNOWN

Stephen

Posted 2019-01-12T07:04:46.353

Reputation: 13

Answers

1

A couple comments from inspecting the Transaction data you posted.

1) I checked the UTXO you are spending from, which is UTXO

  • 9e7562d19165077d566af47bfbc18283629ed6799da8862660dfb037c353de11
  • Index 0

However, your transaction input is currently referencing the following UTXO:

  • c33858b433ef445db35a84daa4da772895df7f03af4b31f21cfa199ea1c017d9
  • Index 0

Which is not a confirmed transaction on main net.

2) Your input script seems properly formed to spend your P2SH(Multisig) output:

  • zero
  • [3044022022b97b9372d35acaea70f3735dee290f5697cab7b5d6be6f7cdfe31139fba4f90220651091b83570843d4fe8afe02d81417194908b0f6af6f8f3b8f1ad020592409f01]
  • [3044022036eff4f54b8bf3f834f7bb6e417a04a6a696047f5c75a896c6f9b624ad87680a02200bf2115b64b3b15647178b69151d4588d8a4ade16a4d66e375d0e1265c79fad601]
  • [2 [025ab78e076801b45ccb2172bce562103cce1714edbeb02ce6123ce1235eb08c76] [02d1b74577050b696d5886a7afa61d099ea7ab0a3797766f9819dbd72526b0ce51] [03facf04db5d9bee657151e30c21e839489c326a277891ebcf75b736ec1e17fc1f] 3 checkmultisig]

The final element is the embedded script, and should hash to the 20B digest in your UTXO you are spending: it does indeed hash160 correctly to 6d49586d7529626aaab49812bcd7839aee7e5800.

For your reference the P2SH(Multisig) output script of your UTXO is: "hash160 [6d49586d7529626aaab49812bcd7839aee7e5800] equal", note that the hashes are the same, so that's good.

3) You haven't included a fee in your transaction. The output amount of 50000 sats is identical to the UTXO amount. There needs to be a minimum fee for this transaction to be accepted by the mempool.

I hope this helps.

James C.

Posted 2019-01-12T07:04:46.353

Reputation: 2 183

Thanks for your comment. It does verify. If it did not it would throw an error.Stephen 2019-01-12T07:42:56.630

Script script = ScriptBuilder.createRedeemScript(2, keys); and Script script = ScriptBuilder.createMultiSigOutputScript(2, keys); are the same thing except createRedeemScript puts the keys in lexicographical order but it returns the same functionStephen 2019-01-12T07:44:02.080

Can u post both raw tx here, would be easier to help debug.James C. 2019-01-12T07:44:16.837

Ah ok. So u still need to confirm the first tx before broadcasting the second.James C. 2019-01-12T07:45:12.640

So I'm a little bit confused. I thought the first tx was an output script which creates the template for the UTXO as context for the second transaction(and the verification). So you're saying I have to broadcast where i created the multisig script and then broadcast the spend as well?Stephen 2019-01-12T07:48:08.140

Yes. Your spending script has nothing to spend: You generate new keys at the beginning of your script, so that means there is no multisig output in the blockchain yet that u are can spend with those keys. Only after this tx with the multisig output is mined, can it be spent. However, your first multisig tx would also need an input, otherwise we would be creating Bitcoin out of thin air.James C. 2019-01-12T07:51:01.733

Oh I get the confusion. Those ECKeys keys are actually a template to the real keys I am using. There's already a UTXO at my real address here: https://www.blockchain.com/btc/address/3BesSajTyegQaBeyJXes6kyav3srZC3p7D So I'm trying to get that output and spend it with the same keys/script I used to create that P2SH address. I just didn't want to expose them here. Sorry for the mix up. I'll try to change it to be less ambiguous

Stephen 2019-01-12T07:54:11.733

Ok, so you didn't mention the P2SH wrapper above. If that's a P2SH wrapped Multisig you are spending, you are not recreating the P2SH wrapper in your output template above, just the raw multisig, and also not a P2SH redeem script in your spending input.James C. 2019-01-12T07:59:37.410

Ok. Thanks. I tried using a P2SHredeemscript in my spending input but it throws a "non-null nulldummy" error using bitcoinj 0.14.7. Thanks for that tip though. I figured I needed to add an OP_0 code to the top of the stack but that didn't seem to work by itself. I'll play with it some more. Please let me know of anything else that might be wrong.Stephen 2019-01-12T08:03:39.763

Post edited for clarification hopefullyStephen 2019-01-12T08:13:34.650

Also make sure that you construct your first template TX exactly as the one that is mined. That is, it must have the exact same in/outputs. This is because the second TX references this first TX by TXID, which is a Bitcoin256 hash of the full TX data serialisation. If you omit inputs or anything else in your first transaction (compared to the TX which is already confirmed), the second transaction will be referencing the wrong TXID.James C. 2019-01-12T08:17:55.870

Great. I appreciate your help sir. Can I email you for some slight clarification?Stephen 2019-01-12T08:26:45.957

Generally speaking yes, but it might be better to keep the discussion related to this question public in this forum for others to view and validate as well. You can always revise your question or post a reformulated one and I will be happy to help where I can.James C. 2019-01-12T08:34:23.147

Hi, I've migrated both of my input and output functions to match the P2SH format of the original UTXO (updated post to show). I'm still getting the same silent fail however, although everything seems to match up. Now I'm not sure what could possibly be the issue other than it not broadcasting to the peer networkStephen 2019-01-12T16:32:09.557

Can u post raw tx’s?James C. 2019-01-12T16:34:16.797

Hey. I edited my post to include the raw tx in hex and the way it appears in my consoleStephen 2019-01-13T01:15:15.163

ahh. okay. After using the blockcypher transaction decoder https://live.blockcypher.com/btc/decodetx/ and putting in my raw transaction hex from the above post...I think I have compiled a malformed transaction. Hmm

Stephen 2019-01-13T22:01:50.700

I edited the answer above to point out several observations made from inspecting your posted transaction.James C. 2019-01-13T22:47:07.170

Yes. Thank you. I've been trying to change that outpoint for the better part of a day now. I have no idea why it's referring to that transaction idStephen 2019-01-14T04:06:46.230

Thank you so much. I fixed the issue by writing in my own outpoint because I have no idea why the library seemed to change my txid for no reason. I'll be writing a bug reportStephen 2019-01-14T18:17:52.400

Awesome! Do could u mark the answer (check) if it helped u solve ur question? Much appreciated.James C. 2019-01-14T19:03:32.877

I would love to but i do not have enough rep to mark an answer as accepted. I don't know why they add that limit but once I get enough rep I will be happy toStephen 2019-01-15T04:44:12.443

No worries, just a nice-to-have. As long as you got the answer you needed and can continue with your project, everybody is happy.James C. 2019-01-15T11:26:55.290

Sir, I just saw that you were the instructor for teachbitcoin.io. No reason to post this I just thought it was cool since I had that site bookmarkedStephen 2019-01-16T04:06:24.700

Cheers Stephen, happy that you are working on Bitcoin :)James C. 2019-01-16T08:14:47.130

@Stephen: I think there is a misunderstanding here. You cannot upvote a post until you have some rep, but you can always mark answers to your question as the most helpful answer. I think you may have tried to click the "up arrow" which is to upvote a post. Try to click the "checkmark" instead!Murch 2019-01-17T23:32:36.440