test: Enhance GetTxSigOpCost tests for coinbase transactions#32840
test: Enhance GetTxSigOpCost tests for coinbase transactions#32840average-gary wants to merge 5 commits into
Conversation
|
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. Code Coverage & BenchmarksFor details see: https://corecheck.dev/bitcoin/bitcoin/pulls/32840. ReviewsSee the guideline for information on the review process. ConflictsReviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first. |
871c671 to
2449a7c
Compare
|
🚧 At least one of the CI tasks failed. HintsTry to run the tests locally, according to the documentation. However, a CI failure may still
Leave a comment here, if you need help tracking down a confusing failure. |
2449a7c to
0aef3d9
Compare
This comment was marked as spam.
This comment was marked as spam.
0aef3d9 to
6b6b0f4
Compare
| @@ -182,6 +186,7 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost) | |||
| // The witness of a coinbase transaction is not taken into account. | |||
| spendingTx.vin[0].prevout.SetNull(); | |||
| assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags) == 0); | |||
There was a problem hiding this comment.
Why did you think this single test was not enough? Is there additional clarity or coverage from the additional checks?
There was a problem hiding this comment.
It seemed lacking since it was only tested for one script type (p2wpkh).
There was a problem hiding this comment.
Mmh, I think that could clarify a bit how the sigops are accounted for, but if you can read the test, you can also just go and read the original code? I guess it could still guard against dumb regressions of the accounting code though.
| @@ -154,6 +154,11 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost) | |||
|
|
|||
| // P2SH sigops are not counted if we don't set the SCRIPT_VERIFY_P2SH flag | |||
| assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, /*flags=*/0) == 0); | |||
|
|
|||
| // The witness of a coinbase transaction is not taken into account. | |||
There was a problem hiding this comment.
This comment is misleading. It is not just the witness that is not taken into account, but also any p2sh sigops.
| @@ -154,6 +154,11 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost) | |||
|
|
|||
There was a problem hiding this comment.
Can you add a case to the multisig case too? There we should be expecting a sigop cost, even if the first input's prevout is null.
| @@ -182,6 +186,7 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost) | |||
| // The witness of a coinbase transaction is not taken into account. | |||
| spendingTx.vin[0].prevout.SetNull(); | |||
| assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags) == 0); | |||
There was a problem hiding this comment.
Mmh, I think that could clarify a bit how the sigops are accounted for, but if you can read the test, you can also just go and read the original code? I guess it could still guard against dumb regressions of the accounting code though.
Found while reviewing bitcoin#32840 The `nullptr` witness path was dead in normal code paths: removing it deletes unreachable logic. Code coverage proof: https://maflcko.github.io/b-c-cov/total.coverage/src/script/interpreter.cpp.gcov.html#L2135
There was a problem hiding this comment.
I have gone through the cases, I think we should take this opportunity and unify the test to use BOOST_ checkers for better error messages, to split the big test into smaller self-contained tests (otherwise the first failure will break the remaining ones - though this will result in some setup-repetition, but we can add helpers for those).
I have applied the cleanup that I would like to see here, if you decide to accept any of it, please do it in multiple focused commits.
Full patch
// Copyright (c) 2012-present The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <addresstype.h>
#include <coins.h>
#include <consensus/consensus.h>
#include <consensus/tx_verify.h>
#include <key.h>
#include <pubkey.h>
#include <script/interpreter.h>
#include <script/script.h>
#include <script/solver.h>
#include <test/util/setup_common.h>
#include <uint256.h>
#include <vector>
#include <boost/test/unit_test.hpp>
static constexpr script_verify_flags STANDARD_SCRIPT_VERIFY_FLAGS{SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH};
struct TestCoinsViewCache {
CCoinsView dummy;
CCoinsViewCache cache{&dummy};
};
static CPubKey GenerateTestPubKey()
{
return GenerateRandomKey().GetPubKey();
}
static std::vector<unsigned char> Serialize(const CScript& s)
{
std::vector<unsigned char> sSerialized(s.begin(), s.end());
return sSerialized;
}
static CTransaction MakeCoinBase(const CMutableTransaction& tx)
{
CMutableTransaction coinbase{tx};
coinbase.vin[0].prevout.SetNull();
return CTransaction{coinbase};
}
BOOST_FIXTURE_TEST_SUITE(sigopcount_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(GetSigOpCount)
{
CScript s1;
BOOST_CHECK_EQUAL(s1.GetSigOpCount(false), 0);
BOOST_CHECK_EQUAL(s1.GetSigOpCount(true), 0);
uint160 dummy;
s1 << OP_1 << ToByteVector(dummy) << ToByteVector(dummy) << OP_2 << OP_CHECKMULTISIG;
BOOST_CHECK_EQUAL(s1.GetSigOpCount(true), 2);
s1 << OP_IF << OP_CHECKSIG << OP_ENDIF;
BOOST_CHECK_EQUAL(s1.GetSigOpCount(true), 3);
BOOST_CHECK_EQUAL(s1.GetSigOpCount(false), 21);
CScript p2sh = GetScriptForDestination(ScriptHash(s1));
CScript scriptSig;
scriptSig << OP_0 << Serialize(s1);
BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(scriptSig), 3);
std::vector<CPubKey> keys;
for (int i{0}; i < 3; i++) {
CKey k = GenerateRandomKey();
keys.push_back(k.GetPubKey());
}
CScript s2 = GetScriptForMultisig(1, keys);
BOOST_CHECK_EQUAL(s2.GetSigOpCount(true), 3);
BOOST_CHECK_EQUAL(s2.GetSigOpCount(false), 20);
p2sh = GetScriptForDestination(ScriptHash(s2));
BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(true), 0);
BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(false), 0);
CScript scriptSig2;
scriptSig2 << OP_1 << ToByteVector(dummy) << ToByteVector(dummy) << Serialize(s2);
BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(scriptSig2), 3);
}
/**
* Verifies script execution of the zeroth scriptPubKey of tx output and
* zeroth scriptSig and witness of tx input.
*/
static ScriptError VerifyWithFlag(const CTransaction& output, const CMutableTransaction& input, script_verify_flags flags)
{
ScriptError error;
CTransaction inputi(input);
bool ret = VerifyScript(inputi.vin[0].scriptSig, output.vout[0].scriptPubKey, &inputi.vin[0].scriptWitness, flags,
TransactionSignatureChecker(&inputi, 0, output.vout[0].nValue, MissingDataBehavior::ASSERT_FAIL), &error);
BOOST_CHECK((ret == true) == (error == SCRIPT_ERR_OK));
return error;
}
/**
* Builds a creationTx from scriptPubKey and a spendingTx from scriptSig
* and witness such that spendingTx spends output zero of creationTx.
* Also inserts creationTx's output into the coins view.
*/
static void BuildTxs(CMutableTransaction& spendingTx, CCoinsViewCache& coins, CMutableTransaction& creationTx, const CScript& scriptPubKey, const CScript& scriptSig, const CScriptWitness& witness)
{
creationTx.version = 1;
creationTx.vin.resize(1);
creationTx.vin[0].prevout.SetNull();
creationTx.vin[0].scriptSig = CScript();
creationTx.vout.resize(1);
creationTx.vout[0].nValue = 1;
creationTx.vout[0].scriptPubKey = scriptPubKey;
BOOST_REQUIRE(CTransaction(creationTx).IsCoinBase());
spendingTx.version = 1;
spendingTx.vin.resize(1);
spendingTx.vin[0].prevout.hash = creationTx.GetHash();
spendingTx.vin[0].prevout.n = 0;
spendingTx.vin[0].scriptSig = scriptSig;
spendingTx.vin[0].scriptWitness = witness;
spendingTx.vout.resize(1);
spendingTx.vout[0].nValue = 1;
spendingTx.vout[0].scriptPubKey = CScript();
BOOST_REQUIRE(!CTransaction(spendingTx).IsCoinBase());
AddCoins(coins, CTransaction(creationTx), 0);
}
BOOST_AUTO_TEST_CASE(GetTxSigOpCost_Multisig)
{
CMutableTransaction creationTx, spendingTx;
TestCoinsViewCache test_coins;
CPubKey pubkey{GenerateTestPubKey()};
CScript scriptPubKey = CScript() << 1 << ToByteVector(pubkey) << ToByteVector(pubkey) << 2 << OP_CHECKMULTISIGVERIFY;
// Do not use a valid signature to avoid using wallet operations.
CScript scriptSig = CScript() << OP_0 << OP_0;
BuildTxs(spendingTx, test_coins.cache, creationTx, scriptPubKey, scriptSig, CScriptWitness());
// Legacy counting only includes signature operations in scriptSigs and scriptPubKeys
// of a transaction and does not take the actual executed sig operations into account.
// spendingTx in itself does not contain a signature operation.
BOOST_CHECK_EQUAL(GetTransactionSigOpCost(CTransaction(spendingTx), test_coins.cache, STANDARD_SCRIPT_VERIFY_FLAGS), 0);
// creationTx contains two signature operations in its scriptPubKey, but legacy counting
// is not accurate.
BOOST_CHECK_EQUAL(GetTransactionSigOpCost(CTransaction(creationTx), test_coins.cache, STANDARD_SCRIPT_VERIFY_FLAGS), MAX_PUBKEYS_PER_MULTISIG * WITNESS_SCALE_FACTOR);
// Sanity check: script verification fails because of an invalid signature.
BOOST_CHECK_EQUAL(VerifyWithFlag(CTransaction(creationTx), spendingTx, STANDARD_SCRIPT_VERIFY_FLAGS), SCRIPT_ERR_CHECKMULTISIGVERIFY);
// Coinbase input sigops are not counted (P2SH included).
BOOST_CHECK_EQUAL(GetTransactionSigOpCost(MakeCoinBase(spendingTx), test_coins.cache, STANDARD_SCRIPT_VERIFY_FLAGS), 0);
}
BOOST_AUTO_TEST_CASE(GetTxSigOpCost_MultisigP2SH)
{
CMutableTransaction creationTx, spendingTx;
TestCoinsViewCache test_coins;
CPubKey pubkey{GenerateTestPubKey()};
CScript redeemScript = CScript() << 1 << ToByteVector(pubkey) << ToByteVector(pubkey) << 2 << OP_CHECKMULTISIGVERIFY;
CScript scriptPubKey = GetScriptForDestination(ScriptHash(redeemScript));
CScript scriptSig = CScript() << OP_0 << OP_0 << ToByteVector(redeemScript);
BuildTxs(spendingTx, test_coins.cache, creationTx, scriptPubKey, scriptSig, CScriptWitness());
BOOST_CHECK_EQUAL(GetTransactionSigOpCost(CTransaction(spendingTx), test_coins.cache, STANDARD_SCRIPT_VERIFY_FLAGS), 2 * WITNESS_SCALE_FACTOR);
BOOST_CHECK_EQUAL(VerifyWithFlag(CTransaction(creationTx), spendingTx, STANDARD_SCRIPT_VERIFY_FLAGS), SCRIPT_ERR_CHECKMULTISIGVERIFY);
// P2SH sigops are not counted if we don't set the SCRIPT_VERIFY_P2SH flag
BOOST_CHECK_EQUAL(GetTransactionSigOpCost(CTransaction(spendingTx), test_coins.cache, /*flags=*/0), 0);
// Coinbase tx does not trigger signature operations in the output script
BOOST_CHECK_EQUAL(GetTransactionSigOpCost(CTransaction(creationTx), test_coins.cache, STANDARD_SCRIPT_VERIFY_FLAGS), 0);
// Coinbase input sigops are not counted (P2SH included).
BOOST_CHECK_EQUAL(GetTransactionSigOpCost(MakeCoinBase(spendingTx), test_coins.cache, STANDARD_SCRIPT_VERIFY_FLAGS), 0);
}
BOOST_AUTO_TEST_CASE(GetTxSigOpCost_P2WPKH)
{
CMutableTransaction creationTx, spendingTx;
TestCoinsViewCache test_coins;
CPubKey pubkey{GenerateTestPubKey()};
CScript scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(pubkey));
CScript scriptSig = CScript();
CScriptWitness scriptWitness;
scriptWitness.stack.emplace_back(0);
scriptWitness.stack.emplace_back(0);
BuildTxs(spendingTx, test_coins.cache, creationTx, scriptPubKey, scriptSig, scriptWitness);
BOOST_CHECK_EQUAL(GetTransactionSigOpCost(CTransaction(spendingTx), test_coins.cache, STANDARD_SCRIPT_VERIFY_FLAGS), 1);
// No signature operations if we don't verify the witness.
BOOST_CHECK_EQUAL(GetTransactionSigOpCost(CTransaction(spendingTx), test_coins.cache, STANDARD_SCRIPT_VERIFY_FLAGS & ~SCRIPT_VERIFY_WITNESS), 0);
BOOST_CHECK_EQUAL(VerifyWithFlag(CTransaction(creationTx), spendingTx, STANDARD_SCRIPT_VERIFY_FLAGS), SCRIPT_ERR_EQUALVERIFY);
// The sig op cost for witness version != 0 is zero.
BOOST_CHECK_EQUAL(scriptPubKey[0], OP_0);
scriptPubKey[0] = 0x51;
BuildTxs(spendingTx, test_coins.cache, creationTx, scriptPubKey, scriptSig, scriptWitness);
BOOST_CHECK_EQUAL(GetTransactionSigOpCost(CTransaction(spendingTx), test_coins.cache, STANDARD_SCRIPT_VERIFY_FLAGS), 0);
scriptPubKey[0] = 0x00;
BuildTxs(spendingTx, test_coins.cache, creationTx, scriptPubKey, scriptSig, scriptWitness);
// Coinbase tx does not trigger witness program validation
BOOST_CHECK_EQUAL(GetTransactionSigOpCost(CTransaction(creationTx), test_coins.cache, STANDARD_SCRIPT_VERIFY_FLAGS), 0);
// The witness of a coinbase transaction is not taken into account.
BOOST_REQUIRE(!spendingTx.vin[0].scriptWitness.IsNull());
BOOST_CHECK_EQUAL(GetTransactionSigOpCost(MakeCoinBase(spendingTx), test_coins.cache, STANDARD_SCRIPT_VERIFY_FLAGS), 0);
}
BOOST_AUTO_TEST_CASE(GetTxSigOpCost_P2WPKH_P2SH)
{
CMutableTransaction creationTx, spendingTx;
TestCoinsViewCache test_coins;
CPubKey pubkey{GenerateTestPubKey()};
CScript scriptSig = GetScriptForDestination(WitnessV0KeyHash(pubkey));
CScript scriptPubKey = GetScriptForDestination(ScriptHash(scriptSig));
scriptSig = CScript() << ToByteVector(scriptSig);
CScriptWitness scriptWitness;
scriptWitness.stack.emplace_back(0);
scriptWitness.stack.emplace_back(0);
BuildTxs(spendingTx, test_coins.cache, creationTx, scriptPubKey, scriptSig, scriptWitness);
BOOST_CHECK_EQUAL(GetTransactionSigOpCost(CTransaction(spendingTx), test_coins.cache, STANDARD_SCRIPT_VERIFY_FLAGS), 1);
BOOST_CHECK_EQUAL(VerifyWithFlag(CTransaction(creationTx), spendingTx, STANDARD_SCRIPT_VERIFY_FLAGS), SCRIPT_ERR_EQUALVERIFY);
// Coinbase tx does not trigger P2SH witness program validation
BOOST_CHECK_EQUAL(GetTransactionSigOpCost(CTransaction(creationTx), test_coins.cache, STANDARD_SCRIPT_VERIFY_FLAGS), 0);
// The witness of a coinbase transaction is not taken into account.
BOOST_REQUIRE(!spendingTx.vin[0].scriptWitness.IsNull());
BOOST_CHECK_EQUAL(GetTransactionSigOpCost(MakeCoinBase(spendingTx), test_coins.cache, STANDARD_SCRIPT_VERIFY_FLAGS), 0);
}
BOOST_AUTO_TEST_CASE(GetTxSigOpCost_P2WSH)
{
CMutableTransaction creationTx, spendingTx;
TestCoinsViewCache test_coins;
CPubKey pubkey{GenerateTestPubKey()};
CScript witnessScript = CScript() << 1 << ToByteVector(pubkey) << ToByteVector(pubkey) << 2 << OP_CHECKMULTISIGVERIFY;
CScript scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessScript));
CScript scriptSig = CScript();
CScriptWitness scriptWitness;
scriptWitness.stack.emplace_back(0);
scriptWitness.stack.emplace_back(0);
scriptWitness.stack.emplace_back(witnessScript.begin(), witnessScript.end());
BuildTxs(spendingTx, test_coins.cache, creationTx, scriptPubKey, scriptSig, scriptWitness);
BOOST_CHECK_EQUAL(GetTransactionSigOpCost(CTransaction(spendingTx), test_coins.cache, STANDARD_SCRIPT_VERIFY_FLAGS), 2);
BOOST_CHECK_EQUAL(GetTransactionSigOpCost(CTransaction(spendingTx), test_coins.cache, STANDARD_SCRIPT_VERIFY_FLAGS & ~SCRIPT_VERIFY_WITNESS), 0);
BOOST_CHECK_EQUAL(VerifyWithFlag(CTransaction(creationTx), spendingTx, STANDARD_SCRIPT_VERIFY_FLAGS), SCRIPT_ERR_CHECKMULTISIGVERIFY);
// Coinbase tx does not trigger witness script validation
BOOST_CHECK_EQUAL(GetTransactionSigOpCost(CTransaction(creationTx), test_coins.cache, STANDARD_SCRIPT_VERIFY_FLAGS), 0);
// The witness of a coinbase transaction is not taken into account.
BOOST_REQUIRE(!spendingTx.vin[0].scriptWitness.IsNull());
BOOST_CHECK_EQUAL(GetTransactionSigOpCost(MakeCoinBase(spendingTx), test_coins.cache, STANDARD_SCRIPT_VERIFY_FLAGS), 0);
}
BOOST_AUTO_TEST_CASE(GetTxSigOpCost_P2WSH_P2SH)
{
CMutableTransaction creationTx, spendingTx;
TestCoinsViewCache test_coins;
CPubKey pubkey{GenerateTestPubKey()};
CScript witnessScript = CScript() << 1 << ToByteVector(pubkey) << ToByteVector(pubkey) << 2 << OP_CHECKMULTISIGVERIFY;
CScript redeemScript = GetScriptForDestination(WitnessV0ScriptHash(witnessScript));
CScript scriptPubKey = GetScriptForDestination(ScriptHash(redeemScript));
CScript scriptSig = CScript() << ToByteVector(redeemScript);
CScriptWitness scriptWitness;
scriptWitness.stack.emplace_back(0);
scriptWitness.stack.emplace_back(0);
scriptWitness.stack.emplace_back(witnessScript.begin(), witnessScript.end());
BuildTxs(spendingTx, test_coins.cache, creationTx, scriptPubKey, scriptSig, scriptWitness);
BOOST_CHECK_EQUAL(GetTransactionSigOpCost(CTransaction(spendingTx), test_coins.cache, STANDARD_SCRIPT_VERIFY_FLAGS), 2);
BOOST_CHECK_EQUAL(VerifyWithFlag(CTransaction(creationTx), spendingTx, STANDARD_SCRIPT_VERIFY_FLAGS), SCRIPT_ERR_CHECKMULTISIGVERIFY);
// Coinbase tx does not trigger P2SH witness script validation
BOOST_CHECK_EQUAL(GetTransactionSigOpCost(CTransaction(creationTx), test_coins.cache, STANDARD_SCRIPT_VERIFY_FLAGS), 0);
// The witness of a coinbase transaction is not taken into account.
BOOST_REQUIRE(!spendingTx.vin[0].scriptWitness.IsNull());
BOOST_CHECK_EQUAL(GetTransactionSigOpCost(MakeCoinBase(spendingTx), test_coins.cache, STANDARD_SCRIPT_VERIFY_FLAGS), 0);
}
BOOST_AUTO_TEST_SUITE_END()Note: during review I also found some dead code that I pushed separately in #33786
| @@ -154,6 +154,11 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost) | |||
|
|
|||
| // P2SH sigops are not counted if we don't set the SCRIPT_VERIFY_P2SH flag | |||
| assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, /*flags=*/0) == 0); | |||
|
|
|||
| // The witness of a coinbase transaction is not taken into account. | |||
| spendingTx.vin[0].prevout.SetNull(); | |||
There was a problem hiding this comment.
it seems to me scriptWitness is actually empty here, so the comment and code are a bit confusing here.
I think a BOOST_REQUIRE(!spendingTx.vin[0].scriptWitness.IsNull()); would document the requirements here better than a comment.
BOOST_CHECK_EQUAL(GetTransactionSigOpCost(CTransaction(creationTx), coins, flags), 0); already checks that coinbases don't have sigops
| // The witness of a coinbase transaction is not taken into account. | ||
| spendingTx.vin[0].prevout.SetNull(); | ||
| assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags) == 0); | ||
| assert(GetTransactionSigOpCost(CTransaction(creationTx), coins, flags) == 0); |
There was a problem hiding this comment.
this is already a coinbase tx, basically equivalent to the above, let's put this before mutating the spending tx, it's not related to it
| @@ -196,6 +201,11 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost) | |||
| BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, scriptWitness); | |||
| assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags) == 1); | |||
| assert(VerifyWithFlag(CTransaction(creationTx), spendingTx, flags) == SCRIPT_ERR_EQUALVERIFY); | |||
|
|
|||
| // The witness of a coinbase transaction is not taken into account. | |||
| spendingTx.vin[0].prevout.SetNull(); | |||
There was a problem hiding this comment.
After we make this a coinbase, it's confusing to refer to it as a "spending" transaction...
We could add another helper that copies it and makes it a coinbase
24bcad3 refactor: remove dead code in `CountWitnessSigOps` (Lőrinc) Pull request description: Found while reviewing #32840 The `nullptr` witness path was dead in normal code paths: replacing it with reference enables us deleting unreachable logic. Code coverage proof: https://maflcko.github.io/b-c-cov/total.coverage/src/script/interpreter.cpp.gcov.html#L2135 ACKs for top commit: kevkevinpal: ACK [24bcad3](24bcad3) maflcko: review ACK 24bcad3 🐏 darosior: Neat. utACK 24bcad3. stickies-v: ACK 24bcad3 Tree-SHA512: 92c87e431f06a15d8eeb02e20e9154b272c4586ddacf77c8d83783091485fb82c24ecbd711db7043a92cf6169746db24ad46a5904d694aea9d3c3aa96da725f0
Added assertions to the GetTxSigOpCost test cases to verify that the witness of a coinbase transaction is not considered in the signature operation cost calculations. Using spendingTx.vin[0].prevout.SetNull() we create a coinbase transaction that evaluates to true for IsCoinbase(). Doing this to transactions (spendingTx in this case) that evaluate to a non-zero sigop output, we more concretely test that the witness of a coinbase transaction is not taken into account for SigOp maths.
- Update copyright year to 2012-present - Add STANDARD_SCRIPT_VERIFY_FLAGS constant to reduce duplication - Add TestCoinsViewCache fixture for cleaner test setup - Add GenerateTestPubKey() helper function - Add MakeCoinBase() helper for creating coinbase transactions - Improve Serialize() function formatting These helpers will be used in subsequent test refactoring commits. Co-Authored-By: l0rinc <pap.lorinc@gmail.com>
- Remove unnecessary U suffix from integer literals - Update for-loop to use brace initialization - Remove redundant comment - Improve brace style consistency Co-Authored-By: l0rinc <pap.lorinc@gmail.com>
Split the monolithic GetTxSigOpCost test into 6 focused test cases: - GetTxSigOpCost_Multisig: Tests legacy multisig sig op counting - GetTxSigOpCost_MultisigP2SH: Tests multisig nested in P2SH - GetTxSigOpCost_P2WPKH: Tests P2WPKH witness programs - GetTxSigOpCost_P2WPKH_P2SH: Tests P2WPKH nested in P2SH - GetTxSigOpCost_P2WSH: Tests P2WSH witness programs - GetTxSigOpCost_P2WSH_P2SH: Tests P2WSH nested in P2SH Benefits: - Each test is self-contained and can be run independently - Failures in one test don't break other tests - Improved test isolation and readability - Uses TestCoinsViewCache fixture and helper functions - Better test organization and maintainability Also adds BOOST_REQUIRE assertions to BuildTxs to verify that creationTx is a coinbase and spendingTx is not. Co-Authored-By: l0rinc <pap.lorinc@gmail.com>
Replace all assert() calls with BOOST_CHECK_EQUAL() to provide better error messages when tests fail. BOOST_CHECK_EQUAL() shows the actual and expected values on failure, making it easier to debug test failures. This improves test diagnostics without changing test behavior. Co-Authored-By: l0rinc <pap.lorinc@gmail.com>
380f4c3 to
81f08dd
Compare
|
Thanks @average-gary. The first commit still adds a test that claims we're testing the witness when in fact it's empty: I would make the test split the first commit, containing the smallest change that can allow us to untangle them, with the minimum helpers since we're already uindenting all lines, we shouldn't do anything else that isn't strictly necessary for the separation. Let me know if there's any way I can help. |
|
Apologies for the delay. I am not confident I'll be circling back to this PR any time soon, feel free to close it. |
Added assertions to the GetTxSigOpCost test cases to verify that the witness of a coinbase transaction is not considered in the signature operation cost calculations.
Using spendingTx.vin[0].prevout.SetNull() we create a coinbase transaction that evaluates to true for IsCoinbase(). Doing this to transactions (spendingTx in this case) that evaluate to a non-zero sigop output, we more concretely test that the witness of a coinbase transaction is not taken into account for SigOp maths.
In my experimentation in mining software (mostly Stratum v2) I encountered the SigOps budget and began exploring the considerations as it applies to coinbase transactions. It was unclear how commitment-type addresses for coinbase were handled compared to bare script when it came to SigOp calculation. Upon further investigation, I saw that the test suite could have added vectors that clearly demonstrate that the witness for a coinbase transaction is not considered for
GetTransactionSigOpCost.Adding these tests makes it more clear for someone in the future how SigOp maths work while exploring the intersection of SigOps and coinbase transactions.