diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 1ba555e05b3..3fc490e6374 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -43,6 +43,7 @@ #include #include +#include #include #include #include @@ -982,4 +983,31 @@ BOOST_AUTO_TEST_CASE(block_template_manager) BOOST_CHECK(block_template->vTxSigOpsCost.empty()); } +// Reproduction (AR-31): submitSolution rewrites the stored block while +// getBlock reads it from another thread. Run under ThreadSanitizer. +BOOST_AUTO_TEST_CASE(repro_submitsolution_concurrent_getblock) +{ + auto mining{MakeMining()}; + BlockCreateOptions options{.coinbase_output_script = CScript() << OP_TRUE}; + std::unique_ptr block_template{mining->createNewBlock(options, /*cooldown=*/false)}; + BOOST_REQUIRE(block_template); + constexpr int ITERATIONS{200}; + std::thread reader([&] { + for (int i = 0; i < ITERATIONS; ++i) { + CBlock block{block_template->getBlock()}; + BOOST_CHECK(!block.vtx.empty()); + } + }); + for (int i = 0; i < ITERATIONS; ++i) { + CMutableTransaction coinbase; + coinbase.vin.resize(1); + coinbase.vin[0].scriptSig = CScript() << i << OP_0; + coinbase.vout.resize(1); + coinbase.vout[0].scriptPubKey = CScript() << OP_TRUE; + std::string reason, debug; + (void)block_template->submitSolution(/*version=*/1, /*timestamp=*/0, /*nonce=*/i, MakeTransactionRef(std::move(coinbase)), reason, debug); + } + reader.join(); +} + BOOST_AUTO_TEST_SUITE_END()