From 5ac35795206d252c9f464e967b84521ddaad38f1 Mon Sep 17 00:00:00 2001 From: rustaceanrob Date: Fri, 12 Dec 2025 15:58:40 +0000 Subject: [PATCH] refactor: Add compile-time-checked hex txid Suggested by @l0rinc in #34004 Message by @l0rinc: This adds a consteval constructor to transaction_identifier (Txid/Wtxid) to allow parsing hex strings at compile-time. This replaces runtime FromHex checks in tests, ensuring that malformed hardcoded hashes cause build failures rather than runtime test failures. Test variables are explicitly marked constexpr. This is required to workaround a regression in GCC 14 (Bug 117501) where the compiler incorrectly flags consteval initialization of non-constexpr variables as "statements with no effect". GCC Bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=117501 Reproducer: https://godbolt.org/z/xb5TMaPs6 Co-authored-by: l0rinc --- src/primitives/transaction_identifier.h | 1 + src/test/bloom_tests.cpp | 16 ++++++++-------- src/test/merkleblock_tests.cpp | 6 +++--- src/test/transaction_tests.cpp | 2 +- src/test/txpackage_tests.cpp | 12 ++++++------ 5 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/primitives/transaction_identifier.h b/src/primitives/transaction_identifier.h index 79af2ddeb63..921f82c6001 100644 --- a/src/primitives/transaction_identifier.h +++ b/src/primitives/transaction_identifier.h @@ -34,6 +34,7 @@ class transaction_identifier public: transaction_identifier() : m_wrapped{} {} + consteval explicit transaction_identifier(std::string_view hex_str) : m_wrapped{uint256{hex_str}} {} template bool operator==(const Other& other) const { return Compare(other) == 0; } diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp index 584c970e196..7e1a7d0677c 100644 --- a/src/test/bloom_tests.cpp +++ b/src/test/bloom_tests.cpp @@ -140,11 +140,11 @@ BOOST_AUTO_TEST_CASE(bloom_match) BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx), "Simple Bloom filter didn't match output address"); filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL); - filter.insert(COutPoint(Txid::FromHex("90c122d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b").value(), 0)); + filter.insert(COutPoint{Txid{"90c122d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b"}, 0}); BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx), "Simple Bloom filter didn't match COutPoint"); filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL); - COutPoint prevOutPoint(Txid::FromHex("90c122d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b").value(), 0); + COutPoint prevOutPoint{Txid{"90c122d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b"}, 0}; { std::vector data(32 + sizeof(unsigned int)); memcpy(data.data(), prevOutPoint.hash.begin(), 32); @@ -162,11 +162,11 @@ BOOST_AUTO_TEST_CASE(bloom_match) BOOST_CHECK_MESSAGE(!filter.IsRelevantAndUpdate(tx), "Simple Bloom filter matched random address"); filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL); - filter.insert(COutPoint(Txid::FromHex("90c122d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b").value(), 1)); + filter.insert(COutPoint{Txid{"90c122d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b"}, 1}); BOOST_CHECK_MESSAGE(!filter.IsRelevantAndUpdate(tx), "Simple Bloom filter matched COutPoint for an output we didn't care about"); filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL); - filter.insert(COutPoint(Txid::FromHex("000000d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b").value(), 0)); + filter.insert(COutPoint{Txid{"000000d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b"}, 0}); BOOST_CHECK_MESSAGE(!filter.IsRelevantAndUpdate(tx), "Simple Bloom filter matched COutPoint for an output we didn't care about"); } @@ -426,9 +426,9 @@ BOOST_AUTO_TEST_CASE(merkle_block_4_test_p2pubkey_only) BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash()); // We should match the generation outpoint - BOOST_CHECK(filter.contains(COutPoint(Txid::FromHex("147caa76786596590baa4e98f5d9f48b86c7765e489f7a6ff3360fe5c674360b").value(), 0))); + BOOST_CHECK(filter.contains(COutPoint{Txid{"147caa76786596590baa4e98f5d9f48b86c7765e489f7a6ff3360fe5c674360b"}, 0})); // ... but not the 4th transaction's output (its not pay-2-pubkey) - BOOST_CHECK(!filter.contains(COutPoint(Txid::FromHex("02981fa052f0481dbc5868f4fc2166035a10f27a03cfd2de67326471df5bc041").value(), 0))); + BOOST_CHECK(!filter.contains(COutPoint{Txid{"02981fa052f0481dbc5868f4fc2166035a10f27a03cfd2de67326471df5bc041"}, 0})); } BOOST_AUTO_TEST_CASE(merkle_block_4_test_update_none) @@ -451,8 +451,8 @@ BOOST_AUTO_TEST_CASE(merkle_block_4_test_update_none) BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash()); // We shouldn't match any outpoints (UPDATE_NONE) - BOOST_CHECK(!filter.contains(COutPoint(Txid::FromHex("147caa76786596590baa4e98f5d9f48b86c7765e489f7a6ff3360fe5c674360b").value(), 0))); - BOOST_CHECK(!filter.contains(COutPoint(Txid::FromHex("02981fa052f0481dbc5868f4fc2166035a10f27a03cfd2de67326471df5bc041").value(), 0))); + BOOST_CHECK(!filter.contains(COutPoint{Txid{"147caa76786596590baa4e98f5d9f48b86c7765e489f7a6ff3360fe5c674360b"}, 0})); + BOOST_CHECK(!filter.contains(COutPoint{Txid{"02981fa052f0481dbc5868f4fc2166035a10f27a03cfd2de67326471df5bc041"}, 0})); } std::vector BloomTest::RandomData() diff --git a/src/test/merkleblock_tests.cpp b/src/test/merkleblock_tests.cpp index 48a61a3fc0d..c1cd5910eb7 100644 --- a/src/test/merkleblock_tests.cpp +++ b/src/test/merkleblock_tests.cpp @@ -24,10 +24,10 @@ BOOST_AUTO_TEST_CASE(merkleblock_construct_from_txids_found) std::set txids; // Last txn in block. - Txid txhash1{Txid::FromHex("74d681e0e03bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20").value()}; + constexpr Txid txhash1{"74d681e0e03bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20"}; // Second txn in block. - Txid txhash2{Txid::FromHex("f9fc751cb7dc372406a9f8d738d5e6f8f63bab71986a39cf36ee70ee17036d07").value()}; + constexpr Txid txhash2{"f9fc751cb7dc372406a9f8d738d5e6f8f63bab71986a39cf36ee70ee17036d07"}; txids.insert(txhash1); txids.insert(txhash2); @@ -63,7 +63,7 @@ BOOST_AUTO_TEST_CASE(merkleblock_construct_from_txids_not_found) CBlock block = getBlock13b8a(); std::set txids2; - txids2.insert(Txid::FromHex("c0ffee00003bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20").value()); + txids2.insert(Txid{"c0ffee00003bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20"}); CMerkleBlock merkleBlock(block, txids2); BOOST_CHECK_EQUAL(merkleBlock.header.GetHash().GetHex(), block.GetHash().GetHex()); diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 0ab9fbb4799..6274e368497 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -509,7 +509,7 @@ BOOST_AUTO_TEST_CASE(test_big_witness_transaction) // create a big transaction of 4500 inputs signed by the same key for(uint32_t ij = 0; ij < 4500; ij++) { uint32_t i = mtx.vin.size(); - COutPoint outpoint(Txid::FromHex("0000000000000000000000000000000000000000000000000000000000000100").value(), i); + COutPoint outpoint{Txid{"0000000000000000000000000000000000000000000000000000000000000100"}, i}; mtx.vin.resize(mtx.vin.size() + 1); mtx.vin[i].prevout = outpoint; diff --git a/src/test/txpackage_tests.cpp b/src/test/txpackage_tests.cpp index d82bd93c6d5..e8b7f0d3b0f 100644 --- a/src/test/txpackage_tests.cpp +++ b/src/test/txpackage_tests.cpp @@ -73,9 +73,9 @@ BOOST_AUTO_TEST_CASE(package_hash_tests) CTransactionRef ptx_3{MakeTransactionRef(tx_3)}; // It's easy to see that wtxids are sorted in lexicographical order: - Wtxid wtxid_1{Wtxid::FromHex("85cd1a31eb38f74ed5742ec9cb546712ab5aaf747de28a9168b53e846cbda17f").value()}; - Wtxid wtxid_2{Wtxid::FromHex("b4749f017444b051c44dfd2720e88f314ff94f3dd6d56d40ef65854fcd7fff6b").value()}; - Wtxid wtxid_3{Wtxid::FromHex("e065bac15f62bb4e761d761db928ddee65a47296b2b776785abb912cdec474e3").value()}; + constexpr Wtxid wtxid_1{"85cd1a31eb38f74ed5742ec9cb546712ab5aaf747de28a9168b53e846cbda17f"}; + constexpr Wtxid wtxid_2{"b4749f017444b051c44dfd2720e88f314ff94f3dd6d56d40ef65854fcd7fff6b"}; + constexpr Wtxid wtxid_3{"e065bac15f62bb4e761d761db928ddee65a47296b2b776785abb912cdec474e3"}; BOOST_CHECK_EQUAL(tx_1.GetWitnessHash(), wtxid_1); BOOST_CHECK_EQUAL(tx_2.GetWitnessHash(), wtxid_2); BOOST_CHECK_EQUAL(tx_3.GetWitnessHash(), wtxid_3); @@ -84,9 +84,9 @@ BOOST_AUTO_TEST_CASE(package_hash_tests) BOOST_CHECK(wtxid_2.GetHex() < wtxid_3.GetHex()); // The txids are not (we want to test that sorting and hashing use wtxid, not txid): - Txid txid_1{Txid::FromHex("bd0f71c1d5e50589063e134fad22053cdae5ab2320db5bf5e540198b0b5a4e69").value()}; - Txid txid_2{Txid::FromHex("b4749f017444b051c44dfd2720e88f314ff94f3dd6d56d40ef65854fcd7fff6b").value()}; - Txid txid_3{Txid::FromHex("ee707be5201160e32c4fc715bec227d1aeea5940fb4295605e7373edce3b1a93").value()}; + constexpr Txid txid_1{"bd0f71c1d5e50589063e134fad22053cdae5ab2320db5bf5e540198b0b5a4e69"}; + constexpr Txid txid_2{"b4749f017444b051c44dfd2720e88f314ff94f3dd6d56d40ef65854fcd7fff6b"}; + constexpr Txid txid_3{"ee707be5201160e32c4fc715bec227d1aeea5940fb4295605e7373edce3b1a93"}; BOOST_CHECK_EQUAL(tx_1.GetHash(), txid_1); BOOST_CHECK_EQUAL(tx_2.GetHash(), txid_2); BOOST_CHECK_EQUAL(tx_3.GetHash(), txid_3);