refactor: Add compile-time-checked hex txid
What changed, and why it matters
This is a code cleanup change that lets Bitcoin Core tests create transaction IDs from hex strings at compile time instead of runtime. It does not change how the live network or wallet behaves, and it does not fix a security bug. It only makes test code stricter so that bad hardcoded hashes fail during compilation rather than when tests run.
No security action needed. Treat as a normal test/development refactor.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit adds a consteval constructor to transaction_identifier so Txid and Wtxid can be initialized from string literals at compile time. It then updates several test files to use Txid{“…”} / Wtxid{“…”} instead of Txid::FromHex(“…”).value(). Variables that need compile-time evaluation are marked constexpr to work around GCC 14 bug 117501. The change is purely a refactor in test code and the transaction identifier header; no consensus, networking, or wallet logic is modified.
Changed components
src/primitives/transaction_identifier.hsrc/test/bloom_tests.cppsrc/test/merkleblock_tests.cppsrc/test/transaction_tests.cppsrc/test/txpackage_tests.cppInspect captured patch +19 / −18
diff --git a/src/primitives/transaction_identifier.h b/src/primitives/transaction_identifier.h
index 79af2dde..921f82c6 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 <typename Other>
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 584c970e..7e1a7d06 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<unsigned char> 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<unsigned char> BloomTest::RandomData()
diff --git a/src/test/merkleblock_tests.cpp b/src/test/merkleblock_tests.cpp
index 48a61a3f..c1cd5910 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<Txid> 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<Txid> 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 0ab9fbb4..6274e368 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 d82bd93c..e8b7f0d3 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);
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.