Multi-Signature

P2WSH-MultiSig 失敗並記錄了 OP_0 解決方法

  • July 30, 2019

根據Developer Guide - MultiSig and BIP147 when using OP_CHECKMULTISIG 中的註釋,需要在 scriptSig 前面加上 OP_0 以適應原始比特幣實現中的錯誤。

如果在見證程序中應用相同的解決方法,則腳本似乎無法通過 SCRIPT_FLAGS_VERIFY_NULLDUMMY 檢查。將 OP_0 添加到 MultiSig Witness Program 的正確方法是什麼?根據下面的範例添加 OP_0 會導致最終的堆棧元素包含 0x00 ,但檢查失敗。

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;

一個簡單的解決方法是將空向量添加到見證程序堆棧而不是 OP_0,例如使用witness.stack.emplace_back()而不是witness.stack.push_back(ToByteVector(op0Script)),但這可能會導致還有其他問題嗎?

真正的要求是額外的輸入OP_CHECKMULTISIG為零(即:空字節數組)。

對於非 SegWit 輸入,在堆棧上獲得這樣一個零的唯一規範方法是OP_0scriptSig. OP_0只是將一個空字節數組推入堆棧。

在 SegWit 輸入中,不再有建構該堆棧的腳本。相反,您直接指定堆棧。結果,不僅是推薦的方法,而且唯一的方法是將空數組添加到scriptWitness.

對於在核心測試框架中工作的任何人,我使用此程式碼創建了一個 2 of 2 多重簽名見證堆棧,其中一個空虛擬對像作為堆棧上的第一個元素:

見證程序 = CScript([OP_2, pubkey1, pubkey2, OP_2,OP_CHECKMULTISIG]

tx.wit.vtxinwit[0].scriptWitness.stack = [b’’, sig1, sig2, witness_program]

我不是 Python 程序員,所以我花了一些時間才發現我需要的只是 b’’ 來創建一個空字節數組元素。

引用自:https://bitcoin.stackexchange.com/questions/71451