test: assert CScript allocation characteristics

Verifies that script types are correctly allocated using prevector's direct or indirect storage based on their size:

Direct allocated script types (size ≤ 28 bytes):
* OP_RETURN (small)
* P2WPKH
* P2SH
* P2PKH

Indirect allocated script types (size > 28 bytes):
* P2WSH
* P2TR
* P2PK
* MULTISIG (small)

This test provides a baseline for verifying changes to prevector's inline capacity.

The `CHECK_SCRIPT_STATIC_SIZE` and `CHECK_SCRIPT_DYNAMIC_SIZE` macros were added to differentiate the two cases - while preserving the correct source code line in case of failure.
This commit is contained in:
Lőrinc
2025-04-15 12:31:20 +02:00
parent 65ac7f6d4d
commit 52121506b2

View File

@@ -1151,6 +1151,107 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23)
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_INVALID_STACK_OPERATION, ScriptErrorString(err));
}
/** Return the TxoutType of a script without exposing Solver details. */
static TxoutType GetTxoutType(const CScript& output_script)
{
std::vector<std::vector<uint8_t>> unused;
return Solver(output_script, unused);
}
#define CHECK_SCRIPT_STATIC_SIZE(script, expected_size) \
do { \
BOOST_CHECK_EQUAL((script).size(), (expected_size)); \
BOOST_CHECK_EQUAL((script).capacity(), CScriptBase::STATIC_SIZE); \
BOOST_CHECK_EQUAL((script).allocated_memory(), 0); \
} while (0)
#define CHECK_SCRIPT_DYNAMIC_SIZE(script, expected_size, expected_extra) \
do { \
BOOST_CHECK_EQUAL((script).size(), (expected_size)); \
BOOST_CHECK_EQUAL((script).capacity(), (expected_extra)); \
BOOST_CHECK_EQUAL((script).allocated_memory(), (expected_extra)); \
} while (0)
BOOST_AUTO_TEST_CASE(script_size_and_capacity_test)
{
BOOST_CHECK_EQUAL(sizeof(CompressedScript), 40);
BOOST_CHECK_EQUAL(sizeof(CScriptBase), 32);
BOOST_CHECK_NE(sizeof(CScriptBase), sizeof(prevector<CScriptBase::STATIC_SIZE + 1, uint8_t>)); // CScriptBase size should be set to avoid wasting space in padding
BOOST_CHECK_EQUAL(sizeof(CScript), 32);
BOOST_CHECK_EQUAL(sizeof(CTxOut), 40);
CKey dummy_key;
dummy_key.MakeNewKey(/*fCompressed=*/true);
const CPubKey dummy_pubkey{dummy_key.GetPubKey()};
// Small OP_RETURN has direct allocation
{
const auto script{CScript() << OP_RETURN << std::vector<uint8_t>(10, 0xaa)};
BOOST_CHECK_EQUAL(GetTxoutType(script), TxoutType::NULL_DATA);
CHECK_SCRIPT_STATIC_SIZE(script, 12);
}
// P2WPKH has direct allocation
{
const auto script{GetScriptForDestination(WitnessV0KeyHash{PKHash{dummy_pubkey}})};
BOOST_CHECK_EQUAL(GetTxoutType(script), TxoutType::WITNESS_V0_KEYHASH);
CHECK_SCRIPT_STATIC_SIZE(script, 22);
}
// P2SH has direct allocation
{
const auto script{GetScriptForDestination(ScriptHash{CScript{} << OP_TRUE})};
BOOST_CHECK(script.IsPayToScriptHash());
CHECK_SCRIPT_STATIC_SIZE(script, 23);
}
// P2PKH has direct allocation
{
const auto script{GetScriptForDestination(PKHash{dummy_pubkey})};
BOOST_CHECK_EQUAL(GetTxoutType(script), TxoutType::PUBKEYHASH);
CHECK_SCRIPT_STATIC_SIZE(script, 25);
}
// P2WSH needs extra allocation
{
const auto script{GetScriptForDestination(WitnessV0ScriptHash{CScript{} << OP_TRUE})};
BOOST_CHECK(script.IsPayToWitnessScriptHash());
CHECK_SCRIPT_DYNAMIC_SIZE(script, 34, 34);
}
// P2TR needs extra allocation
{
const auto script{GetScriptForDestination(WitnessV1Taproot{XOnlyPubKey{dummy_pubkey}})};
BOOST_CHECK_EQUAL(GetTxoutType(script), TxoutType::WITNESS_V1_TAPROOT);
CHECK_SCRIPT_DYNAMIC_SIZE(script, 34, 34);
}
// Compressed P2PK needs extra allocation
{
const auto script{GetScriptForRawPubKey(dummy_pubkey)};
BOOST_CHECK_EQUAL(GetTxoutType(script), TxoutType::PUBKEY);
CHECK_SCRIPT_DYNAMIC_SIZE(script, 35, 35);
}
// Uncompressed P2PK needs extra allocation
{
CKey uncompressed_key;
uncompressed_key.MakeNewKey(/*fCompressed=*/false);
const CPubKey uncompressed_pubkey{uncompressed_key.GetPubKey()};
const auto script{GetScriptForRawPubKey(uncompressed_pubkey)};
BOOST_CHECK_EQUAL(GetTxoutType(script), TxoutType::PUBKEY);
CHECK_SCRIPT_DYNAMIC_SIZE(script, 67, 67);
}
// Bare multisig needs extra allocation
{
const auto script{GetScriptForMultisig(1, std::vector{2, dummy_pubkey})};
BOOST_CHECK_EQUAL(GetTxoutType(script), TxoutType::MULTISIG);
CHECK_SCRIPT_DYNAMIC_SIZE(script, 71, 103);
}
}
/* Wrapper around ProduceSignature to combine two scriptsigs */
SignatureData CombineSignatures(const CTxOut& txout, const CMutableTransaction& tx, const SignatureData& scriptSig1, const SignatureData& scriptSig2)
{