|
10 | 10 | #include <clientversion.h>
|
11 | 11 | #include <consensus/amount.h>
|
12 | 12 | #include <consensus/tx_check.h>
|
| 13 | +#include <consensus/tx_verify.h> |
13 | 14 | #include <consensus/validation.h>
|
14 | 15 | #include <core_io.h>
|
15 | 16 | #include <key.h>
|
@@ -1053,4 +1054,99 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
|
1053 | 1054 | CheckIsNotStandard(t, "dust");
|
1054 | 1055 | }
|
1055 | 1056 |
|
| 1057 | +BOOST_AUTO_TEST_CASE(max_standard_legacy_sigops) |
| 1058 | +{ |
| 1059 | + CCoinsView coins_dummy; |
| 1060 | + CCoinsViewCache coins(&coins_dummy); |
| 1061 | + CKey key; |
| 1062 | + key.MakeNewKey(true); |
| 1063 | + |
| 1064 | + // Create a pathological P2SH script padded with as many sigops as is standard. |
| 1065 | + CScript max_sigops_redeem_script{CScript() << std::vector<unsigned char>{} << key.GetPubKey()}; |
| 1066 | + for (unsigned i{0}; i < MAX_P2SH_SIGOPS - 1; ++i) max_sigops_redeem_script << OP_2DUP << OP_CHECKSIG << OP_DROP; |
| 1067 | + max_sigops_redeem_script << OP_CHECKSIG << OP_NOT; |
| 1068 | + const CScript max_sigops_p2sh{GetScriptForDestination(ScriptHash(max_sigops_redeem_script))}; |
| 1069 | + |
| 1070 | + // Create a transaction fanning out as many such P2SH outputs as is standard to spend in a |
| 1071 | + // single transaction, and a transaction spending them. |
| 1072 | + CMutableTransaction tx_create, tx_max_sigops; |
| 1073 | + const unsigned p2sh_inputs_count{MAX_TX_LEGACY_SIGOPS / MAX_P2SH_SIGOPS}; |
| 1074 | + tx_create.vout.reserve(p2sh_inputs_count); |
| 1075 | + for (unsigned i{0}; i < p2sh_inputs_count; ++i) { |
| 1076 | + tx_create.vout.emplace_back(424242 + i, max_sigops_p2sh); |
| 1077 | + } |
| 1078 | + auto prev_txid{tx_create.GetHash()}; |
| 1079 | + tx_max_sigops.vin.reserve(p2sh_inputs_count); |
| 1080 | + for (unsigned i{0}; i < p2sh_inputs_count; ++i) { |
| 1081 | + tx_max_sigops.vin.emplace_back(prev_txid, i, CScript() << ToByteVector(max_sigops_redeem_script)); |
| 1082 | + } |
| 1083 | + |
| 1084 | + // p2sh_inputs_count is truncated to 166 (from 166.6666..) |
| 1085 | + BOOST_CHECK_LT(p2sh_inputs_count * MAX_P2SH_SIGOPS, MAX_TX_LEGACY_SIGOPS); |
| 1086 | + AddCoins(coins, CTransaction(tx_create), 0, false); |
| 1087 | + |
| 1088 | + // 2490 sigops is below the limit. |
| 1089 | + BOOST_CHECK_EQUAL(GetP2SHSigOpCount(CTransaction(tx_max_sigops), coins), 2490); |
| 1090 | + BOOST_CHECK(::AreInputsStandard(CTransaction(tx_max_sigops), coins)); |
| 1091 | + |
| 1092 | + // Adding one more input will bump this to 2505, hitting the limit. |
| 1093 | + tx_create.vout.emplace_back(424242, max_sigops_p2sh); |
| 1094 | + prev_txid = tx_create.GetHash(); |
| 1095 | + for (unsigned i{0}; i < p2sh_inputs_count; ++i) { |
| 1096 | + tx_max_sigops.vin[i] = CTxIn(COutPoint(prev_txid, i), CScript() << ToByteVector(max_sigops_redeem_script)); |
| 1097 | + } |
| 1098 | + tx_max_sigops.vin.emplace_back(prev_txid, p2sh_inputs_count, CScript() << ToByteVector(max_sigops_redeem_script)); |
| 1099 | + AddCoins(coins, CTransaction(tx_create), 0, false); |
| 1100 | + BOOST_CHECK_GT((p2sh_inputs_count + 1) * MAX_P2SH_SIGOPS, MAX_TX_LEGACY_SIGOPS); |
| 1101 | + BOOST_CHECK_EQUAL(GetP2SHSigOpCount(CTransaction(tx_max_sigops), coins), 2505); |
| 1102 | + BOOST_CHECK(!::AreInputsStandard(CTransaction(tx_max_sigops), coins)); |
| 1103 | + |
| 1104 | + // Now, check the limit can be reached with regular P2PK outputs too. Use a separate |
| 1105 | + // preparation transaction, to demonstrate spending coins from a single tx is irrelevant. |
| 1106 | + CMutableTransaction tx_create_p2pk; |
| 1107 | + const auto p2pk_script{CScript() << key.GetPubKey() << OP_CHECKSIG}; |
| 1108 | + unsigned p2pk_inputs_count{10}; // From 2490 to 2500. |
| 1109 | + for (unsigned i{0}; i < p2pk_inputs_count; ++i) { |
| 1110 | + tx_create_p2pk.vout.emplace_back(212121 + i, p2pk_script); |
| 1111 | + } |
| 1112 | + prev_txid = tx_create_p2pk.GetHash(); |
| 1113 | + tx_max_sigops.vin.resize(p2sh_inputs_count); // Drop the extra input. |
| 1114 | + for (unsigned i{0}; i < p2pk_inputs_count; ++i) { |
| 1115 | + tx_max_sigops.vin.emplace_back(prev_txid, i); |
| 1116 | + } |
| 1117 | + AddCoins(coins, CTransaction(tx_create_p2pk), 0, false); |
| 1118 | + |
| 1119 | + // The transaction now contains exactly 2500 sigops, the check should pass. |
| 1120 | + BOOST_CHECK_EQUAL(p2sh_inputs_count * MAX_P2SH_SIGOPS + p2pk_inputs_count * 1, MAX_TX_LEGACY_SIGOPS); |
| 1121 | + BOOST_CHECK(::AreInputsStandard(CTransaction(tx_max_sigops), coins)); |
| 1122 | + |
| 1123 | + // Now, add some Segwit inputs. We add one for each defined Segwit output type. The limit |
| 1124 | + // is exclusively on non-witness sigops and therefore those should not be counted. |
| 1125 | + CMutableTransaction tx_create_segwit; |
| 1126 | + const auto witness_script{CScript() << key.GetPubKey() << OP_CHECKSIG}; |
| 1127 | + tx_create_segwit.vout.emplace_back(121212, GetScriptForDestination(WitnessV0KeyHash(key.GetPubKey()))); |
| 1128 | + tx_create_segwit.vout.emplace_back(131313, GetScriptForDestination(WitnessV0ScriptHash(witness_script))); |
| 1129 | + tx_create_segwit.vout.emplace_back(141414, GetScriptForDestination(WitnessV1Taproot{XOnlyPubKey(key.GetPubKey())})); |
| 1130 | + prev_txid = tx_create_segwit.GetHash(); |
| 1131 | + for (unsigned i{0}; i < tx_create_segwit.vout.size(); ++i) { |
| 1132 | + tx_max_sigops.vin.emplace_back(prev_txid, i); |
| 1133 | + } |
| 1134 | + |
| 1135 | + // The transaction now still contains exactly 2500 sigops, the check should pass. |
| 1136 | + AddCoins(coins, CTransaction(tx_create_segwit), 0, false); |
| 1137 | + BOOST_REQUIRE(::AreInputsStandard(CTransaction(tx_max_sigops), coins)); |
| 1138 | + |
| 1139 | + // Add one more P2PK input. We'll reach the limit. |
| 1140 | + tx_create_p2pk.vout.emplace_back(212121, p2pk_script); |
| 1141 | + prev_txid = tx_create_p2pk.GetHash(); |
| 1142 | + tx_max_sigops.vin.resize(p2sh_inputs_count); |
| 1143 | + ++p2pk_inputs_count; |
| 1144 | + for (unsigned i{0}; i < p2pk_inputs_count; ++i) { |
| 1145 | + tx_max_sigops.vin.emplace_back(prev_txid, i); |
| 1146 | + } |
| 1147 | + AddCoins(coins, CTransaction(tx_create_p2pk), 0, false); |
| 1148 | + BOOST_CHECK_GT(p2sh_inputs_count * MAX_P2SH_SIGOPS + p2pk_inputs_count * 1, MAX_TX_LEGACY_SIGOPS); |
| 1149 | + BOOST_CHECK(!::AreInputsStandard(CTransaction(tx_max_sigops), coins)); |
| 1150 | +} |
| 1151 | + |
1056 | 1152 | BOOST_AUTO_TEST_SUITE_END()
|
0 commit comments