Skip to content

Commit d90308b

Browse files
committed
Add code to force coverage
The RandomOrphan function and the function ecdsa_signature_parse_der_lax in pubkey.cpp were causing non-deterministic test coverage. In the former, if a random orphan was selected the index of which is bigger than the max. orphan index in mapOrphanTransactions, the last orphan was returned from RandomOrphan. If the random number generated was never large enough, this condition would not be fulfilled and the corresponding branch wouldn't run. The proposed solution is to force one of the 50 dependant orphans to depend on the last orphan in mapOrphanTransactions using the newly introduced function OrphanByIndex (and passing it a large uint256), forcing this branch to always run at least once. In the latter, if values for ECDSA R or S (or both) had no leading zeros, some code would not be executed. The solution was to find a constant value for the public key and a corresponding signature that would be comprised of R and S values with leading zeros and calling CPubKey::Verify at the end of the test forcing this code to always run at least once at the end even if it hadn't throughout the test. Removed denialofservice_tests test entry from the list of non-deterministic tests in the coverage script.
1 parent 195822f commit d90308b

File tree

2 files changed

+40
-4
lines changed

2 files changed

+40
-4
lines changed

contrib/devtools/test_deterministic_coverage.sh

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ GCOV_EXECUTABLE="gcov"
1616
NON_DETERMINISTIC_TESTS=(
1717
"blockfilter_index_tests/blockfilter_index_initial_sync" # src/checkqueue.h: In CCheckQueue::Loop(): while (queue.empty()) { ... }
1818
"coinselector_tests/knapsack_solver_test" # coinselector_tests.cpp: if (equal_sets(setCoinsRet, setCoinsRet2))
19-
"denialofservice_tests/DoS_mapOrphans" # denialofservice_tests.cpp: it = mapOrphanTransactions.lower_bound(InsecureRand256());
2019
"fs_tests/fsbridge_fstream" # deterministic test failure?
2120
"miner_tests/CreateNewBlock_validity" # validation.cpp: if (GetMainSignals().CallbacksPending() > 10)
2221
"scheduler_tests/manythreads" # scheduler.cpp: CScheduler::serviceQueue()

src/test/denialofservice_tests.cpp

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44

55
// Unit tests for denial-of-service detection/prevention code
66

7+
#include <arith_uint256.h>
78
#include <banman.h>
89
#include <chainparams.h>
910
#include <net.h>
1011
#include <net_processing.h>
12+
#include <pubkey.h>
1113
#include <script/sign.h>
1214
#include <script/signingprovider.h>
1315
#include <script/standard.h>
@@ -356,16 +358,42 @@ BOOST_AUTO_TEST_CASE(DoS_bantime)
356358
peerLogic->FinalizeNode(dummyNode.GetId(), dummy);
357359
}
358360

359-
static CTransactionRef RandomOrphan()
361+
static CTransactionRef OrphanByIndex(const uint256& orphan_index)
360362
{
361363
std::map<uint256, COrphanTx>::iterator it;
362364
LOCK2(cs_main, g_cs_orphans);
363-
it = mapOrphanTransactions.lower_bound(InsecureRand256());
365+
it = mapOrphanTransactions.lower_bound(orphan_index);
364366
if (it == mapOrphanTransactions.end())
365367
it = mapOrphanTransactions.begin();
366368
return it->second.tx;
367369
}
368370

371+
static CTransactionRef RandomOrphan()
372+
{
373+
return OrphanByIndex(InsecureRand256());
374+
}
375+
376+
/** This function runs CPubkey::Verify with parameters that result in all
377+
* branches of the function ecdsa_signature_parse_der_lax to run. Namely, the
378+
* signature literal in vch_sig_str in the following function is comprised of R
379+
* and S values which both contain leading zeroes, forcing some branches of said
380+
* function to run which would otherwise not. This function is called at the end
381+
* of the DoS_mapOrphans test to force deterministic coverage.
382+
*/
383+
static void ForceCoverageInPubKeyVerify()
384+
{
385+
std::string coverage_pubkey_str("\x03\x1c\xef\xd3\xfa\x1e\x91\xd2\x24\x7e\x9d\x74\xa1\xf4\x31\x27\x6e\xe4\x74"
386+
"\x5b\xaf\x73\x1d\xb0\x0c\x07\xa0\x78\x81\xa5\xca\x4c\xc9", 33);
387+
CPubKey coverage_pubkey(coverage_pubkey_str.begin(), coverage_pubkey_str.end());
388+
std::string vch_sig_str("\x30\x44\x02\x20\x00\xa8\x9f\x92\xf4\x47\x6e\x3f\x0b\x1f\x58\x9f\x6b\x3b\xb9\xae\xc0"
389+
"\x99\x84\x82\x22\x40\x68\xf5\x12\xf7\x43\xbe\xa7\x54\x87\x7c\x02\x20\x00\xa1\x09\x98\x60\x32\x32\x71\x34"
390+
"\xad\xf6\x6a\x9c\x2f\xd9\xd4\xf3\xcc\xf4\xc8\x4c\x38\xbb\xd0\xac\xde\xa7\x3d\x66\x28\xa0\xe0", 70);
391+
uint256 hash;
392+
hash.SetHex(std::string("6af516422fef8a745aff6acdcc84076c77fb2ecd72bd5711df301230ac58fdd5"));
393+
394+
coverage_pubkey.Verify(hash, std::vector<unsigned char>(vch_sig_str.begin(), vch_sig_str.end()));
395+
}
396+
369397
BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
370398
{
371399
CKey key;
@@ -391,7 +419,14 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
391419
// ... and 50 that depend on other orphans:
392420
for (int i = 0; i < 50; i++)
393421
{
394-
CTransactionRef txPrev = RandomOrphan();
422+
CTransactionRef txPrev;
423+
if (i == 0) {
424+
// This block makes sure that the condition "if (it == mapOrphanTransactions.end())" in OrphanByIndex() gets called at least once.
425+
// Otherwise test coverage is non-deterministic.
426+
txPrev = OrphanByIndex(ArithToUint256(arith_uint256(std::numeric_limits<uint64_t>::max())));
427+
} else {
428+
txPrev = RandomOrphan();
429+
}
395430

396431
CMutableTransaction tx;
397432
tx.vin.resize(1);
@@ -445,6 +480,8 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
445480
BOOST_CHECK(mapOrphanTransactions.size() <= 10);
446481
LimitOrphanTxSize(0);
447482
BOOST_CHECK(mapOrphanTransactions.empty());
483+
484+
ForceCoverageInPubKeyVerify();
448485
}
449486

450487
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)