diff --git a/src/script/sign.h b/src/script/sign.h --- a/src/script/sign.h +++ b/src/script/sign.h @@ -102,15 +102,6 @@ CMutableTransaction &txTo, unsigned int nIn, SigHashType sigHashType); -/** - * Combine two script signatures using a generic signature checker, - * intelligently, possibly with OP_0 placeholders. - */ -SignatureData CombineSignatures(const CScript &scriptPubKey, - const BaseSignatureChecker &checker, - const SignatureData &scriptSig1, - const SignatureData &scriptSig2); - /** Extract signature data from a transaction input, and insert it. */ SignatureData DataFromTransaction(const CMutableTransaction &tx, unsigned int nIn, const CTxOut &txout); diff --git a/src/script/sign.cpp b/src/script/sign.cpp --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -360,130 +360,6 @@ sigHashType); } -static std::vector CombineMultisig( - const CScript &scriptPubKey, const BaseSignatureChecker &checker, - const std::vector &vSolutions, const std::vector &sigs1, - const std::vector &sigs2) { - // Combine all the signatures we've got: - std::set allsigs; - for (const valtype &v : sigs1) { - if (!v.empty()) { - allsigs.insert(v); - } - } - - for (const valtype &v : sigs2) { - if (!v.empty()) { - allsigs.insert(v); - } - } - - // Build a map of pubkey -> signature by matching sigs to pubkeys: - assert(vSolutions.size() > 1); - unsigned int nSigsRequired = vSolutions.front()[0]; - unsigned int nPubKeys = vSolutions.size() - 2; - std::map sigs; - for (const valtype &sig : allsigs) { - for (unsigned int i = 0; i < nPubKeys; i++) { - const valtype &pubkey = vSolutions[i + 1]; - // Already got a sig for this pubkey - if (sigs.count(pubkey)) { - continue; - } - - if (checker.CheckSig(sig, pubkey, scriptPubKey, - STANDARD_SCRIPT_VERIFY_FLAGS)) { - sigs[pubkey] = sig; - break; - } - } - } - // Now build a merged CScript: - unsigned int nSigsHave = 0; - // pop-one-too-many workaround - std::vector result; - result.push_back(valtype()); - for (unsigned int i = 0; i < nPubKeys && nSigsHave < nSigsRequired; i++) { - if (sigs.count(vSolutions[i + 1])) { - result.push_back(sigs[vSolutions[i + 1]]); - ++nSigsHave; - } - } - - // Fill any missing with OP_0: - for (unsigned int i = nSigsHave; i < nSigsRequired; i++) { - result.push_back(valtype()); - } - - return result; -} - -static Stacks CombineSignatures(const CScript &scriptPubKey, - const BaseSignatureChecker &checker, - const txnouttype txType, - const std::vector &vSolutions, - Stacks sigs1, Stacks sigs2) { - switch (txType) { - case TX_NONSTANDARD: - case TX_NULL_DATA: - // Don't know anything about this, assume bigger one is correct: - if (sigs1.script.size() >= sigs2.script.size()) { - return sigs1; - } - - return sigs2; - case TX_PUBKEY: - case TX_PUBKEYHASH: - // Signatures are bigger than placeholders or empty scripts: - if (sigs1.script.empty() || sigs1.script[0].empty()) { - return sigs2; - } - - return sigs1; - case TX_SCRIPTHASH: { - if (sigs1.script.empty() || sigs1.script.back().empty()) { - return sigs2; - } - - if (sigs2.script.empty() || sigs2.script.back().empty()) { - return sigs1; - } - - // Recur to combine: - valtype spk = sigs1.script.back(); - CScript pubKey2(spk.begin(), spk.end()); - - txnouttype txType2; - std::vector> vSolutions2; - Solver(pubKey2, txType2, vSolutions2); - sigs1.script.pop_back(); - sigs2.script.pop_back(); - Stacks result = CombineSignatures(pubKey2, checker, txType2, - vSolutions2, sigs1, sigs2); - result.script.push_back(spk); - return result; - } - case TX_MULTISIG: - return Stacks(CombineMultisig(scriptPubKey, checker, vSolutions, - sigs1.script, sigs2.script)); - default: - return Stacks(); - } -} - -SignatureData CombineSignatures(const CScript &scriptPubKey, - const BaseSignatureChecker &checker, - const SignatureData &scriptSig1, - const SignatureData &scriptSig2) { - txnouttype txType; - std::vector> vSolutions; - Solver(scriptPubKey, txType, vSolutions); - - return CombineSignatures(scriptPubKey, checker, txType, vSolutions, - Stacks(scriptSig1), Stacks(scriptSig2)) - .Output(); -} - namespace { /** Dummy signature checker which accepts all signatures. */ class DummySignatureChecker final : public BaseSignatureChecker { diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -2545,9 +2545,22 @@ ScriptErrorString(err)); } +/* Wrapper around ProduceSignature to combine two scriptsigs */ +SignatureData CombineSignatures(const CTxOut &txout, + const CMutableTransaction &tx, + const SignatureData &scriptSig1, + const SignatureData &scriptSig2) { + SignatureData data; + data.MergeSignatureData(scriptSig1); + data.MergeSignatureData(scriptSig2); + ProduceSignature(DUMMY_SIGNING_PROVIDER, + MutableTransactionSignatureCreator(&tx, 0, txout.nValue), + txout.scriptPubKey, data); + return data; +} + BOOST_AUTO_TEST_CASE(script_combineSigs) { - // Test the CombineSignatures function - Amount amount = Amount::zero(); + // Test the ProduceSignature's ability to combine signatures function CBasicKeyStore keystore; std::vector keys; std::vector pubkeys; @@ -2564,99 +2577,83 @@ CMutableTransaction txTo = BuildSpendingTransaction(CScript(), CTransaction(txFrom)); CScript &scriptPubKey = txFrom.vout[0].scriptPubKey; - CScript &scriptSig = txTo.vin[0].scriptSig; + SignatureData scriptSig; SignatureData empty; - SignatureData combined = CombineSignatures( - scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), - empty, empty); + SignatureData combined = + CombineSignatures(txFrom.vout[0], txTo, empty, empty); BOOST_CHECK(combined.scriptSig.empty()); // Single signature case: - SignSignature(keystore, CTransaction(txFrom), txTo, 0, SigHashType()); - combined = CombineSignatures( - scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), - SignatureData(scriptSig), empty); - BOOST_CHECK(combined.scriptSig == scriptSig); - combined = CombineSignatures( - scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), - empty, SignatureData(scriptSig)); - BOOST_CHECK(combined.scriptSig == scriptSig); - CScript scriptSigCopy = scriptSig; + SignSignature(keystore, CTransaction(txFrom), txTo, 0, + SigHashType().withForkId()); + scriptSig = DataFromTransaction(txTo, 0, txFrom.vout[0]); + combined = CombineSignatures(txFrom.vout[0], txTo, scriptSig, empty); + BOOST_CHECK(combined.scriptSig == scriptSig.scriptSig); + combined = CombineSignatures(txFrom.vout[0], txTo, empty, scriptSig); + BOOST_CHECK(combined.scriptSig == scriptSig.scriptSig); + SignatureData scriptSigCopy = scriptSig; // Signing again will give a different, valid signature: - SignSignature(keystore, CTransaction(txFrom), txTo, 0, SigHashType()); - combined = CombineSignatures( - scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), - SignatureData(scriptSigCopy), SignatureData(scriptSig)); - BOOST_CHECK(combined.scriptSig == scriptSigCopy || - combined.scriptSig == scriptSig); + SignSignature(keystore, CTransaction(txFrom), txTo, 0, + SigHashType().withForkId()); + scriptSig = DataFromTransaction(txTo, 0, txFrom.vout[0]); + combined = + CombineSignatures(txFrom.vout[0], txTo, scriptSigCopy, scriptSig); + BOOST_CHECK(combined.scriptSig == scriptSigCopy.scriptSig || + combined.scriptSig == scriptSig.scriptSig); // P2SH, single-signature case: CScript pkSingle; pkSingle << ToByteVector(keys[0].GetPubKey()) << OP_CHECKSIG; keystore.AddCScript(pkSingle); scriptPubKey = GetScriptForDestination(CScriptID(pkSingle)); - SignSignature(keystore, CTransaction(txFrom), txTo, 0, SigHashType()); - combined = CombineSignatures( - scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), - SignatureData(scriptSig), empty); - BOOST_CHECK(combined.scriptSig == scriptSig); - combined = CombineSignatures( - scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), - empty, SignatureData(scriptSig)); - BOOST_CHECK(combined.scriptSig == scriptSig); + SignSignature(keystore, CTransaction(txFrom), txTo, 0, + SigHashType().withForkId()); + scriptSig = DataFromTransaction(txTo, 0, txFrom.vout[0]); + combined = CombineSignatures(txFrom.vout[0], txTo, scriptSig, empty); + BOOST_CHECK(combined.scriptSig == scriptSig.scriptSig); + combined = CombineSignatures(txFrom.vout[0], txTo, empty, scriptSig); + BOOST_CHECK(combined.scriptSig == scriptSig.scriptSig); scriptSigCopy = scriptSig; - SignSignature(keystore, CTransaction(txFrom), txTo, 0, SigHashType()); - combined = CombineSignatures( - scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), - SignatureData(scriptSigCopy), SignatureData(scriptSig)); - BOOST_CHECK(combined.scriptSig == scriptSigCopy || - combined.scriptSig == scriptSig); - // dummy scriptSigCopy with placeholder, should always choose - // non-placeholder: - scriptSigCopy = CScript() - << OP_0 - << std::vector(pkSingle.begin(), pkSingle.end()); - combined = CombineSignatures( - scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), - SignatureData(scriptSigCopy), SignatureData(scriptSig)); - BOOST_CHECK(combined.scriptSig == scriptSig); - combined = CombineSignatures( - scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), - SignatureData(scriptSig), SignatureData(scriptSigCopy)); - BOOST_CHECK(combined.scriptSig == scriptSig); + SignSignature(keystore, CTransaction(txFrom), txTo, 0, + SigHashType().withForkId()); + scriptSig = DataFromTransaction(txTo, 0, txFrom.vout[0]); + combined = + CombineSignatures(txFrom.vout[0], txTo, scriptSigCopy, scriptSig); + BOOST_CHECK(combined.scriptSig == scriptSigCopy.scriptSig || + combined.scriptSig == scriptSig.scriptSig); // Hardest case: Multisig 2-of-3 scriptPubKey = GetScriptForMultisig(2, pubkeys); keystore.AddCScript(scriptPubKey); - SignSignature(keystore, CTransaction(txFrom), txTo, 0, SigHashType()); - combined = CombineSignatures( - scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), - SignatureData(scriptSig), empty); - BOOST_CHECK(combined.scriptSig == scriptSig); - combined = CombineSignatures( - scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), - empty, SignatureData(scriptSig)); - BOOST_CHECK(combined.scriptSig == scriptSig); + SignSignature(keystore, CTransaction(txFrom), txTo, 0, + SigHashType().withForkId()); + scriptSig = DataFromTransaction(txTo, 0, txFrom.vout[0]); + combined = CombineSignatures(txFrom.vout[0], txTo, scriptSig, empty); + BOOST_CHECK(combined.scriptSig == scriptSig.scriptSig); + combined = CombineSignatures(txFrom.vout[0], txTo, empty, scriptSig); + BOOST_CHECK(combined.scriptSig == scriptSig.scriptSig); // A couple of partially-signed versions: std::vector sig1; uint256 hash1 = SignatureHash(scriptPubKey, CTransaction(txTo), 0, - SigHashType(), Amount::zero()); + SigHashType().withForkId(), Amount::zero()); BOOST_CHECK(keys[0].SignECDSA(hash1, sig1)); - sig1.push_back(SIGHASH_ALL); + sig1.push_back(SIGHASH_ALL | SIGHASH_FORKID); std::vector sig2; uint256 hash2 = SignatureHash( scriptPubKey, CTransaction(txTo), 0, - SigHashType().withBaseType(BaseSigHashType::NONE), Amount::zero()); + SigHashType().withBaseType(BaseSigHashType::NONE).withForkId(), + Amount::zero()); BOOST_CHECK(keys[1].SignECDSA(hash2, sig2)); - sig2.push_back(SIGHASH_NONE); + sig2.push_back(SIGHASH_NONE | SIGHASH_FORKID); std::vector sig3; uint256 hash3 = SignatureHash( scriptPubKey, CTransaction(txTo), 0, - SigHashType().withBaseType(BaseSigHashType::SINGLE), Amount::zero()); + SigHashType().withBaseType(BaseSigHashType::SINGLE).withForkId(), + Amount::zero()); BOOST_CHECK(keys[2].SignECDSA(hash3, sig3)); - sig3.push_back(SIGHASH_SINGLE); + sig3.push_back(SIGHASH_SINGLE | SIGHASH_FORKID); // Not fussy about order (or even existence) of placeholders or signatures: CScript partial1a = CScript() << OP_0 << sig1 << OP_0; @@ -2669,38 +2666,39 @@ CScript complete12 = CScript() << OP_0 << sig1 << sig2; CScript complete13 = CScript() << OP_0 << sig1 << sig3; CScript complete23 = CScript() << OP_0 << sig2 << sig3; - - combined = CombineSignatures( - scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), - SignatureData(partial1a), SignatureData(partial1b)); + SignatureData partial1_sigs; + partial1_sigs.signatures.emplace(keys[0].GetPubKey().GetID(), + SigPair(keys[0].GetPubKey(), sig1)); + SignatureData partial2_sigs; + partial2_sigs.signatures.emplace(keys[1].GetPubKey().GetID(), + SigPair(keys[1].GetPubKey(), sig2)); + SignatureData partial3_sigs; + partial3_sigs.signatures.emplace(keys[2].GetPubKey().GetID(), + SigPair(keys[2].GetPubKey(), sig3)); + + combined = + CombineSignatures(txFrom.vout[0], txTo, partial1_sigs, partial1_sigs); BOOST_CHECK(combined.scriptSig == partial1a); - combined = CombineSignatures( - scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), - SignatureData(partial1a), SignatureData(partial2a)); + combined = + CombineSignatures(txFrom.vout[0], txTo, partial1_sigs, partial2_sigs); BOOST_CHECK(combined.scriptSig == complete12); - combined = CombineSignatures( - scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), - SignatureData(partial2a), SignatureData(partial1a)); + combined = + CombineSignatures(txFrom.vout[0], txTo, partial2_sigs, partial1_sigs); BOOST_CHECK(combined.scriptSig == complete12); - combined = CombineSignatures( - scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), - SignatureData(partial1b), SignatureData(partial2b)); + combined = + CombineSignatures(txFrom.vout[0], txTo, partial1_sigs, partial2_sigs); BOOST_CHECK(combined.scriptSig == complete12); - combined = CombineSignatures( - scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), - SignatureData(partial3b), SignatureData(partial1b)); + combined = + CombineSignatures(txFrom.vout[0], txTo, partial3_sigs, partial1_sigs); BOOST_CHECK(combined.scriptSig == complete13); - combined = CombineSignatures( - scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), - SignatureData(partial2a), SignatureData(partial3a)); + combined = + CombineSignatures(txFrom.vout[0], txTo, partial2_sigs, partial3_sigs); BOOST_CHECK(combined.scriptSig == complete23); - combined = CombineSignatures( - scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), - SignatureData(partial3b), SignatureData(partial2b)); + combined = + CombineSignatures(txFrom.vout[0], txTo, partial3_sigs, partial2_sigs); BOOST_CHECK(combined.scriptSig == complete23); - combined = CombineSignatures( - scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), - SignatureData(partial3b), SignatureData(partial3a)); + combined = + CombineSignatures(txFrom.vout[0], txTo, partial3_sigs, partial3_sigs); BOOST_CHECK(combined.scriptSig == partial3c); } diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -499,6 +499,19 @@ threadGroup.join_all(); } +SignatureData CombineSignatures(const CMutableTransaction &input1, + const CMutableTransaction &input2, + const CTransactionRef tx) { + SignatureData sigdata; + sigdata = DataFromTransaction(input1, 0, tx->vout[0]); + sigdata.MergeSignatureData(DataFromTransaction(input2, 0, tx->vout[0])); + ProduceSignature( + DUMMY_SIGNING_PROVIDER, + MutableTransactionSignatureCreator(&input1, 0, tx->vout[0].nValue), + tx->vout[0].scriptPubKey, sigdata); + return sigdata; +} + BOOST_AUTO_TEST_CASE(test_witness) { CBasicKeyStore keystore, keystore2; CKey key1, key2, key3, key1L, key2L; @@ -594,13 +607,7 @@ CreateCreditAndSpend(keystore2, scriptMulti, output2, input2, false); CheckWithFlag(output2, input2, 0, false); BOOST_CHECK(*output1 == *output2); - UpdateInput( - input1.vin[0], - CombineSignatures(output1->vout[0].scriptPubKey, - MutableTransactionSignatureChecker( - &input1, 0, output1->vout[0].nValue), - DataFromTransaction(input1, 0, output1->vout[0]), - DataFromTransaction(input2, 0, output1->vout[0]))); + UpdateInput(input1.vin[0], CombineSignatures(input1, input2, output1)); CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true); // P2SH 2-of-2 multisig @@ -615,13 +622,7 @@ CheckWithFlag(output2, input2, 0, true); CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH, false); BOOST_CHECK(*output1 == *output2); - UpdateInput( - input1.vin[0], - CombineSignatures(output1->vout[0].scriptPubKey, - MutableTransactionSignatureChecker( - &input1, 0, output1->vout[0].nValue), - DataFromTransaction(input1, 0, output1->vout[0]), - DataFromTransaction(input2, 0, output1->vout[0]))); + UpdateInput(input1.vin[0], CombineSignatures(input1, input2, output1)); CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true); CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true); }