Getting "mandatory-script-verify-flag-failed" error while making a S|A transaction

4 replies 193 views
degen2009Member
Posts: 5 · Reputation: 119
#1Nov 5, 2022, 02:39 AM
So, I've been trying to use SigHash SINGLE | ANY (0x83), which is supposed to let you combine multiple transactions with this flag into one. Sounds cool in theory, right? But it's just not working for me. Here's the situation: I set up two separate transactions on testnet4, each one using a single input and sending to just one output, both using P2PKH: Transaction 1: 0100000001bd20399c1f2d841f1e147ad2e03df1635f48fc5b8cdbb55ea3a86b14225e2743000000006a4730440220435c5161232d1b3215c44169f7c0328f1af1d40b4a8f2ec16795f442e9b80f7e022047fd3dd15421d2a0956a79e2141f3a72d2fe69c4e6c5bcc75047bd6d8bf019f5832102313b35fdf7af967980d29e684c508c90e11d99b10cab20ef477c9e63d9bf7bf7ffffffff0158980000000000001976a914c103e57c094061209b419e5ca559704a8a22f3f988ac00000000 Transaction 2: 0100000001d38e64afa39dd966f96e07bf56ae44bc3d402630771480f461389edff8a0051e010000006a4730440220645a7a25ebb00194a3b2f940d5618d021f513c2dd9bdc0504f623824e9845d8e0220077af01808e98b7ca97d2927243f9beb8cbd48e85d07dde26ea46c2e79d6c2ee832102e90fb489e28444cdfd85057fcce5f6d6ab1ba525acf570a7ffa355dbb7f21162ffffffff0128a000000000000001976a914d80f48340dd7ee6c7fae287342d3eeb1d4d887e288ac00000000 After that, I tried merging them into one transaction, making sure the outputs were lined up correctly:
3 Reply Quote Share
im_apeHero Member
Posts: 629 · Reputation: 3824
#2Nov 5, 2022, 08:49 AM
When using SIGHASH_SINGLE, regardless of the fact that you are only signing a "single" output, the digest is actually going to be different based on the number of outputs because the output count is still part of the digest and other outputs are still used during signing. When you sign the first input with SIGHASH_SINGLE the output_count used during signing is 1 but when you sign the second input it is 2. You also have to write -1 (negative one or 9 byte 255,255,...,0) as each output that is not being signed. Meaning you are signing a "single" output's script and amount while also signing other outputs but not care about their script or amount (acknowledge that they exist). This is why you can't just reuse the signature produced for a transaction with 1 output (your Transaction 1) for a transaction with 2 outputs (your combined/last tx).
6 Reply Quote Share
degen2009Member
Posts: 5 · Reputation: 119
#3Nov 5, 2022, 10:47 AM
Many thanks for your response! There are Bitcoin resources that mention combining various Single|Anyonecanpay (S|A) transactions as the primary use case for this type of SigHash flag. This is why I provided the interpretation I explained earlier. Based on your response, I now understand that: 1. It is not possible to combine two S|A transactions that are initially unaware of each other. However, 2. It is possible to sign an S|A transaction first, and then add other inputs/outputs using SigHash flags like ALL, which take into account the already-signed S|A transaction (signed separately before adding the other inputs). This is demonstrated here: https://mempool.space/tx/59bd7b2cff5da929581fc9fef31a2fba14508f1477e366befb1eb42a8810a000 I would greatly appreciate your help in validating the two statements above.
7 Reply Quote Share
im_apeHero Member
Posts: 629 · Reputation: 3824
#4Nov 5, 2022, 04:23 PM
It is not a simple "it is possible/impossible" answer. To get a better answer to your question, it is best if you first create exact examples of it (like you did here) and then use a reliable code to generate sighash values and see if they are the same. Then with actual transactions we can analyze why it worked or didn't work by looking at the code. For example in here we had 3 transactions: tx1, tx2 and txCombined. tx1 has 1 input, 1 output. Same as tx2. txCombined combines tx1 & tx2 and has 2 inputs and 2 outputs. The sighah for first and only input of tx1 and first input of txCombined were both: 6b122db40e279d4d557c09a34489dbf957b88829f59abfb7e2151c71af071c7d but the sighash for first and only input of tx2 is 601f841ebc69424c8df7aa264d14fa421e4bb9212c6c0a75da8a4a3b82f86202 while for second input of txCombined is ef8056cea6d4248d3ace4632a5e0ea38f838d855d3ee7341d599cb858163abfb So you can already guess that your tx1 signature should be valid for your txCombined but not the signature of tx2. Now lets go through the code to understand what is causing the difference. In this case we deal with this line: https://github.com/bitcoin/bitcoin/blob/bb57017b2945d5e0bbd95c7f1a9369a8ab7c6fcd/src/script/interpreter.cpp#L1332 which deals with txOutCount or nOutputs and for SIGHASH_SINGLE the number of outputs depends on the index of the input you are signing. And these lines dealing with serialization of outputs after we decided on their count: https://github.com/bitcoin/bitcoin/blob/bb57017b2945d5e0bbd95c7f1a9369a8ab7c6fcd/src/script/interpreter.cpp#L1334-L1335 https://github.com/bitcoin/bitcoin/blob/bb57017b2945d5e0bbd95c7f1a9369a8ab7c6fcd/src/script/interpreter.cpp#L1313-L1319 When signing tx1, tx2 and the first input in txCombined using SIGHASH_SINGLE which is at nIn=0 your txoutcount is going to be 1 (nOutputs). But when you sign the second input in txCombined (which is the only input of tx2) using SIGHASH_SINGLE which is at nIn=1 your txoutcount is going to be 2. That's the first difference that changed the final sighash for second input. The next step is to serialize the outputs. When signing tx1, tx2 and the first input in txCombined using SIGHASH_SINGLE you are only serializing 1 output regardless of how many outputs there are. And the serialization process is the same (L1313-L1319). But when you sign the second input of txCombined using SIGHASH_SINGLE you are serializing 2 outputs while using a default (-1) for the first output and use the correct second output (the default CTxOut() that is passed to the Serialize method). This is the second difference that changed the final sighash for second input.
1 Reply Quote Share
degen2009Member
Posts: 5 · Reputation: 119
#5Nov 5, 2022, 05:50 PM
Thank you very much for your response, and apologies for the delay in getting back to you. Following your explanation, I added a dummy output to the second transaction during the serialization process, and it worked perfectly! However, this leads me to a new question: When the number of inputs exceeds the number of outputs, and all inputs are signed using the SIGHASH_SINGLE | ANYONECANPAY (S|A) flag, how are the outputs serialized for the inputs that go beyond the available outputs? For instance, consider this transaction: https://mempool.space/tx/1395dde8ec0152f4cb090f4d4e8dcb14cc0d8bbba780e686c44934370cd316ec In this case, how are the outputs serialized for inputs 2, 3, and 4, which correspond to indices where there are no matching outputs? I have already tried various options for serializing outputs of inputs 2, 3 and 4, but none of them have worked in order to verify each input's signature (my code reconstructs the hash-preimage for each input and then verifies the signature): - Leaving the output field blank (as in SigHash NONE) - Setting all the outputs to dummy output - If input 1 had output 0 (dummy) and output 1, repeating that pattern in inputs 2, 3 and 4. For example, input 3 had output 0 (dummy) and output 1. Thank you in advance for your guidance! Edit: Just found the solution to the upper question. When the number of inputs exceeds the number of outputs, the S|A signatures are signed with the hash-preimage being "1" https://www.mail-archive.com/bitcoin-development@lists.sourceforge.net/msg01408.html
1 Reply Quote Share

Related topics