0
As per the note at Developer Guide - MultiSig and BIP147 when using OP_CHECKMULTISIG an OP_0 needs to be prefaced to the scriptSig to accommodate a bug in the original Bitcoin implementation.
If the same workaround is applied in a Witness Program the script seems to fail the SCRIPT_FLAGS_VERIFY_NULLDUMMY check. What's the correct way to add the OP_0 to a MultiSig Witness Program? Adding an OP_0 as per the sample below results in a final stack element containing 0x00 which fails the check.
auto aliceKeyData = ParseHex("bbc27228ddcb9209d7fd6f36b02f7dfa6252af40bb2f1cbc7a557da8027ff866");
CKey aliceKey{};
aliceKey.Set(aliceKeyData.begin(), aliceKeyData.end(), true);
CPubKey alicePubkey = aliceKey.GetPubKey();
auto keyData = ParseHex("619c335025c7f4012e556c2a58b2506e30b8511b53ade95ea316fd8c3286feb9");
CKey bobKey{};
bobKey.Set(keyData.begin(), keyData.end(), true);
CPubKey bobPubKey = bobKey.GetPubKey();
CScript redeemScript = CScript{} << OP_1 << ToByteVector(alicePubkey) << ToByteVector(bobPubKey) << OP_2 << OP_CHECKMULTISIG;
uint256 redeemScriptHash{};
CSHA256().Write(redeemScript.data(), redeemScript.size()).Finalize(redeemScriptHash.begin());
CScript scriptPubkey = CScript{} << OP_0 << ToByteVector(redeemScriptHash); // P2WSH
int amount = 600000000;
CScript scriptSig;
CScriptWitness scriptWitness;
CMutableTransaction tx = BuildFundingTransaction(scriptSig, scriptWitness, amount);
uint256 coinZeroSigHash = SignatureHash(redeemScript, tx, 0, SIGHASH_ALL, amount, SIGVERSION_WITNESS_V0);
CScript op0Script = CScript{} << OP_0;
std::vector<uint8_t> coinZeroBobSig{};
bobKey.Sign(coinZeroSigHash, coinZeroBobSig, 0);
coinZeroBobSig.push_back(SIGHASH_ALL);
CScriptWitness& witness = tx.vin[0].scriptWitness;
witness.stack.push_back(ToByteVector(op0Script)); // <-- Results in 0x00 stack element in Witness Program.
witness.stack.push_back(ToByteVector(coinZeroBobSig));
witness.stack.push_back(ToByteVector(redeemScript));
CDataStream txSpendingStm(SER_NETWORK, PROTOCOL_VERSION);
txSpendingStm << tx;
std::cout << "Spending Tx: " << CTransaction(tx).ToString() << std::endl;
bitcoinconsensus_error err;
auto spendCoinZeroResult = bitcoinconsensus_verify_script_with_amount(scriptPubkey.data(), scriptPubkey.size(), amount, (const unsigned char*)&txSpendingStm[0], txSpendingStm.size(), 0, bitcoinconsensus_SCRIPT_FLAGS_VERIFY_ALL, &err);
std::cout << "Spend Coin Zero result: " << spendCoinZeroResult << ", error code " << err << std::endl;
One trivial fix is to add an empty vector to the Witness Program stack rather than an OP_0, such as by using witness.stack.emplace_back() instead of witness.stack.push_back(ToByteVector(op0Script)), but is that likely to cause any other issues?