aboutsummaryrefslogtreecommitdiff
path: root/src/test
diff options
context:
space:
mode:
Diffstat (limited to 'src/test')
-rw-r--r--src/test/README.md50
-rw-r--r--src/test/argsman_tests.cpp4
-rw-r--r--src/test/blockencodings_tests.cpp10
-rw-r--r--src/test/bswap_tests.cpp6
-rw-r--r--src/test/coinstatsindex_tests.cpp2
-rw-r--r--src/test/common_url_tests.cpp75
-rw-r--r--src/test/compress_tests.cpp33
-rw-r--r--src/test/crypto_tests.cpp22
-rw-r--r--src/test/feefrac_tests.cpp85
-rw-r--r--src/test/fuzz/addition_overflow.cpp2
-rw-r--r--src/test/fuzz/deserialize.cpp1
-rw-r--r--src/test/fuzz/feefrac.cpp123
-rw-r--r--src/test/fuzz/feeratediagram.cpp133
-rw-r--r--src/test/fuzz/fuzz.cpp11
-rw-r--r--src/test/fuzz/headerssync.cpp2
-rw-r--r--src/test/fuzz/integer.cpp1
-rw-r--r--src/test/fuzz/multiplication_overflow.cpp6
-rw-r--r--src/test/fuzz/net.cpp37
-rw-r--r--src/test/fuzz/net_permissions.cpp4
-rw-r--r--src/test/fuzz/p2p_transport_serialization.cpp1
-rw-r--r--src/test/fuzz/package_eval.cpp20
-rw-r--r--src/test/fuzz/partially_downloaded_block.cpp4
-rw-r--r--src/test/fuzz/process_message.cpp4
-rw-r--r--src/test/fuzz/process_messages.cpp4
-rw-r--r--src/test/fuzz/rbf.cpp141
-rw-r--r--src/test/fuzz/script_bitcoin_consensus.cpp50
-rw-r--r--src/test/fuzz/string.cpp2
-rw-r--r--src/test/fuzz/tx_pool.cpp12
-rw-r--r--src/test/fuzz/util.cpp2
-rw-r--r--src/test/fuzz/util/descriptor.cpp2
-rw-r--r--src/test/i2p_tests.cpp21
-rw-r--r--src/test/main.cpp7
-rw-r--r--src/test/miniscript_tests.cpp1
-rw-r--r--src/test/netbase_tests.cpp18
-rw-r--r--src/test/peerman_tests.cpp4
-rw-r--r--src/test/policyestimator_tests.cpp20
-rw-r--r--src/test/rbf_tests.cpp476
-rw-r--r--src/test/rpc_tests.cpp1
-rw-r--r--src/test/sanity_tests.cpp2
-rw-r--r--src/test/scheduler_tests.cpp10
-rw-r--r--src/test/script_tests.cpp252
-rw-r--r--src/test/serfloat_tests.cpp116
-rw-r--r--src/test/system_tests.cpp14
-rw-r--r--src/test/txindex_tests.cpp2
-rw-r--r--src/test/txpackage_tests.cpp36
-rw-r--r--src/test/txvalidation_tests.cpp90
-rw-r--r--src/test/util/chainstate.h17
-rw-r--r--src/test/util/mining.cpp6
-rw-r--r--src/test/util/setup_common.cpp72
-rw-r--r--src/test/util/setup_common.h7
-rw-r--r--src/test/util/txmempool.cpp1
-rw-r--r--src/test/util_tests.cpp5
-rw-r--r--src/test/validation_block_tests.cpp9
-rw-r--r--src/test/validation_chainstatemanager_tests.cpp26
-rw-r--r--src/test/validationinterface_tests.cpp31
55 files changed, 1521 insertions, 572 deletions
diff --git a/src/test/README.md b/src/test/README.md
index 0876db7a72..bab1a28f61 100644
--- a/src/test/README.md
+++ b/src/test/README.md
@@ -42,17 +42,18 @@ test_bitcoin --log_level=all --run_test=getarg_tests
```
`log_level` controls the verbosity of the test framework, which logs when a
-test case is entered, for example. `test_bitcoin` also accepts the command
-line arguments accepted by `bitcoind`. Use `--` to separate both types of
-arguments:
+test case is entered, for example.
+
+`test_bitcoin` also accepts some of the command line arguments accepted by
+`bitcoind`. Use `--` to separate these sets of arguments:
```bash
test_bitcoin --log_level=all --run_test=getarg_tests -- -printtoconsole=1
```
-The `-printtoconsole=1` after the two dashes redirects the debug log, which
-would normally go to a file in the test datadir
-(`BasicTestingSetup::m_path_root`), to the standard terminal output.
+The `-printtoconsole=1` after the two dashes sends debug logging, which
+normally goes only to `debug.log` within the data directory, also to the
+standard terminal output.
... or to run just the doubledash test:
@@ -60,7 +61,42 @@ would normally go to a file in the test datadir
test_bitcoin --run_test=getarg_tests/doubledash
```
-Run `test_bitcoin --help` for the full list.
+`test_bitcoin` creates a temporary working (data) directory with a randomly
+generated pathname within `test_common_Bitcoin Core/`, which in turn is within
+the system's temporary directory (see
+[`temp_directory_path`](https://en.cppreference.com/w/cpp/filesystem/temp_directory_path)).
+This data directory looks like a simplified form of the standard `bitcoind` data
+directory. Its content will vary depending on the test, but it will always
+have a `debug.log` file, for example.
+
+The location of the temporary data directory can be specified with the
+`-testdatadir` option. This can make debugging easier. The directory
+path used is the argument path appended with
+`/test_common_Bitcoin Core/<test-name>/datadir`.
+The directory path is created if necessary.
+Specifying this argument also causes the data directory
+not to be removed after the last test. This is useful for looking at
+what the test wrote to `debug.log` after it completes, for example.
+(The directory is removed at the start of the next test run,
+so no leftover state is used.)
+
+```bash
+$ test_bitcoin --run_test=getarg_tests/doubledash -- -testdatadir=/somewhere/mydatadir
+Test directory (will not be deleted): "/somewhere/mydatadir/test_common_Bitcoin Core/getarg_tests/doubledash/datadir
+Running 1 test case...
+
+*** No errors detected
+$ ls -l '/somewhere/mydatadir/test_common_Bitcoin Core/getarg_tests/doubledash/datadir'
+total 8
+drwxrwxr-x 2 admin admin 4096 Nov 27 22:45 blocks
+-rw-rw-r-- 1 admin admin 1003 Nov 27 22:45 debug.log
+```
+
+If you run an entire test suite, such as `--run_test=getarg_tests`, or all the test suites
+(by not specifying `--run_test`), a separate directory
+will be created for each individual test.
+
+Run `test_bitcoin --help` for the full list of tests.
### Adding test cases
diff --git a/src/test/argsman_tests.cpp b/src/test/argsman_tests.cpp
index 1f46efe464..340208a1c9 100644
--- a/src/test/argsman_tests.cpp
+++ b/src/test/argsman_tests.cpp
@@ -904,7 +904,7 @@ BOOST_FIXTURE_TEST_CASE(util_ArgsMerge, ArgsMergeTestingSetup)
// If check below fails, should manually dump the results with:
//
- // ARGS_MERGE_TEST_OUT=results.txt ./test_bitcoin --run_test=util_tests/util_ArgsMerge
+ // ARGS_MERGE_TEST_OUT=results.txt ./test_bitcoin --run_test=argsman_tests/util_ArgsMerge
//
// And verify diff against previous results to make sure the changes are expected.
//
@@ -1007,7 +1007,7 @@ BOOST_FIXTURE_TEST_CASE(util_ChainMerge, ChainMergeTestingSetup)
// If check below fails, should manually dump the results with:
//
- // CHAIN_MERGE_TEST_OUT=results.txt ./test_bitcoin --run_test=util_tests/util_ChainMerge
+ // CHAIN_MERGE_TEST_OUT=results.txt ./test_bitcoin --run_test=argsman_tests/util_ChainMerge
//
// And verify diff against previous results to make sure the changes are expected.
//
diff --git a/src/test/blockencodings_tests.cpp b/src/test/blockencodings_tests.cpp
index 763f0f897e..05355fb21d 100644
--- a/src/test/blockencodings_tests.cpp
+++ b/src/test/blockencodings_tests.cpp
@@ -14,7 +14,7 @@
#include <boost/test/unit_test.hpp>
-std::vector<std::pair<uint256, CTransactionRef>> extra_txn;
+std::vector<CTransactionRef> extra_txn;
BOOST_FIXTURE_TEST_SUITE(blockencodings_tests, RegTestingSetup)
@@ -126,7 +126,7 @@ public:
explicit TestHeaderAndShortIDs(const CBlock& block) :
TestHeaderAndShortIDs(CBlockHeaderAndShortTxIDs{block}) {}
- uint64_t GetShortID(const uint256& txhash) const {
+ uint64_t GetShortID(const Wtxid& txhash) const {
DataStream stream{};
stream << *this;
CBlockHeaderAndShortTxIDs base;
@@ -155,8 +155,8 @@ BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest)
shortIDs.prefilledtxn.resize(1);
shortIDs.prefilledtxn[0] = {1, block.vtx[1]};
shortIDs.shorttxids.resize(2);
- shortIDs.shorttxids[0] = shortIDs.GetShortID(block.vtx[0]->GetHash());
- shortIDs.shorttxids[1] = shortIDs.GetShortID(block.vtx[2]->GetHash());
+ shortIDs.shorttxids[0] = shortIDs.GetShortID(block.vtx[0]->GetWitnessHash());
+ shortIDs.shorttxids[1] = shortIDs.GetShortID(block.vtx[2]->GetWitnessHash());
DataStream stream{};
stream << shortIDs;
@@ -226,7 +226,7 @@ BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest)
shortIDs.prefilledtxn[0] = {0, block.vtx[0]};
shortIDs.prefilledtxn[1] = {1, block.vtx[2]}; // id == 1 as it is 1 after index 1
shortIDs.shorttxids.resize(1);
- shortIDs.shorttxids[0] = shortIDs.GetShortID(block.vtx[1]->GetHash());
+ shortIDs.shorttxids[0] = shortIDs.GetShortID(block.vtx[1]->GetWitnessHash());
DataStream stream{};
stream << shortIDs;
diff --git a/src/test/bswap_tests.cpp b/src/test/bswap_tests.cpp
index 2be7122fc1..fe48e39c41 100644
--- a/src/test/bswap_tests.cpp
+++ b/src/test/bswap_tests.cpp
@@ -16,9 +16,9 @@ BOOST_AUTO_TEST_CASE(bswap_tests)
uint16_t e1 = 0x3412;
uint32_t e2 = 0xbc9a7856;
uint64_t e3 = 0xbc9a78563412f0de;
- BOOST_CHECK(bswap_16(u1) == e1);
- BOOST_CHECK(bswap_32(u2) == e2);
- BOOST_CHECK(bswap_64(u3) == e3);
+ BOOST_CHECK(internal_bswap_16(u1) == e1);
+ BOOST_CHECK(internal_bswap_32(u2) == e2);
+ BOOST_CHECK(internal_bswap_64(u3) == e3);
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/coinstatsindex_tests.cpp b/src/test/coinstatsindex_tests.cpp
index cc1ec49d41..08814c1499 100644
--- a/src/test/coinstatsindex_tests.cpp
+++ b/src/test/coinstatsindex_tests.cpp
@@ -70,7 +70,7 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_initial_sync, TestChain100Setup)
// SyncWithValidationInterfaceQueue() call below is also needed to ensure
// TSAN always sees the test thread waiting for the notification thread, and
// avoid potential false positive reports.
- SyncWithValidationInterfaceQueue();
+ m_node.validation_signals->SyncWithValidationInterfaceQueue();
// Shutdown sequence (c.f. Shutdown() in init.cpp)
coin_stats_index.Stop();
diff --git a/src/test/common_url_tests.cpp b/src/test/common_url_tests.cpp
new file mode 100644
index 0000000000..065c7d97bc
--- /dev/null
+++ b/src/test/common_url_tests.cpp
@@ -0,0 +1,75 @@
+// Copyright (c) 2024-present The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or https://opensource.org/license/mit/.
+
+#include <common/url.h>
+
+#include <string>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_SUITE(common_url_tests)
+
+// These test vectors were ported from test/regress.c in the libevent library
+// which used to be a dependency of the UrlDecode function.
+
+BOOST_AUTO_TEST_CASE(encode_decode_test) {
+ BOOST_CHECK_EQUAL(UrlDecode("Hello"), "Hello");
+ BOOST_CHECK_EQUAL(UrlDecode("99"), "99");
+ BOOST_CHECK_EQUAL(UrlDecode(""), "");
+ BOOST_CHECK_EQUAL(UrlDecode("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789-.~_"),
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789-.~_");
+ BOOST_CHECK_EQUAL(UrlDecode("%20"), " ");
+ BOOST_CHECK_EQUAL(UrlDecode("%FF%F0%E0"), "\xff\xf0\xe0");
+ BOOST_CHECK_EQUAL(UrlDecode("%01%19"), "\x01\x19");
+ BOOST_CHECK_EQUAL(UrlDecode("http%3A%2F%2Fwww.ietf.org%2Frfc%2Frfc3986.txt"),
+ "http://www.ietf.org/rfc/rfc3986.txt");
+ BOOST_CHECK_EQUAL(UrlDecode("1%2B2%3D3"), "1+2=3");
+}
+
+BOOST_AUTO_TEST_CASE(decode_malformed_test) {
+ BOOST_CHECK_EQUAL(UrlDecode("%%xhello th+ere \xff"), "%%xhello th+ere \xff");
+
+ BOOST_CHECK_EQUAL(UrlDecode("%"), "%");
+ BOOST_CHECK_EQUAL(UrlDecode("%%"), "%%");
+ BOOST_CHECK_EQUAL(UrlDecode("%%%"), "%%%");
+ BOOST_CHECK_EQUAL(UrlDecode("%%%%"), "%%%%");
+
+ BOOST_CHECK_EQUAL(UrlDecode("+"), "+");
+ BOOST_CHECK_EQUAL(UrlDecode("++"), "++");
+
+ BOOST_CHECK_EQUAL(UrlDecode("?"), "?");
+ BOOST_CHECK_EQUAL(UrlDecode("??"), "??");
+
+ BOOST_CHECK_EQUAL(UrlDecode("%G1"), "%G1");
+ BOOST_CHECK_EQUAL(UrlDecode("%2"), "%2");
+ BOOST_CHECK_EQUAL(UrlDecode("%ZX"), "%ZX");
+
+ BOOST_CHECK_EQUAL(UrlDecode("valid%20string%G1"), "valid string%G1");
+ BOOST_CHECK_EQUAL(UrlDecode("%20invalid%ZX"), " invalid%ZX");
+ BOOST_CHECK_EQUAL(UrlDecode("%20%G1%ZX"), " %G1%ZX");
+
+ BOOST_CHECK_EQUAL(UrlDecode("%1 "), "%1 ");
+ BOOST_CHECK_EQUAL(UrlDecode("% 9"), "% 9");
+ BOOST_CHECK_EQUAL(UrlDecode(" %Z "), " %Z ");
+ BOOST_CHECK_EQUAL(UrlDecode(" % X"), " % X");
+
+ BOOST_CHECK_EQUAL(UrlDecode("%%ffg"), "%\xffg");
+ BOOST_CHECK_EQUAL(UrlDecode("%fg"), "%fg");
+
+ BOOST_CHECK_EQUAL(UrlDecode("%-1"), "%-1");
+ BOOST_CHECK_EQUAL(UrlDecode("%1-"), "%1-");
+}
+
+BOOST_AUTO_TEST_CASE(decode_lowercase_hex_test) {
+ BOOST_CHECK_EQUAL(UrlDecode("%f0%a0%b0"), "\xf0\xa0\xb0");
+}
+
+BOOST_AUTO_TEST_CASE(decode_internal_nulls_test) {
+ std::string result1{"\0\0x\0\0", 5};
+ BOOST_CHECK_EQUAL(UrlDecode("%00%00x%00%00"), result1);
+ std::string result2{"abc\0\0", 5};
+ BOOST_CHECK_EQUAL(UrlDecode("abc%00%00"), result2);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/compress_tests.cpp b/src/test/compress_tests.cpp
index 264b47b07c..13c2740553 100644
--- a/src/test/compress_tests.cpp
+++ b/src/test/compress_tests.cpp
@@ -4,6 +4,7 @@
#include <compressor.h>
#include <script/script.h>
+#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <stdint.h>
@@ -131,4 +132,36 @@ BOOST_AUTO_TEST_CASE(compress_script_to_uncompressed_pubkey_id)
BOOST_CHECK_EQUAL(out[0], 0x04 | (script[65] & 0x01)); // least significant bit (lsb) of last char of pubkey is mapped into out[0]
}
+BOOST_AUTO_TEST_CASE(compress_p2pk_scripts_not_on_curve)
+{
+ XOnlyPubKey x_not_on_curve;
+ do {
+ x_not_on_curve = XOnlyPubKey(g_insecure_rand_ctx.randbytes(32));
+ } while (x_not_on_curve.IsFullyValid());
+
+ // Check that P2PK script with uncompressed pubkey [=> OP_PUSH65 <0x04 .....> OP_CHECKSIG]
+ // which is not fully valid (i.e. point is not on curve) can't be compressed
+ std::vector<unsigned char> pubkey_raw(65, 0);
+ pubkey_raw[0] = 4;
+ std::copy(x_not_on_curve.begin(), x_not_on_curve.end(), &pubkey_raw[1]);
+ CPubKey pubkey_not_on_curve(pubkey_raw);
+ assert(pubkey_not_on_curve.IsValid());
+ assert(!pubkey_not_on_curve.IsFullyValid());
+ CScript script = CScript() << ToByteVector(pubkey_not_on_curve) << OP_CHECKSIG;
+ BOOST_CHECK_EQUAL(script.size(), 67U);
+
+ CompressedScript out;
+ bool done = CompressScript(script, out);
+ BOOST_CHECK_EQUAL(done, false);
+
+ // Check that compressed P2PK script with uncompressed pubkey that is not fully
+ // valid (i.e. x coordinate of the pubkey is not on curve) can't be decompressed
+ CompressedScript compressed_script(x_not_on_curve.begin(), x_not_on_curve.end());
+ for (unsigned int compression_id : {4, 5}) {
+ CScript uncompressed_script;
+ bool success = DecompressScript(uncompressed_script, compression_id, compressed_script);
+ BOOST_CHECK_EQUAL(success, false);
+ }
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp
index 0a6378adf4..46acc6fc9f 100644
--- a/src/test/crypto_tests.cpp
+++ b/src/test/crypto_tests.cpp
@@ -1060,28 +1060,6 @@ BOOST_AUTO_TEST_CASE(hkdf_hmac_sha256_l32_tests)
"8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d");
}
-BOOST_AUTO_TEST_CASE(countbits_tests)
-{
- FastRandomContext ctx;
- for (unsigned int i = 0; i <= 64; ++i) {
- if (i == 0) {
- // Check handling of zero.
- BOOST_CHECK_EQUAL(CountBits(0), 0U);
- } else if (i < 10) {
- for (uint64_t j = uint64_t{1} << (i - 1); (j >> i) == 0; ++j) {
- // Exhaustively test up to 10 bits
- BOOST_CHECK_EQUAL(CountBits(j), i);
- }
- } else {
- for (int k = 0; k < 1000; k++) {
- // Randomly test 1000 samples of each length above 10 bits.
- uint64_t j = (uint64_t{1}) << (i - 1) | ctx.randbits(i - 1);
- BOOST_CHECK_EQUAL(CountBits(j), i);
- }
- }
- }
-}
-
BOOST_AUTO_TEST_CASE(sha256d64)
{
for (int i = 0; i <= 32; ++i) {
diff --git a/src/test/feefrac_tests.cpp b/src/test/feefrac_tests.cpp
new file mode 100644
index 0000000000..5af3c3d7ed
--- /dev/null
+++ b/src/test/feefrac_tests.cpp
@@ -0,0 +1,85 @@
+// Copyright (c) 2024-present The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <util/feefrac.h>
+#include <random.h>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_SUITE(feefrac_tests)
+
+BOOST_AUTO_TEST_CASE(feefrac_operators)
+{
+ FeeFrac p1{1000, 100}, p2{500, 300};
+ FeeFrac sum{1500, 400};
+ FeeFrac diff{500, -200};
+ FeeFrac empty{0, 0};
+ FeeFrac zero_fee{0, 1}; // zero-fee allowed
+
+ BOOST_CHECK(empty == FeeFrac{}); // same as no-args
+
+ BOOST_CHECK(p1 == p1);
+ BOOST_CHECK(p1 + p2 == sum);
+ BOOST_CHECK(p1 - p2 == diff);
+
+ FeeFrac p3{2000, 200};
+ BOOST_CHECK(p1 != p3); // feefracs only equal if both fee and size are same
+ BOOST_CHECK(p2 != p3);
+
+ FeeFrac p4{3000, 300};
+ BOOST_CHECK(p1 == p4-p3);
+ BOOST_CHECK(p1 + p3 == p4);
+
+ // Fee-rate comparison
+ BOOST_CHECK(p1 > p2);
+ BOOST_CHECK(p1 >= p2);
+ BOOST_CHECK(p1 >= p4-p3);
+ BOOST_CHECK(!(p1 >> p3)); // not strictly better
+ BOOST_CHECK(p1 >> p2); // strictly greater feerate
+
+ BOOST_CHECK(p2 < p1);
+ BOOST_CHECK(p2 <= p1);
+ BOOST_CHECK(p1 <= p4-p3);
+ BOOST_CHECK(!(p3 << p1)); // not strictly worse
+ BOOST_CHECK(p2 << p1); // strictly lower feerate
+
+ // "empty" comparisons
+ BOOST_CHECK(!(p1 >> empty)); // << will always result in false
+ BOOST_CHECK(!(p1 << empty));
+ BOOST_CHECK(!(empty >> empty));
+ BOOST_CHECK(!(empty << empty));
+
+ // empty is always bigger than everything else
+ BOOST_CHECK(empty > p1);
+ BOOST_CHECK(empty > p2);
+ BOOST_CHECK(empty > p3);
+ BOOST_CHECK(empty >= p1);
+ BOOST_CHECK(empty >= p2);
+ BOOST_CHECK(empty >= p3);
+
+ // check "max" values for comparison
+ FeeFrac oversized_1{4611686000000, 4000000};
+ FeeFrac oversized_2{184467440000000, 100000};
+
+ BOOST_CHECK(oversized_1 < oversized_2);
+ BOOST_CHECK(oversized_1 <= oversized_2);
+ BOOST_CHECK(oversized_1 << oversized_2);
+ BOOST_CHECK(oversized_1 != oversized_2);
+
+ // Tests paths that use double arithmetic
+ FeeFrac busted{(static_cast<int64_t>(INT32_MAX)) + 1, INT32_MAX};
+ BOOST_CHECK(!(busted < busted));
+
+ FeeFrac max_fee{2100000000000000, INT32_MAX};
+ BOOST_CHECK(!(max_fee < max_fee));
+ BOOST_CHECK(!(max_fee > max_fee));
+ BOOST_CHECK(max_fee <= max_fee);
+ BOOST_CHECK(max_fee >= max_fee);
+
+ FeeFrac max_fee2{1, 1};
+ BOOST_CHECK(max_fee >= max_fee2);
+
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/fuzz/addition_overflow.cpp b/src/test/fuzz/addition_overflow.cpp
index 5100b6f438..071e5fb029 100644
--- a/src/test/fuzz/addition_overflow.cpp
+++ b/src/test/fuzz/addition_overflow.cpp
@@ -24,12 +24,14 @@ void TestAdditionOverflow(FuzzedDataProvider& fuzzed_data_provider)
assert(is_addition_overflow_custom == AdditionOverflow(j, i));
assert(maybe_add == CheckedAdd(j, i));
assert(sat_add == SaturatingAdd(j, i));
+#ifndef _MSC_VER
T result_builtin;
const bool is_addition_overflow_builtin = __builtin_add_overflow(i, j, &result_builtin);
assert(is_addition_overflow_custom == is_addition_overflow_builtin);
if (!is_addition_overflow_custom) {
assert(i + j == result_builtin);
}
+#endif
if (is_addition_overflow_custom) {
assert(sat_add == std::numeric_limits<T>::min() || sat_add == std::numeric_limits<T>::max());
} else {
diff --git a/src/test/fuzz/deserialize.cpp b/src/test/fuzz/deserialize.cpp
index ebc5673e71..c9a3bc86ac 100644
--- a/src/test/fuzz/deserialize.cpp
+++ b/src/test/fuzz/deserialize.cpp
@@ -33,7 +33,6 @@
#include <optional>
#include <stdexcept>
#include <stdint.h>
-#include <unistd.h>
using node::SnapshotMetadata;
diff --git a/src/test/fuzz/feefrac.cpp b/src/test/fuzz/feefrac.cpp
new file mode 100644
index 0000000000..2c7553360e
--- /dev/null
+++ b/src/test/fuzz/feefrac.cpp
@@ -0,0 +1,123 @@
+// Copyright (c) 2024 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <util/feefrac.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <compare>
+#include <cstdint>
+#include <iostream>
+
+namespace {
+
+/** Compute a * b, represented in 4x32 bits, highest limb first. */
+std::array<uint32_t, 4> Mul128(uint64_t a, uint64_t b)
+{
+ std::array<uint32_t, 4> ret{0, 0, 0, 0};
+
+ /** Perform ret += v << (32 * pos), at 128-bit precision. */
+ auto add_fn = [&](uint64_t v, int pos) {
+ uint64_t accum{0};
+ for (int i = 0; i + pos < 4; ++i) {
+ // Add current value at limb pos in ret.
+ accum += ret[3 - pos - i];
+ // Add low or high half of v.
+ if (i == 0) accum += v & 0xffffffff;
+ if (i == 1) accum += v >> 32;
+ // Store lower half of result in limb pos in ret.
+ ret[3 - pos - i] = accum & 0xffffffff;
+ // Leave carry in accum.
+ accum >>= 32;
+ }
+ // Make sure no overflow.
+ assert(accum == 0);
+ };
+
+ // Multiply the 4 individual limbs (schoolbook multiply, with base 2^32).
+ add_fn((a & 0xffffffff) * (b & 0xffffffff), 0);
+ add_fn((a >> 32) * (b & 0xffffffff), 1);
+ add_fn((a & 0xffffffff) * (b >> 32), 1);
+ add_fn((a >> 32) * (b >> 32), 2);
+ return ret;
+}
+
+/* comparison helper for std::array */
+std::strong_ordering compare_arrays(const std::array<uint32_t, 4>& a, const std::array<uint32_t, 4>& b) {
+ for (size_t i = 0; i < a.size(); ++i) {
+ if (a[i] != b[i]) return a[i] <=> b[i];
+ }
+ return std::strong_ordering::equal;
+}
+
+std::strong_ordering MulCompare(int64_t a1, int64_t a2, int64_t b1, int64_t b2)
+{
+ // Compute and compare signs.
+ int sign_a = (a1 == 0 ? 0 : a1 < 0 ? -1 : 1) * (a2 == 0 ? 0 : a2 < 0 ? -1 : 1);
+ int sign_b = (b1 == 0 ? 0 : b1 < 0 ? -1 : 1) * (b2 == 0 ? 0 : b2 < 0 ? -1 : 1);
+ if (sign_a != sign_b) return sign_a <=> sign_b;
+
+ // Compute absolute values.
+ uint64_t abs_a1 = static_cast<uint64_t>(a1), abs_a2 = static_cast<uint64_t>(a2);
+ uint64_t abs_b1 = static_cast<uint64_t>(b1), abs_b2 = static_cast<uint64_t>(b2);
+ // Use (~x + 1) instead of the equivalent (-x) to silence the linter; mod 2^64 behavior is
+ // intentional here.
+ if (a1 < 0) abs_a1 = ~abs_a1 + 1;
+ if (a2 < 0) abs_a2 = ~abs_a2 + 1;
+ if (b1 < 0) abs_b1 = ~abs_b1 + 1;
+ if (b2 < 0) abs_b2 = ~abs_b2 + 1;
+
+ // Compute products of absolute values.
+ auto mul_abs_a = Mul128(abs_a1, abs_a2);
+ auto mul_abs_b = Mul128(abs_b1, abs_b2);
+ if (sign_a < 0) {
+ return compare_arrays(mul_abs_b, mul_abs_a);
+ } else {
+ return compare_arrays(mul_abs_a, mul_abs_b);
+ }
+}
+
+} // namespace
+
+FUZZ_TARGET(feefrac)
+{
+ FuzzedDataProvider provider(buffer.data(), buffer.size());
+
+ int64_t f1 = provider.ConsumeIntegral<int64_t>();
+ int32_t s1 = provider.ConsumeIntegral<int32_t>();
+ if (s1 == 0) f1 = 0;
+ FeeFrac fr1(f1, s1);
+ assert(fr1.IsEmpty() == (s1 == 0));
+
+ int64_t f2 = provider.ConsumeIntegral<int64_t>();
+ int32_t s2 = provider.ConsumeIntegral<int32_t>();
+ if (s2 == 0) f2 = 0;
+ FeeFrac fr2(f2, s2);
+ assert(fr2.IsEmpty() == (s2 == 0));
+
+ // Feerate comparisons
+ auto cmp_feerate = MulCompare(f1, s2, f2, s1);
+ assert(FeeRateCompare(fr1, fr2) == cmp_feerate);
+ assert((fr1 << fr2) == std::is_lt(cmp_feerate));
+ assert((fr1 >> fr2) == std::is_gt(cmp_feerate));
+
+ // Compare with manual invocation of FeeFrac::Mul.
+ auto cmp_mul = FeeFrac::Mul(f1, s2) <=> FeeFrac::Mul(f2, s1);
+ assert(cmp_mul == cmp_feerate);
+
+ // Same, but using FeeFrac::MulFallback.
+ auto cmp_fallback = FeeFrac::MulFallback(f1, s2) <=> FeeFrac::MulFallback(f2, s1);
+ assert(cmp_fallback == cmp_feerate);
+
+ // Total order comparisons
+ auto cmp_total = std::is_eq(cmp_feerate) ? (s2 <=> s1) : cmp_feerate;
+ assert((fr1 <=> fr2) == cmp_total);
+ assert((fr1 < fr2) == std::is_lt(cmp_total));
+ assert((fr1 > fr2) == std::is_gt(cmp_total));
+ assert((fr1 <= fr2) == std::is_lteq(cmp_total));
+ assert((fr1 >= fr2) == std::is_gteq(cmp_total));
+ assert((fr1 == fr2) == std::is_eq(cmp_total));
+ assert((fr1 != fr2) == std::is_neq(cmp_total));
+}
diff --git a/src/test/fuzz/feeratediagram.cpp b/src/test/fuzz/feeratediagram.cpp
new file mode 100644
index 0000000000..1a9c5ee946
--- /dev/null
+++ b/src/test/fuzz/feeratediagram.cpp
@@ -0,0 +1,133 @@
+// Copyright (c) 2023 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <stdint.h>
+
+#include <vector>
+
+#include <util/feefrac.h>
+#include <policy/rbf.h>
+
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <assert.h>
+
+namespace {
+
+/** Takes the pre-computed and topologically-valid chunks and generates a fee diagram which starts at FeeFrac of (0, 0) */
+std::vector<FeeFrac> BuildDiagramFromChunks(const Span<const FeeFrac> chunks)
+{
+ std::vector<FeeFrac> diagram;
+ diagram.reserve(chunks.size() + 1);
+
+ diagram.emplace_back(0, 0);
+ for (auto& chunk : chunks) {
+ diagram.emplace_back(diagram.back() + chunk);
+ }
+ return diagram;
+}
+
+
+/** Evaluate a diagram at a specific size, returning the fee as a fraction.
+ *
+ * Fees in diagram cannot exceed 2^32, as the returned evaluation could overflow
+ * the FeeFrac::fee field in the result. */
+FeeFrac EvaluateDiagram(int32_t size, Span<const FeeFrac> diagram)
+{
+ assert(diagram.size() > 0);
+ unsigned not_above = 0;
+ unsigned not_below = diagram.size() - 1;
+ // If outside the range of diagram, extend begin/end.
+ if (size < diagram[not_above].size) return {diagram[not_above].fee, 1};
+ if (size > diagram[not_below].size) return {diagram[not_below].fee, 1};
+ // Perform bisection search to locate the diagram segment that size is in.
+ while (not_below > not_above + 1) {
+ unsigned mid = (not_below + not_above) / 2;
+ if (diagram[mid].size <= size) not_above = mid;
+ if (diagram[mid].size >= size) not_below = mid;
+ }
+ // If the size matches a transition point between segments, return its fee.
+ if (not_below == not_above) return {diagram[not_below].fee, 1};
+ // Otherwise, interpolate.
+ auto dir_coef = diagram[not_below] - diagram[not_above];
+ assert(dir_coef.size > 0);
+ // Let A = diagram[not_above] and B = diagram[not_below]
+ const auto& point_a = diagram[not_above];
+ // We want to return:
+ // A.fee + (B.fee - A.fee) / (B.size - A.size) * (size - A.size)
+ // = A.fee + dir_coef.fee / dir_coef.size * (size - A.size)
+ // = (A.fee * dir_coef.size + dir_coef.fee * (size - A.size)) / dir_coef.size
+ assert(size >= point_a.size);
+ return {point_a.fee * dir_coef.size + dir_coef.fee * (size - point_a.size), dir_coef.size};
+}
+
+std::weak_ordering CompareFeeFracWithDiagram(const FeeFrac& ff, Span<const FeeFrac> diagram)
+{
+ return FeeRateCompare(FeeFrac{ff.fee, 1}, EvaluateDiagram(ff.size, diagram));
+}
+
+std::partial_ordering CompareDiagrams(Span<const FeeFrac> dia1, Span<const FeeFrac> dia2)
+{
+ bool all_ge = true;
+ bool all_le = true;
+ for (const auto p1 : dia1) {
+ auto cmp = CompareFeeFracWithDiagram(p1, dia2);
+ if (std::is_lt(cmp)) all_ge = false;
+ if (std::is_gt(cmp)) all_le = false;
+ }
+ for (const auto p2 : dia2) {
+ auto cmp = CompareFeeFracWithDiagram(p2, dia1);
+ if (std::is_lt(cmp)) all_le = false;
+ if (std::is_gt(cmp)) all_ge = false;
+ }
+ if (all_ge && all_le) return std::partial_ordering::equivalent;
+ if (all_ge && !all_le) return std::partial_ordering::greater;
+ if (!all_ge && all_le) return std::partial_ordering::less;
+ return std::partial_ordering::unordered;
+}
+
+void PopulateChunks(FuzzedDataProvider& fuzzed_data_provider, std::vector<FeeFrac>& chunks)
+{
+ chunks.clear();
+
+ LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 50)
+ {
+ chunks.emplace_back(fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(INT32_MIN>>1, INT32_MAX>>1), fuzzed_data_provider.ConsumeIntegralInRange<int32_t>(1, 1000000));
+ }
+ return;
+}
+
+} // namespace
+
+FUZZ_TARGET(build_and_compare_feerate_diagram)
+{
+ // Generate a random set of chunks
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+ std::vector<FeeFrac> chunks1, chunks2;
+ FeeFrac empty{0, 0};
+
+ PopulateChunks(fuzzed_data_provider, chunks1);
+ PopulateChunks(fuzzed_data_provider, chunks2);
+
+ std::vector<FeeFrac> diagram1{BuildDiagramFromChunks(chunks1)};
+ std::vector<FeeFrac> diagram2{BuildDiagramFromChunks(chunks2)};
+
+ assert(diagram1.front() == empty);
+ assert(diagram2.front() == empty);
+
+ auto real = CompareChunks(chunks1, chunks2);
+ auto sim = CompareDiagrams(diagram1, diagram2);
+ assert(real == sim);
+
+ // Do explicit evaluation at up to 1000 points, and verify consistency with the result.
+ LIMITED_WHILE(fuzzed_data_provider.remaining_bytes(), 1000) {
+ int32_t size = fuzzed_data_provider.ConsumeIntegralInRange<int32_t>(0, diagram2.back().size);
+ auto eval1 = EvaluateDiagram(size, diagram1);
+ auto eval2 = EvaluateDiagram(size, diagram2);
+ auto cmp = FeeRateCompare(eval1, eval2);
+ if (std::is_lt(cmp)) assert(!std::is_gt(real));
+ if (std::is_gt(cmp)) assert(!std::is_lt(real));
+ }
+}
diff --git a/src/test/fuzz/fuzz.cpp b/src/test/fuzz/fuzz.cpp
index 6de480ff15..f9915187bd 100644
--- a/src/test/fuzz/fuzz.cpp
+++ b/src/test/fuzz/fuzz.cpp
@@ -25,7 +25,6 @@
#include <memory>
#include <string>
#include <tuple>
-#include <unistd.h>
#include <utility>
#include <vector>
@@ -35,6 +34,8 @@ __AFL_FUZZ_INIT();
const std::function<void(const std::string&)> G_TEST_LOG_FUN{};
+const std::function<std::string()> G_TEST_GET_FULL_NAME{};
+
/**
* A copy of the command line arguments that start with `--`.
* First `LLVMFuzzerInitialize()` is called, which saves the arguments to `g_args`.
@@ -81,7 +82,7 @@ static const TypeTestOneInput* g_test_one_input{nullptr};
void initialize()
{
// Terminate immediately if a fuzzing harness ever tries to create a TCP socket.
- CreateSock = [](const CService&) -> std::unique_ptr<Sock> { std::terminate(); };
+ CreateSock = [](const sa_family_t&) -> std::unique_ptr<Sock> { std::terminate(); };
// Terminate immediately if a fuzzing harness ever tries to perform a DNS lookup.
g_dns_lookup = [](const std::string& name, bool allow_lookup) {
@@ -133,9 +134,9 @@ void initialize()
#if defined(PROVIDE_FUZZ_MAIN_FUNCTION)
static bool read_stdin(std::vector<uint8_t>& data)
{
- uint8_t buffer[1024];
- ssize_t length = 0;
- while ((length = read(STDIN_FILENO, buffer, 1024)) > 0) {
+ std::istream::char_type buffer[1024];
+ std::streamsize length;
+ while ((std::cin.read(buffer, 1024), length = std::cin.gcount()) > 0) {
data.insert(data.end(), buffer, buffer + length);
}
return length == 0;
diff --git a/src/test/fuzz/headerssync.cpp b/src/test/fuzz/headerssync.cpp
index 62f6bbaffe..1aa878bd6d 100644
--- a/src/test/fuzz/headerssync.cpp
+++ b/src/test/fuzz/headerssync.cpp
@@ -108,7 +108,7 @@ FUZZ_TARGET(headers_sync_state, .init = initialize_headers_sync_state_fuzz)
// If we get to redownloading, the presynced headers need
// to have the min amount of work on them.
- assert(CalculateHeadersWork(all_headers) >= min_work);
+ assert(CalculateClaimedHeadersWork(all_headers) >= min_work);
}
}
diff --git a/src/test/fuzz/integer.cpp b/src/test/fuzz/integer.cpp
index 2577f9e97a..db246bb84e 100644
--- a/src/test/fuzz/integer.cpp
+++ b/src/test/fuzz/integer.cpp
@@ -80,7 +80,6 @@ FUZZ_TARGET(integer, .init = initialize_integer)
static const uint256 u256_max(uint256S("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"));
const std::vector<uint256> v256{u256, u256_min, u256_max};
(void)ComputeMerkleRoot(v256);
- (void)CountBits(u64);
(void)DecompressAmount(u64);
{
if (std::optional<CAmount> parsed = ParseMoney(FormatMoney(i64))) {
diff --git a/src/test/fuzz/multiplication_overflow.cpp b/src/test/fuzz/multiplication_overflow.cpp
index aeef4f24b7..a762a4dfe3 100644
--- a/src/test/fuzz/multiplication_overflow.cpp
+++ b/src/test/fuzz/multiplication_overflow.cpp
@@ -17,12 +17,18 @@ void TestMultiplicationOverflow(FuzzedDataProvider& fuzzed_data_provider)
const T i = fuzzed_data_provider.ConsumeIntegral<T>();
const T j = fuzzed_data_provider.ConsumeIntegral<T>();
const bool is_multiplication_overflow_custom = MultiplicationOverflow(i, j);
+#ifndef _MSC_VER
T result_builtin;
const bool is_multiplication_overflow_builtin = __builtin_mul_overflow(i, j, &result_builtin);
assert(is_multiplication_overflow_custom == is_multiplication_overflow_builtin);
if (!is_multiplication_overflow_custom) {
assert(i * j == result_builtin);
}
+#else
+ if (!is_multiplication_overflow_custom) {
+ (void)(i * j);
+ }
+#endif
}
} // namespace
diff --git a/src/test/fuzz/net.cpp b/src/test/fuzz/net.cpp
index c882bd766a..e8b1480c5b 100644
--- a/src/test/fuzz/net.cpp
+++ b/src/test/fuzz/net.cpp
@@ -77,3 +77,40 @@ FUZZ_TARGET(net, .init = initialize_net)
(void)node.HasPermission(net_permission_flags);
(void)node.ConnectedThroughNetwork();
}
+
+FUZZ_TARGET(local_address, .init = initialize_net)
+{
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+ CService service{ConsumeService(fuzzed_data_provider)};
+ CNode node{ConsumeNode(fuzzed_data_provider)};
+ {
+ LOCK(g_maplocalhost_mutex);
+ mapLocalHost.clear();
+ }
+ LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000) {
+ CallOneOf(
+ fuzzed_data_provider,
+ [&] {
+ service = ConsumeService(fuzzed_data_provider);
+ },
+ [&] {
+ const bool added{AddLocal(service, fuzzed_data_provider.ConsumeIntegralInRange<int>(0, LOCAL_MAX - 1))};
+ if (!added) return;
+ assert(service.IsRoutable());
+ assert(IsLocal(service));
+ assert(SeenLocal(service));
+ },
+ [&] {
+ (void)RemoveLocal(service);
+ },
+ [&] {
+ (void)SeenLocal(service);
+ },
+ [&] {
+ (void)IsLocal(service);
+ },
+ [&] {
+ (void)GetLocalAddress(node);
+ });
+ }
+}
diff --git a/src/test/fuzz/net_permissions.cpp b/src/test/fuzz/net_permissions.cpp
index 6ea2139c46..811c0de4b9 100644
--- a/src/test/fuzz/net_permissions.cpp
+++ b/src/test/fuzz/net_permissions.cpp
@@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <net_permissions.h>
+#include <netbase.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
@@ -31,8 +32,9 @@ FUZZ_TARGET(net_permissions)
}
NetWhitelistPermissions net_whitelist_permissions;
+ ConnectionDirection connection_direction;
bilingual_str error_net_whitelist_permissions;
- if (NetWhitelistPermissions::TryParse(s, net_whitelist_permissions, error_net_whitelist_permissions)) {
+ if (NetWhitelistPermissions::TryParse(s, net_whitelist_permissions, connection_direction, error_net_whitelist_permissions)) {
(void)NetPermissions::ToStrings(net_whitelist_permissions.m_flags);
(void)NetPermissions::AddFlag(net_whitelist_permissions.m_flags, net_permission_flags);
assert(NetPermissions::HasFlag(net_whitelist_permissions.m_flags, net_permission_flags));
diff --git a/src/test/fuzz/p2p_transport_serialization.cpp b/src/test/fuzz/p2p_transport_serialization.cpp
index a205ce19f4..1b7a732260 100644
--- a/src/test/fuzz/p2p_transport_serialization.cpp
+++ b/src/test/fuzz/p2p_transport_serialization.cpp
@@ -354,6 +354,7 @@ std::unique_ptr<Transport> MakeV2Transport(NodeId nodeid, bool initiator, RNG& r
} else {
// If it's longer, generate it from the RNG. This avoids having large amounts of
// (hopefully) irrelevant data needing to be stored in the fuzzer data.
+ garb.resize(garb_len);
for (auto& v : garb) v = uint8_t(rng());
}
// Retrieve entropy
diff --git a/src/test/fuzz/package_eval.cpp b/src/test/fuzz/package_eval.cpp
index 9e658e0ced..c201118bce 100644
--- a/src/test/fuzz/package_eval.cpp
+++ b/src/test/fuzz/package_eval.cpp
@@ -47,7 +47,7 @@ void initialize_tx_pool()
g_outpoints_coinbase_init_mature.push_back(prevout);
}
}
- SyncWithValidationInterfaceQueue();
+ g_setup->m_node.validation_signals->SyncWithValidationInterfaceQueue();
}
struct OutpointsUpdater final : public CValidationInterface {
@@ -147,7 +147,7 @@ FUZZ_TARGET(tx_package_eval, .init = initialize_tx_pool)
}
auto outpoints_updater = std::make_shared<OutpointsUpdater>(mempool_outpoints);
- RegisterSharedValidationInterface(outpoints_updater);
+ node.validation_signals->RegisterSharedValidationInterface(outpoints_updater);
CTxMemPool tx_pool_{MakeMempool(fuzzed_data_provider, node)};
MockedTxPool& tx_pool = *static_cast<MockedTxPool*>(&tx_pool_);
@@ -269,15 +269,21 @@ FUZZ_TARGET(tx_package_eval, .init = initialize_tx_pool)
// Remember all added transactions
std::set<CTransactionRef> added;
auto txr = std::make_shared<TransactionsDelta>(added);
- RegisterSharedValidationInterface(txr);
+ node.validation_signals->RegisterSharedValidationInterface(txr);
// When there are multiple transactions in the package, we call ProcessNewPackage(txs, test_accept=false)
// and AcceptToMemoryPool(txs.back(), test_accept=true). When there is only 1 transaction, we might flip it
// (the package is a test accept and ATMP is a submission).
auto single_submit = txs.size() == 1 && fuzzed_data_provider.ConsumeBool();
+ // Exercise client_maxfeerate logic
+ std::optional<CFeeRate> client_maxfeerate{};
+ if (fuzzed_data_provider.ConsumeBool()) {
+ client_maxfeerate = CFeeRate(fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(-1, 50 * COIN), 100);
+ }
+
const auto result_package = WITH_LOCK(::cs_main,
- return ProcessNewPackage(chainstate, tx_pool, txs, /*test_accept=*/single_submit));
+ return ProcessNewPackage(chainstate, tx_pool, txs, /*test_accept=*/single_submit, client_maxfeerate));
// Always set bypass_limits to false because it is not supported in ProcessNewPackage and
// can be a source of divergence.
@@ -285,8 +291,8 @@ FUZZ_TARGET(tx_package_eval, .init = initialize_tx_pool)
/*bypass_limits=*/false, /*test_accept=*/!single_submit));
const bool passed = res.m_result_type == MempoolAcceptResult::ResultType::VALID;
- SyncWithValidationInterfaceQueue();
- UnregisterSharedValidationInterface(txr);
+ node.validation_signals->SyncWithValidationInterfaceQueue();
+ node.validation_signals->UnregisterSharedValidationInterface(txr);
// There is only 1 transaction in the package. We did a test-package-accept and a ATMP
if (single_submit) {
@@ -310,7 +316,7 @@ FUZZ_TARGET(tx_package_eval, .init = initialize_tx_pool)
CheckMempoolV3Invariants(tx_pool);
}
- UnregisterSharedValidationInterface(outpoints_updater);
+ node.validation_signals->UnregisterSharedValidationInterface(outpoints_updater);
WITH_LOCK(::cs_main, tx_pool.check(chainstate.CoinsTip(), chainstate.m_chain.Height() + 1));
}
diff --git a/src/test/fuzz/partially_downloaded_block.cpp b/src/test/fuzz/partially_downloaded_block.cpp
index 4a4b46da60..ab75afe066 100644
--- a/src/test/fuzz/partially_downloaded_block.cpp
+++ b/src/test/fuzz/partially_downloaded_block.cpp
@@ -60,7 +60,7 @@ FUZZ_TARGET(partially_downloaded_block, .init = initialize_pdb)
// The coinbase is always available
available.insert(0);
- std::vector<std::pair<uint256, CTransactionRef>> extra_txn;
+ std::vector<CTransactionRef> extra_txn;
for (size_t i = 1; i < block->vtx.size(); ++i) {
auto tx{block->vtx[i]};
@@ -68,7 +68,7 @@ FUZZ_TARGET(partially_downloaded_block, .init = initialize_pdb)
bool add_to_mempool{fuzzed_data_provider.ConsumeBool()};
if (add_to_extra_txn) {
- extra_txn.emplace_back(tx->GetWitnessHash(), tx);
+ extra_txn.emplace_back(tx);
available.insert(i);
}
diff --git a/src/test/fuzz/process_message.cpp b/src/test/fuzz/process_message.cpp
index 56b391ed5c..a467fd5382 100644
--- a/src/test/fuzz/process_message.cpp
+++ b/src/test/fuzz/process_message.cpp
@@ -47,7 +47,7 @@ void initialize_process_message()
for (int i = 0; i < 2 * COINBASE_MATURITY; i++) {
MineBlock(g_setup->m_node, CScript() << OP_TRUE);
}
- SyncWithValidationInterfaceQueue();
+ g_setup->m_node.validation_signals->SyncWithValidationInterfaceQueue();
}
FUZZ_TARGET(process_message, .init = initialize_process_message)
@@ -89,6 +89,6 @@ FUZZ_TARGET(process_message, .init = initialize_process_message)
}
g_setup->m_node.peerman->SendMessages(&p2p_node);
}
- SyncWithValidationInterfaceQueue();
+ g_setup->m_node.validation_signals->SyncWithValidationInterfaceQueue();
g_setup->m_node.connman->StopNodes();
}
diff --git a/src/test/fuzz/process_messages.cpp b/src/test/fuzz/process_messages.cpp
index 6b264907b5..38acd432fa 100644
--- a/src/test/fuzz/process_messages.cpp
+++ b/src/test/fuzz/process_messages.cpp
@@ -37,7 +37,7 @@ void initialize_process_messages()
for (int i = 0; i < 2 * COINBASE_MATURITY; i++) {
MineBlock(g_setup->m_node, CScript() << OP_TRUE);
}
- SyncWithValidationInterfaceQueue();
+ g_setup->m_node.validation_signals->SyncWithValidationInterfaceQueue();
}
FUZZ_TARGET(process_messages, .init = initialize_process_messages)
@@ -89,6 +89,6 @@ FUZZ_TARGET(process_messages, .init = initialize_process_messages)
g_setup->m_node.peerman->SendMessages(&random_node);
}
}
- SyncWithValidationInterfaceQueue();
+ g_setup->m_node.validation_signals->SyncWithValidationInterfaceQueue();
g_setup->m_node.connman->StopNodes();
}
diff --git a/src/test/fuzz/rbf.cpp b/src/test/fuzz/rbf.cpp
index aa6385d12d..64785948f6 100644
--- a/src/test/fuzz/rbf.cpp
+++ b/src/test/fuzz/rbf.cpp
@@ -23,12 +23,30 @@ namespace {
const BasicTestingSetup* g_setup;
} // namespace
+const int NUM_ITERS = 10000;
+
+std::vector<COutPoint> g_outpoints;
+
void initialize_rbf()
{
static const auto testing_setup = MakeNoLogFileContext<>();
g_setup = testing_setup.get();
}
+void initialize_package_rbf()
+{
+ static const auto testing_setup = MakeNoLogFileContext<>();
+ g_setup = testing_setup.get();
+
+ // Create a fixed set of unique "UTXOs" to source parents from
+ // to avoid fuzzer giving circular references
+ for (int i = 0; i < NUM_ITERS; ++i) {
+ g_outpoints.emplace_back();
+ g_outpoints.back().n = i;
+ }
+
+}
+
FUZZ_TARGET(rbf, .init = initialize_rbf)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
@@ -40,7 +58,7 @@ FUZZ_TARGET(rbf, .init = initialize_rbf)
CTxMemPool pool{MemPoolOptionsForTest(g_setup->m_node)};
- LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000)
+ LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), NUM_ITERS)
{
const std::optional<CMutableTransaction> another_mtx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider, TX_WITH_WITNESS);
if (!another_mtx) {
@@ -63,3 +81,124 @@ FUZZ_TARGET(rbf, .init = initialize_rbf)
(void)IsRBFOptIn(tx, pool);
}
}
+
+FUZZ_TARGET(package_rbf, .init = initialize_package_rbf)
+{
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+ SetMockTime(ConsumeTime(fuzzed_data_provider));
+
+ std::optional<CMutableTransaction> child = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider, TX_WITH_WITNESS);
+ if (!child) return;
+
+ CTxMemPool pool{MemPoolOptionsForTest(g_setup->m_node)};
+
+ // Add a bunch of parent-child pairs to the mempool, and remember them.
+ std::vector<CTransaction> mempool_txs;
+ size_t iter{0};
+
+ int32_t replacement_vsize = fuzzed_data_provider.ConsumeIntegralInRange<int32_t>(1, 1000000);
+
+ // Keep track of the total vsize of CTxMemPoolEntry's being added to the mempool to avoid overflow
+ // Add replacement_vsize since this is added to new diagram during RBF check
+ int64_t running_vsize_total{replacement_vsize};
+
+ LOCK2(cs_main, pool.cs);
+
+ LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), NUM_ITERS)
+ {
+ // Make sure txns only have one input, and that a unique input is given to avoid circular references
+ std::optional<CMutableTransaction> parent = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider, TX_WITH_WITNESS);
+ if (!parent) {
+ return;
+ }
+ assert(iter <= g_outpoints.size());
+ parent->vin.resize(1);
+ parent->vin[0].prevout = g_outpoints[iter++];
+
+ mempool_txs.emplace_back(*parent);
+ const auto parent_entry = ConsumeTxMemPoolEntry(fuzzed_data_provider, mempool_txs.back());
+ running_vsize_total += parent_entry.GetTxSize();
+ if (running_vsize_total > std::numeric_limits<int32_t>::max()) {
+ // We aren't adding this final tx to mempool, so we don't want to conflict with it
+ mempool_txs.pop_back();
+ break;
+ }
+ pool.addUnchecked(parent_entry);
+ if (fuzzed_data_provider.ConsumeBool() && !child->vin.empty()) {
+ child->vin[0].prevout = COutPoint{mempool_txs.back().GetHash(), 0};
+ }
+ mempool_txs.emplace_back(*child);
+ const auto child_entry = ConsumeTxMemPoolEntry(fuzzed_data_provider, mempool_txs.back());
+ running_vsize_total += child_entry.GetTxSize();
+ if (running_vsize_total > std::numeric_limits<int32_t>::max()) {
+ // We aren't adding this final tx to mempool, so we don't want to conflict with it
+ mempool_txs.pop_back();
+ break;
+ }
+ pool.addUnchecked(child_entry);
+
+ if (fuzzed_data_provider.ConsumeBool()) {
+ pool.PrioritiseTransaction(mempool_txs.back().GetHash().ToUint256(), fuzzed_data_provider.ConsumeIntegralInRange<int32_t>(-100000, 100000));
+ }
+ }
+
+ // Pick some transactions at random to be the direct conflicts
+ CTxMemPool::setEntries direct_conflicts;
+ for (auto& tx : mempool_txs) {
+ if (fuzzed_data_provider.ConsumeBool()) {
+ direct_conflicts.insert(*pool.GetIter(tx.GetHash()));
+ }
+ }
+
+ // Calculate all conflicts:
+ CTxMemPool::setEntries all_conflicts;
+ for (auto& txiter : direct_conflicts) {
+ pool.CalculateDescendants(txiter, all_conflicts);
+ }
+
+ // Calculate the chunks for a replacement.
+ CAmount replacement_fees = ConsumeMoney(fuzzed_data_provider);
+ auto calc_results{pool.CalculateChunksForRBF(replacement_fees, replacement_vsize, direct_conflicts, all_conflicts)};
+
+ if (calc_results.has_value()) {
+ // Sanity checks on the chunks.
+
+ // Feerates are monotonically decreasing.
+ FeeFrac first_sum;
+ for (size_t i = 0; i < calc_results->first.size(); ++i) {
+ first_sum += calc_results->first[i];
+ if (i) assert(!(calc_results->first[i - 1] << calc_results->first[i]));
+ }
+ FeeFrac second_sum;
+ for (size_t i = 0; i < calc_results->second.size(); ++i) {
+ second_sum += calc_results->second[i];
+ if (i) assert(!(calc_results->second[i - 1] << calc_results->second[i]));
+ }
+
+ FeeFrac replaced;
+ for (auto txiter : all_conflicts) {
+ replaced.fee += txiter->GetModifiedFee();
+ replaced.size += txiter->GetTxSize();
+ }
+ // The total fee & size of the new diagram minus replaced fee & size should be the total
+ // fee & size of the old diagram minus replacement fee & size.
+ assert((first_sum - replaced) == (second_sum - FeeFrac{replacement_fees, replacement_vsize}));
+ }
+
+ // If internals report error, wrapper should too
+ auto err_tuple{ImprovesFeerateDiagram(pool, direct_conflicts, all_conflicts, replacement_fees, replacement_vsize)};
+ if (!calc_results.has_value()) {
+ assert(err_tuple.value().first == DiagramCheckError::UNCALCULABLE);
+ } else {
+ // Diagram check succeeded
+ auto old_sum = std::accumulate(calc_results->first.begin(), calc_results->first.end(), FeeFrac{});
+ auto new_sum = std::accumulate(calc_results->second.begin(), calc_results->second.end(), FeeFrac{});
+ if (!err_tuple.has_value()) {
+ // New diagram's final fee should always match or exceed old diagram's
+ assert(old_sum.fee <= new_sum.fee);
+ } else if (old_sum.fee > new_sum.fee) {
+ // Or it failed, and if old diagram had higher fees, it should be a failure
+ assert(err_tuple.value().first == DiagramCheckError::FAILURE);
+ }
+ }
+}
diff --git a/src/test/fuzz/script_bitcoin_consensus.cpp b/src/test/fuzz/script_bitcoin_consensus.cpp
deleted file mode 100644
index 846389863d..0000000000
--- a/src/test/fuzz/script_bitcoin_consensus.cpp
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (c) 2020 The Bitcoin Core developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-#include <script/bitcoinconsensus.h>
-#include <script/interpreter.h>
-#include <test/fuzz/FuzzedDataProvider.h>
-#include <test/fuzz/fuzz.h>
-#include <test/fuzz/util.h>
-
-#include <cstdint>
-#include <string>
-#include <vector>
-
-FUZZ_TARGET(script_bitcoin_consensus)
-{
- FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
- const std::vector<uint8_t> random_bytes_1 = ConsumeRandomLengthByteVector(fuzzed_data_provider);
- const std::vector<uint8_t> random_bytes_2 = ConsumeRandomLengthByteVector(fuzzed_data_provider);
- const CAmount money = ConsumeMoney(fuzzed_data_provider);
- bitcoinconsensus_error err;
- bitcoinconsensus_error* err_p = fuzzed_data_provider.ConsumeBool() ? &err : nullptr;
- const unsigned int n_in = fuzzed_data_provider.ConsumeIntegral<unsigned int>();
- const unsigned int flags = fuzzed_data_provider.ConsumeIntegral<unsigned int>();
- assert(bitcoinconsensus_version() == BITCOINCONSENSUS_API_VER);
- if ((flags & SCRIPT_VERIFY_WITNESS) != 0 && (flags & SCRIPT_VERIFY_P2SH) == 0) {
- return;
- }
- (void)bitcoinconsensus_verify_script(random_bytes_1.data(), random_bytes_1.size(), random_bytes_2.data(), random_bytes_2.size(), n_in, flags, err_p);
- (void)bitcoinconsensus_verify_script_with_amount(random_bytes_1.data(), random_bytes_1.size(), money, random_bytes_2.data(), random_bytes_2.size(), n_in, flags, err_p);
-
- std::vector<UTXO> spent_outputs;
- std::vector<std::vector<unsigned char>> spent_spks;
- if (n_in <= 24386) {
- spent_outputs.reserve(n_in);
- spent_spks.reserve(n_in);
- for (size_t i = 0; i < n_in; ++i) {
- spent_spks.push_back(ConsumeRandomLengthByteVector(fuzzed_data_provider));
- const CAmount value{ConsumeMoney(fuzzed_data_provider)};
- const auto spk_size{static_cast<unsigned>(spent_spks.back().size())};
- spent_outputs.push_back({.scriptPubKey = spent_spks.back().data(), .scriptPubKeySize = spk_size, .value = value});
- }
- }
-
- const auto spent_outs_size{static_cast<unsigned>(spent_outputs.size())};
-
- (void)bitcoinconsensus_verify_script_with_spent_outputs(
- random_bytes_1.data(), random_bytes_1.size(), money, random_bytes_2.data(), random_bytes_2.size(),
- spent_outputs.data(), spent_outs_size, n_in, flags, err_p);
-}
diff --git a/src/test/fuzz/string.cpp b/src/test/fuzz/string.cpp
index e81efac6e0..631da13803 100644
--- a/src/test/fuzz/string.cpp
+++ b/src/test/fuzz/string.cpp
@@ -90,7 +90,7 @@ FUZZ_TARGET(string)
(void)ToUpper(random_string_1);
(void)TrimString(random_string_1);
(void)TrimString(random_string_1, random_string_2);
- (void)urlDecode(random_string_1);
+ (void)UrlDecode(random_string_1);
(void)ContainsNoNUL(random_string_1);
(void)_(random_string_1.c_str());
try {
diff --git a/src/test/fuzz/tx_pool.cpp b/src/test/fuzz/tx_pool.cpp
index fcf230642a..0b4019d5eb 100644
--- a/src/test/fuzz/tx_pool.cpp
+++ b/src/test/fuzz/tx_pool.cpp
@@ -50,7 +50,7 @@ void initialize_tx_pool()
g_outpoints_coinbase_init_immature;
outpoints.push_back(prevout);
}
- SyncWithValidationInterfaceQueue();
+ g_setup->m_node.validation_signals->SyncWithValidationInterfaceQueue();
}
struct TransactionsDelta final : public CValidationInterface {
@@ -105,7 +105,7 @@ void Finish(FuzzedDataProvider& fuzzed_data_provider, MockedTxPool& tx_pool, Cha
assert(tx_pool.size() < info_all.size());
WITH_LOCK(::cs_main, tx_pool.check(chainstate.CoinsTip(), chainstate.m_chain.Height() + 1));
}
- SyncWithValidationInterfaceQueue();
+ g_setup->m_node.validation_signals->SyncWithValidationInterfaceQueue();
}
void MockTime(FuzzedDataProvider& fuzzed_data_provider, const Chainstate& chainstate)
@@ -285,13 +285,13 @@ FUZZ_TARGET(tx_pool_standard, .init = initialize_tx_pool)
std::set<CTransactionRef> removed;
std::set<CTransactionRef> added;
auto txr = std::make_shared<TransactionsDelta>(removed, added);
- RegisterSharedValidationInterface(txr);
+ node.validation_signals->RegisterSharedValidationInterface(txr);
const bool bypass_limits = fuzzed_data_provider.ConsumeBool();
// Make sure ProcessNewPackage on one transaction works.
// The result is not guaranteed to be the same as what is returned by ATMP.
const auto result_package = WITH_LOCK(::cs_main,
- return ProcessNewPackage(chainstate, tx_pool, {tx}, true));
+ return ProcessNewPackage(chainstate, tx_pool, {tx}, true, /*client_maxfeerate=*/{}));
// If something went wrong due to a package-specific policy, it might not return a
// validation result for the transaction.
if (result_package.m_state.GetResult() != PackageValidationResult::PCKG_POLICY) {
@@ -303,8 +303,8 @@ FUZZ_TARGET(tx_pool_standard, .init = initialize_tx_pool)
const auto res = WITH_LOCK(::cs_main, return AcceptToMemoryPool(chainstate, tx, GetTime(), bypass_limits, /*test_accept=*/false));
const bool accepted = res.m_result_type == MempoolAcceptResult::ResultType::VALID;
- SyncWithValidationInterfaceQueue();
- UnregisterSharedValidationInterface(txr);
+ node.validation_signals->SyncWithValidationInterfaceQueue();
+ node.validation_signals->UnregisterSharedValidationInterface(txr);
bool txid_in_mempool = tx_pool.exists(GenTxid::Txid(tx->GetHash()));
bool wtxid_in_mempool = tx_pool.exists(GenTxid::Wtxid(tx->GetWitnessHash()));
diff --git a/src/test/fuzz/util.cpp b/src/test/fuzz/util.cpp
index 23b0761355..259b00fcae 100644
--- a/src/test/fuzz/util.cpp
+++ b/src/test/fuzz/util.cpp
@@ -272,7 +272,7 @@ FILE* FuzzedFileProvider::open()
[&] {
mode = "a+";
});
-#if defined _GNU_SOURCE && !defined __ANDROID__
+#if defined _GNU_SOURCE && (defined(__linux__) || defined(__FreeBSD__))
const cookie_io_functions_t io_hooks = {
FuzzedFileProvider::read,
FuzzedFileProvider::write,
diff --git a/src/test/fuzz/util/descriptor.cpp b/src/test/fuzz/util/descriptor.cpp
index df78bdf314..0fed2bc5e1 100644
--- a/src/test/fuzz/util/descriptor.cpp
+++ b/src/test/fuzz/util/descriptor.cpp
@@ -15,7 +15,7 @@ void MockedDescriptorConverter::Init() {
// an extended one.
if (IdIsCompPubKey(i) || IdIsUnCompPubKey(i) || IdIsXOnlyPubKey(i) || IdIsConstPrivKey(i)) {
CKey privkey;
- privkey.Set(UCharCast(key_data.begin()), UCharCast(key_data.end()), !IdIsUnCompPubKey(i));
+ privkey.Set(key_data.begin(), key_data.end(), !IdIsUnCompPubKey(i));
if (IdIsCompPubKey(i) || IdIsUnCompPubKey(i)) {
CPubKey pubkey{privkey.GetPubKey()};
keys_str[i] = HexStr(pubkey);
diff --git a/src/test/i2p_tests.cpp b/src/test/i2p_tests.cpp
index f80f07d190..d7249d88f4 100644
--- a/src/test/i2p_tests.cpp
+++ b/src/test/i2p_tests.cpp
@@ -6,6 +6,7 @@
#include <i2p.h>
#include <logging.h>
#include <netaddress.h>
+#include <netbase.h>
#include <test/util/logging.h>
#include <test/util/net.h>
#include <test/util/setup_common.h>
@@ -38,7 +39,7 @@ public:
private:
const BCLog::Level m_prev_log_level;
- const std::function<std::unique_ptr<Sock>(const CService&)> m_create_sock_orig;
+ const std::function<std::unique_ptr<Sock>(const sa_family_t&)> m_create_sock_orig;
};
BOOST_FIXTURE_TEST_SUITE(i2p_tests, EnvTestingSetup)
@@ -46,12 +47,14 @@ BOOST_FIXTURE_TEST_SUITE(i2p_tests, EnvTestingSetup)
BOOST_AUTO_TEST_CASE(unlimited_recv)
{
// Mock CreateSock() to create MockSock.
- CreateSock = [](const CService&) {
+ CreateSock = [](const sa_family_t&) {
return std::make_unique<StaticContentsSock>(std::string(i2p::sam::MAX_MSG_SIZE + 1, 'a'));
};
CThreadInterrupt interrupt;
- i2p::sam::Session session(gArgs.GetDataDirNet() / "test_i2p_private_key", CService{}, &interrupt);
+ const std::optional<CService> addr{Lookup("127.0.0.1", 9000, false)};
+ const Proxy sam_proxy(addr.value(), false);
+ i2p::sam::Session session(gArgs.GetDataDirNet() / "test_i2p_private_key", sam_proxy, &interrupt);
{
ASSERT_DEBUG_LOG("Creating persistent SAM session");
@@ -66,7 +69,7 @@ BOOST_AUTO_TEST_CASE(unlimited_recv)
BOOST_AUTO_TEST_CASE(listen_ok_accept_fail)
{
size_t num_sockets{0};
- CreateSock = [&num_sockets](const CService&) {
+ CreateSock = [&num_sockets](const sa_family_t&) {
// clang-format off
++num_sockets;
// First socket is the control socket for creating the session.
@@ -111,8 +114,10 @@ BOOST_AUTO_TEST_CASE(listen_ok_accept_fail)
};
CThreadInterrupt interrupt;
+ const CService addr{in6_addr(IN6ADDR_LOOPBACK_INIT), /*port=*/7656};
+ const Proxy sam_proxy(addr, false);
i2p::sam::Session session(gArgs.GetDataDirNet() / "test_i2p_private_key",
- CService{in6_addr(IN6ADDR_LOOPBACK_INIT), /*port=*/7656},
+ sam_proxy,
&interrupt);
i2p::Connection conn;
@@ -130,7 +135,7 @@ BOOST_AUTO_TEST_CASE(damaged_private_key)
{
const auto CreateSockOrig = CreateSock;
- CreateSock = [](const CService&) {
+ CreateSock = [](const sa_family_t&) {
return std::make_unique<StaticContentsSock>("HELLO REPLY RESULT=OK VERSION=3.1\n"
"SESSION STATUS RESULT=OK DESTINATION=\n");
};
@@ -154,7 +159,9 @@ BOOST_AUTO_TEST_CASE(damaged_private_key)
BOOST_REQUIRE(WriteBinaryFile(i2p_private_key_file, file_contents));
CThreadInterrupt interrupt;
- i2p::sam::Session session(i2p_private_key_file, CService{}, &interrupt);
+ const CService addr{in6_addr(IN6ADDR_LOOPBACK_INIT), /*port=*/7656};
+ const Proxy sam_proxy{addr, false};
+ i2p::sam::Session session(i2p_private_key_file, sam_proxy, &interrupt);
{
ASSERT_DEBUG_LOG("Creating persistent SAM session");
diff --git a/src/test/main.cpp b/src/test/main.cpp
index 0809f83c93..67740ece93 100644
--- a/src/test/main.cpp
+++ b/src/test/main.cpp
@@ -39,3 +39,10 @@ const std::function<std::vector<const char*>()> G_TEST_COMMAND_LINE_ARGUMENTS =
}
return args;
};
+
+/**
+ * Retrieve the boost unit test name.
+ */
+const std::function<std::string()> G_TEST_GET_FULL_NAME = []() {
+ return boost::unit_test::framework::current_test_case().full_name();
+};
diff --git a/src/test/miniscript_tests.cpp b/src/test/miniscript_tests.cpp
index 1f28e61bc9..a3699f84b6 100644
--- a/src/test/miniscript_tests.cpp
+++ b/src/test/miniscript_tests.cpp
@@ -297,6 +297,7 @@ using miniscript::operator"" _mst;
using Node = miniscript::Node<CPubKey>;
/** Compute all challenges (pubkeys, hashes, timelocks) that occur in a given Miniscript. */
+// NOLINTNEXTLINE(misc-no-recursion)
std::set<Challenge> FindChallenges(const NodeRef& ref) {
std::set<Challenge> chal;
for (const auto& key : ref->keys) {
diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp
index fa70f62eb4..3422cb8023 100644
--- a/src/test/netbase_tests.cpp
+++ b/src/test/netbase_tests.cpp
@@ -366,6 +366,7 @@ BOOST_AUTO_TEST_CASE(netpermissions_test)
bilingual_str error;
NetWhitebindPermissions whitebindPermissions;
NetWhitelistPermissions whitelistPermissions;
+ ConnectionDirection connection_direction;
// Detect invalid white bind
BOOST_CHECK(!NetWhitebindPermissions::TryParse("", whitebindPermissions, error));
@@ -435,24 +436,33 @@ BOOST_AUTO_TEST_CASE(netpermissions_test)
BOOST_CHECK(NetWhitebindPermissions::TryParse(",,@1.2.3.4:32", whitebindPermissions, error));
BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, NetPermissionFlags::None);
+ BOOST_CHECK(!NetWhitebindPermissions::TryParse("out,forcerelay@1.2.3.4:32", whitebindPermissions, error));
+ BOOST_CHECK(error.original.find("whitebind may only be used for incoming connections (\"out\" was passed)") != std::string::npos);
+
// Detect invalid flag
BOOST_CHECK(!NetWhitebindPermissions::TryParse("bloom,forcerelay,oopsie@1.2.3.4:32", whitebindPermissions, error));
BOOST_CHECK(error.original.find("Invalid P2P permission") != std::string::npos);
// Check netmask error
- BOOST_CHECK(!NetWhitelistPermissions::TryParse("bloom,forcerelay,noban@1.2.3.4:32", whitelistPermissions, error));
+ BOOST_CHECK(!NetWhitelistPermissions::TryParse("bloom,forcerelay,noban@1.2.3.4:32", whitelistPermissions, connection_direction, error));
BOOST_CHECK(error.original.find("Invalid netmask specified in -whitelist") != std::string::npos);
// Happy path for whitelist parsing
- BOOST_CHECK(NetWhitelistPermissions::TryParse("noban@1.2.3.4", whitelistPermissions, error));
+ BOOST_CHECK(NetWhitelistPermissions::TryParse("noban@1.2.3.4", whitelistPermissions, connection_direction, error));
BOOST_CHECK_EQUAL(whitelistPermissions.m_flags, NetPermissionFlags::NoBan);
BOOST_CHECK(NetPermissions::HasFlag(whitelistPermissions.m_flags, NetPermissionFlags::NoBan));
- BOOST_CHECK(NetWhitelistPermissions::TryParse("bloom,forcerelay,noban,relay@1.2.3.4/32", whitelistPermissions, error));
+ BOOST_CHECK(NetWhitelistPermissions::TryParse("bloom,forcerelay,noban,relay@1.2.3.4/32", whitelistPermissions, connection_direction, error));
BOOST_CHECK_EQUAL(whitelistPermissions.m_flags, NetPermissionFlags::BloomFilter | NetPermissionFlags::ForceRelay | NetPermissionFlags::NoBan | NetPermissionFlags::Relay);
BOOST_CHECK(error.empty());
BOOST_CHECK_EQUAL(whitelistPermissions.m_subnet.ToString(), "1.2.3.4/32");
- BOOST_CHECK(NetWhitelistPermissions::TryParse("bloom,forcerelay,noban,relay,mempool@1.2.3.4/32", whitelistPermissions, error));
+ BOOST_CHECK(NetWhitelistPermissions::TryParse("bloom,forcerelay,noban,relay,mempool@1.2.3.4/32", whitelistPermissions, connection_direction, error));
+ BOOST_CHECK(NetWhitelistPermissions::TryParse("in,relay@1.2.3.4", whitelistPermissions, connection_direction, error));
+ BOOST_CHECK_EQUAL(connection_direction, ConnectionDirection::In);
+ BOOST_CHECK(NetWhitelistPermissions::TryParse("out,bloom@1.2.3.4", whitelistPermissions, connection_direction, error));
+ BOOST_CHECK_EQUAL(connection_direction, ConnectionDirection::Out);
+ BOOST_CHECK(NetWhitelistPermissions::TryParse("in,out,bloom@1.2.3.4", whitelistPermissions, connection_direction, error));
+ BOOST_CHECK_EQUAL(connection_direction, ConnectionDirection::Both);
const auto strings = NetPermissions::ToStrings(NetPermissionFlags::All);
BOOST_CHECK_EQUAL(strings.size(), 7U);
diff --git a/src/test/peerman_tests.cpp b/src/test/peerman_tests.cpp
index 2c79329385..28866695bc 100644
--- a/src/test/peerman_tests.cpp
+++ b/src/test/peerman_tests.cpp
@@ -25,7 +25,7 @@ static void mineBlock(const node::NodeContext& node, std::chrono::seconds block_
block.fChecked = true; // little speedup
SetMockTime(curr_time); // process block at current time
Assert(node.chainman->ProcessNewBlock(std::make_shared<const CBlock>(block), /*force_processing=*/true, /*min_pow_checked=*/true, nullptr));
- SyncWithValidationInterfaceQueue(); // drain events queue
+ node.validation_signals->SyncWithValidationInterfaceQueue(); // drain events queue
}
// Verifying when network-limited peer connections are desirable based on the node's proximity to the tip
@@ -57,7 +57,7 @@ BOOST_AUTO_TEST_CASE(connections_desirable_service_flags)
// By now, we tested that the connections desirable services flags change based on the node's time proximity to the tip.
// Now, perform the same tests for when the node receives a block.
- RegisterValidationInterface(peerman.get());
+ m_node.validation_signals->RegisterValidationInterface(peerman.get());
// First, verify a block in the past doesn't enable limited peers connections
// At this point, our time is (NODE_NETWORK_LIMITED_ALLOW_CONN_BLOCKS + 1) * 10 minutes ahead the tip's time.
diff --git a/src/test/policyestimator_tests.cpp b/src/test/policyestimator_tests.cpp
index ede73c6895..6cadc3290a 100644
--- a/src/test/policyestimator_tests.cpp
+++ b/src/test/policyestimator_tests.cpp
@@ -20,7 +20,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
{
CBlockPolicyEstimator& feeEst = *Assert(m_node.fee_estimator);
CTxMemPool& mpool = *Assert(m_node.mempool);
- RegisterValidationInterface(&feeEst);
+ m_node.validation_signals->RegisterValidationInterface(&feeEst);
TestMemPoolEntryHelper entry;
CAmount basefee(2000);
CAmount deltaFee(100);
@@ -74,7 +74,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
/*submitted_in_package=*/false,
/*chainstate_is_current=*/true,
/*has_no_mempool_parents=*/true)};
- GetMainSignals().TransactionAddedToMempool(tx_info, mpool.GetAndIncrementSequence());
+ m_node.validation_signals->TransactionAddedToMempool(tx_info, mpool.GetAndIncrementSequence());
}
uint256 hash = tx.GetHash();
txHashes[j].push_back(hash);
@@ -102,7 +102,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
// Check after just a few txs that combining buckets works as expected
if (blocknum == 3) {
// Wait for fee estimator to catch up
- SyncWithValidationInterfaceQueue();
+ m_node.validation_signals->SyncWithValidationInterfaceQueue();
// At this point we should need to combine 3 buckets to get enough data points
// So estimateFee(1) should fail and estimateFee(2) should return somewhere around
// 9*baserate. estimateFee(2) %'s are 100,100,90 = average 97%
@@ -113,7 +113,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
}
// Wait for fee estimator to catch up
- SyncWithValidationInterfaceQueue();
+ m_node.validation_signals->SyncWithValidationInterfaceQueue();
std::vector<CAmount> origFeeEst;
// Highest feerate is 10*baseRate and gets in all blocks,
@@ -146,7 +146,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
}
// Wait for fee estimator to catch up
- SyncWithValidationInterfaceQueue();
+ m_node.validation_signals->SyncWithValidationInterfaceQueue();
BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));
for (int i = 2; i < 10;i++) {
@@ -175,7 +175,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
/*submitted_in_package=*/false,
/*chainstate_is_current=*/true,
/*has_no_mempool_parents=*/true)};
- GetMainSignals().TransactionAddedToMempool(tx_info, mpool.GetAndIncrementSequence());
+ m_node.validation_signals->TransactionAddedToMempool(tx_info, mpool.GetAndIncrementSequence());
}
uint256 hash = tx.GetHash();
txHashes[j].push_back(hash);
@@ -188,7 +188,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
}
// Wait for fee estimator to catch up
- SyncWithValidationInterfaceQueue();
+ m_node.validation_signals->SyncWithValidationInterfaceQueue();
for (int i = 1; i < 10;i++) {
BOOST_CHECK(feeEst.estimateFee(i) == CFeeRate(0) || feeEst.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);
@@ -212,7 +212,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
block.clear();
// Wait for fee estimator to catch up
- SyncWithValidationInterfaceQueue();
+ m_node.validation_signals->SyncWithValidationInterfaceQueue();
BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));
for (int i = 2; i < 10;i++) {
@@ -239,7 +239,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
/*submitted_in_package=*/false,
/*chainstate_is_current=*/true,
/*has_no_mempool_parents=*/true)};
- GetMainSignals().TransactionAddedToMempool(tx_info, mpool.GetAndIncrementSequence());
+ m_node.validation_signals->TransactionAddedToMempool(tx_info, mpool.GetAndIncrementSequence());
}
uint256 hash = tx.GetHash();
CTransactionRef ptx = mpool.get(hash);
@@ -257,7 +257,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
block.clear();
}
// Wait for fee estimator to catch up
- SyncWithValidationInterfaceQueue();
+ m_node.validation_signals->SyncWithValidationInterfaceQueue();
BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));
for (int i = 2; i < 9; i++) { // At 9, the original estimate was already at the bottom (b/c scale = 2)
BOOST_CHECK(feeEst.estimateFee(i).GetFeePerK() < origFeeEst[i-1] - deltaFee);
diff --git a/src/test/rbf_tests.cpp b/src/test/rbf_tests.cpp
index e6c135eed9..19e45c550a 100644
--- a/src/test/rbf_tests.cpp
+++ b/src/test/rbf_tests.cpp
@@ -37,7 +37,38 @@ static inline CTransactionRef make_tx(const std::vector<CTransactionRef>& inputs
return MakeTransactionRef(tx);
}
-static void add_descendants(const CTransactionRef& tx, int32_t num_descendants, CTxMemPool& pool)
+// Make two child transactions from parent (which must have at least 2 outputs).
+// Each tx will have the same outputs, using the amounts specified in output_values.
+static inline std::pair<CTransactionRef, CTransactionRef> make_two_siblings(const CTransactionRef parent,
+ const std::vector<CAmount>& output_values)
+{
+ assert(parent->vout.size() >= 2);
+
+ // First tx takes first parent output
+ CMutableTransaction tx1 = CMutableTransaction();
+ tx1.vin.resize(1);
+ tx1.vout.resize(output_values.size());
+
+ tx1.vin[0].prevout.hash = parent->GetHash();
+ tx1.vin[0].prevout.n = 0;
+ // Add a witness so wtxid != txid
+ CScriptWitness witness;
+ witness.stack.emplace_back(10);
+ tx1.vin[0].scriptWitness = witness;
+
+ for (size_t i = 0; i < output_values.size(); ++i) {
+ tx1.vout[i].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
+ tx1.vout[i].nValue = output_values[i];
+ }
+
+ // Second tx takes second parent output
+ CMutableTransaction tx2 = tx1;
+ tx2.vin[0].prevout.n = 1;
+
+ return std::make_pair(MakeTransactionRef(tx1), MakeTransactionRef(tx2));
+}
+
+static CTransactionRef add_descendants(const CTransactionRef& tx, int32_t num_descendants, CTxMemPool& pool)
EXCLUSIVE_LOCKS_REQUIRED(::cs_main, pool.cs)
{
AssertLockHeld(::cs_main);
@@ -50,6 +81,35 @@ static void add_descendants(const CTransactionRef& tx, int32_t num_descendants,
pool.addUnchecked(entry.FromTx(next_tx));
tx_to_spend = next_tx;
}
+ // Return last created tx
+ return tx_to_spend;
+}
+
+static CTransactionRef add_descendant_to_parents(const std::vector<CTransactionRef>& parents, CTxMemPool& pool)
+ EXCLUSIVE_LOCKS_REQUIRED(::cs_main, pool.cs)
+{
+ AssertLockHeld(::cs_main);
+ AssertLockHeld(pool.cs);
+ TestMemPoolEntryHelper entry;
+ // Assumes this isn't already spent in mempool
+ auto child_tx = make_tx(/*inputs=*/parents, /*output_values=*/{50 * CENT});
+ pool.addUnchecked(entry.FromTx(child_tx));
+ // Return last created tx
+ return child_tx;
+}
+
+// Makes two children for a single parent
+static std::pair<CTransactionRef, CTransactionRef> add_children_to_parent(const CTransactionRef parent, CTxMemPool& pool)
+ EXCLUSIVE_LOCKS_REQUIRED(::cs_main, pool.cs)
+{
+ AssertLockHeld(::cs_main);
+ AssertLockHeld(pool.cs);
+ TestMemPoolEntryHelper entry;
+ // Assumes this isn't already spent in mempool
+ auto children_tx = make_two_siblings(/*parent=*/parent, /*output_values=*/{50 * CENT});
+ pool.addUnchecked(entry.FromTx(children_tx.first));
+ pool.addUnchecked(entry.FromTx(children_tx.second));
+ return children_tx;
}
BOOST_FIXTURE_TEST_CASE(rbf_helper_functions, TestChain100Setup)
@@ -89,28 +149,51 @@ BOOST_FIXTURE_TEST_CASE(rbf_helper_functions, TestChain100Setup)
const auto tx8 = make_tx(/*inputs=*/ {m_coinbase_txns[4]}, /*output_values=*/ {999 * CENT});
pool.addUnchecked(entry.Fee(high_fee).FromTx(tx8));
- const auto entry1 = pool.GetIter(tx1->GetHash()).value();
- const auto entry2 = pool.GetIter(tx2->GetHash()).value();
- const auto entry3 = pool.GetIter(tx3->GetHash()).value();
- const auto entry4 = pool.GetIter(tx4->GetHash()).value();
- const auto entry5 = pool.GetIter(tx5->GetHash()).value();
- const auto entry6 = pool.GetIter(tx6->GetHash()).value();
- const auto entry7 = pool.GetIter(tx7->GetHash()).value();
- const auto entry8 = pool.GetIter(tx8->GetHash()).value();
-
- BOOST_CHECK_EQUAL(entry1->GetFee(), normal_fee);
- BOOST_CHECK_EQUAL(entry2->GetFee(), normal_fee);
- BOOST_CHECK_EQUAL(entry3->GetFee(), low_fee);
- BOOST_CHECK_EQUAL(entry4->GetFee(), high_fee);
- BOOST_CHECK_EQUAL(entry5->GetFee(), low_fee);
- BOOST_CHECK_EQUAL(entry6->GetFee(), low_fee);
- BOOST_CHECK_EQUAL(entry7->GetFee(), high_fee);
- BOOST_CHECK_EQUAL(entry8->GetFee(), high_fee);
-
- CTxMemPool::setEntries set_12_normal{entry1, entry2};
- CTxMemPool::setEntries set_34_cpfp{entry3, entry4};
- CTxMemPool::setEntries set_56_low{entry5, entry6};
- CTxMemPool::setEntries all_entries{entry1, entry2, entry3, entry4, entry5, entry6, entry7, entry8};
+ // Normal txs, will chain txns right before CheckConflictTopology test
+ const auto tx9 = make_tx(/*inputs=*/ {m_coinbase_txns[5]}, /*output_values=*/ {995 * CENT});
+ pool.addUnchecked(entry.Fee(normal_fee).FromTx(tx9));
+ const auto tx10 = make_tx(/*inputs=*/ {m_coinbase_txns[6]}, /*output_values=*/ {995 * CENT});
+ pool.addUnchecked(entry.Fee(normal_fee).FromTx(tx10));
+
+ // Will make these two parents of single child
+ const auto tx11 = make_tx(/*inputs=*/ {m_coinbase_txns[7]}, /*output_values=*/ {995 * CENT});
+ pool.addUnchecked(entry.Fee(normal_fee).FromTx(tx11));
+ const auto tx12 = make_tx(/*inputs=*/ {m_coinbase_txns[8]}, /*output_values=*/ {995 * CENT});
+ pool.addUnchecked(entry.Fee(normal_fee).FromTx(tx12));
+
+ // Will make two children of this single parent
+ const auto tx13 = make_tx(/*inputs=*/ {m_coinbase_txns[9]}, /*output_values=*/ {995 * CENT, 995 * CENT});
+ pool.addUnchecked(entry.Fee(normal_fee).FromTx(tx13));
+
+ const auto entry1_normal = pool.GetIter(tx1->GetHash()).value();
+ const auto entry2_normal = pool.GetIter(tx2->GetHash()).value();
+ const auto entry3_low = pool.GetIter(tx3->GetHash()).value();
+ const auto entry4_high = pool.GetIter(tx4->GetHash()).value();
+ const auto entry5_low = pool.GetIter(tx5->GetHash()).value();
+ const auto entry6_low_prioritised = pool.GetIter(tx6->GetHash()).value();
+ const auto entry7_high = pool.GetIter(tx7->GetHash()).value();
+ const auto entry8_high = pool.GetIter(tx8->GetHash()).value();
+ const auto entry9_unchained = pool.GetIter(tx9->GetHash()).value();
+ const auto entry10_unchained = pool.GetIter(tx10->GetHash()).value();
+ const auto entry11_unchained = pool.GetIter(tx11->GetHash()).value();
+ const auto entry12_unchained = pool.GetIter(tx12->GetHash()).value();
+ const auto entry13_unchained = pool.GetIter(tx13->GetHash()).value();
+
+ BOOST_CHECK_EQUAL(entry1_normal->GetFee(), normal_fee);
+ BOOST_CHECK_EQUAL(entry2_normal->GetFee(), normal_fee);
+ BOOST_CHECK_EQUAL(entry3_low->GetFee(), low_fee);
+ BOOST_CHECK_EQUAL(entry4_high->GetFee(), high_fee);
+ BOOST_CHECK_EQUAL(entry5_low->GetFee(), low_fee);
+ BOOST_CHECK_EQUAL(entry6_low_prioritised->GetFee(), low_fee);
+ BOOST_CHECK_EQUAL(entry7_high->GetFee(), high_fee);
+ BOOST_CHECK_EQUAL(entry8_high->GetFee(), high_fee);
+
+ CTxMemPool::setEntries set_12_normal{entry1_normal, entry2_normal};
+ CTxMemPool::setEntries set_34_cpfp{entry3_low, entry4_high};
+ CTxMemPool::setEntries set_56_low{entry5_low, entry6_low_prioritised};
+ CTxMemPool::setEntries set_78_high{entry7_high, entry8_high};
+ CTxMemPool::setEntries all_entries{entry1_normal, entry2_normal, entry3_low, entry4_high,
+ entry5_low, entry6_low_prioritised, entry7_high, entry8_high};
CTxMemPool::setEntries empty_set;
const auto unused_txid{GetRandHash()};
@@ -118,29 +201,29 @@ BOOST_FIXTURE_TEST_CASE(rbf_helper_functions, TestChain100Setup)
// Tests for PaysMoreThanConflicts
// These tests use feerate, not absolute fee.
BOOST_CHECK(PaysMoreThanConflicts(/*iters_conflicting=*/set_12_normal,
- /*replacement_feerate=*/CFeeRate(entry1->GetModifiedFee() + 1, entry1->GetTxSize() + 2),
+ /*replacement_feerate=*/CFeeRate(entry1_normal->GetModifiedFee() + 1, entry1_normal->GetTxSize() + 2),
/*txid=*/unused_txid).has_value());
// Replacement must be strictly greater than the originals.
- BOOST_CHECK(PaysMoreThanConflicts(set_12_normal, CFeeRate(entry1->GetModifiedFee(), entry1->GetTxSize()), unused_txid).has_value());
- BOOST_CHECK(PaysMoreThanConflicts(set_12_normal, CFeeRate(entry1->GetModifiedFee() + 1, entry1->GetTxSize()), unused_txid) == std::nullopt);
+ BOOST_CHECK(PaysMoreThanConflicts(set_12_normal, CFeeRate(entry1_normal->GetModifiedFee(), entry1_normal->GetTxSize()), unused_txid).has_value());
+ BOOST_CHECK(PaysMoreThanConflicts(set_12_normal, CFeeRate(entry1_normal->GetModifiedFee() + 1, entry1_normal->GetTxSize()), unused_txid) == std::nullopt);
// These tests use modified fees (including prioritisation), not base fees.
- BOOST_CHECK(PaysMoreThanConflicts({entry5}, CFeeRate(entry5->GetModifiedFee() + 1, entry5->GetTxSize()), unused_txid) == std::nullopt);
- BOOST_CHECK(PaysMoreThanConflicts({entry6}, CFeeRate(entry6->GetFee() + 1, entry6->GetTxSize()), unused_txid).has_value());
- BOOST_CHECK(PaysMoreThanConflicts({entry6}, CFeeRate(entry6->GetModifiedFee() + 1, entry6->GetTxSize()), unused_txid) == std::nullopt);
+ BOOST_CHECK(PaysMoreThanConflicts({entry5_low}, CFeeRate(entry5_low->GetModifiedFee() + 1, entry5_low->GetTxSize()), unused_txid) == std::nullopt);
+ BOOST_CHECK(PaysMoreThanConflicts({entry6_low_prioritised}, CFeeRate(entry6_low_prioritised->GetFee() + 1, entry6_low_prioritised->GetTxSize()), unused_txid).has_value());
+ BOOST_CHECK(PaysMoreThanConflicts({entry6_low_prioritised}, CFeeRate(entry6_low_prioritised->GetModifiedFee() + 1, entry6_low_prioritised->GetTxSize()), unused_txid) == std::nullopt);
// PaysMoreThanConflicts checks individual feerate, not ancestor feerate. This test compares
- // replacement_feerate and entry4's feerate, which are the same. The replacement_feerate is
- // considered too low even though entry4 has a low ancestor feerate.
- BOOST_CHECK(PaysMoreThanConflicts(set_34_cpfp, CFeeRate(entry4->GetModifiedFee(), entry4->GetTxSize()), unused_txid).has_value());
+ // replacement_feerate and entry4_high's feerate, which are the same. The replacement_feerate is
+ // considered too low even though entry4_high has a low ancestor feerate.
+ BOOST_CHECK(PaysMoreThanConflicts(set_34_cpfp, CFeeRate(entry4_high->GetModifiedFee(), entry4_high->GetTxSize()), unused_txid).has_value());
// Tests for EntriesAndTxidsDisjoint
BOOST_CHECK(EntriesAndTxidsDisjoint(empty_set, {tx1->GetHash()}, unused_txid) == std::nullopt);
BOOST_CHECK(EntriesAndTxidsDisjoint(set_12_normal, {tx3->GetHash()}, unused_txid) == std::nullopt);
- BOOST_CHECK(EntriesAndTxidsDisjoint({entry2}, {tx2->GetHash()}, unused_txid).has_value());
+ BOOST_CHECK(EntriesAndTxidsDisjoint({entry2_normal}, {tx2->GetHash()}, unused_txid).has_value());
BOOST_CHECK(EntriesAndTxidsDisjoint(set_12_normal, {tx1->GetHash()}, unused_txid).has_value());
BOOST_CHECK(EntriesAndTxidsDisjoint(set_12_normal, {tx2->GetHash()}, unused_txid).has_value());
// EntriesAndTxidsDisjoint does not calculate descendants of iters_conflicting; it uses whatever
- // the caller passed in. As such, no error is returned even though entry2 is a descendant of tx1.
- BOOST_CHECK(EntriesAndTxidsDisjoint({entry2}, {tx1->GetHash()}, unused_txid) == std::nullopt);
+ // the caller passed in. As such, no error is returned even though entry2_normal is a descendant of tx1.
+ BOOST_CHECK(EntriesAndTxidsDisjoint({entry2_normal}, {tx1->GetHash()}, unused_txid) == std::nullopt);
// Tests for PaysForRBF
const CFeeRate incremental_relay_feerate{DEFAULT_INCREMENTAL_RELAY_FEE};
@@ -163,8 +246,8 @@ BOOST_FIXTURE_TEST_CASE(rbf_helper_functions, TestChain100Setup)
BOOST_CHECK(PaysForRBF(low_fee, high_fee + 99999999, 99999999, incremental_relay_feerate, unused_txid) == std::nullopt);
// Tests for GetEntriesForConflicts
- CTxMemPool::setEntries all_parents{entry1, entry3, entry5, entry7, entry8};
- CTxMemPool::setEntries all_children{entry2, entry4, entry6};
+ CTxMemPool::setEntries all_parents{entry1_normal, entry3_low, entry5_low, entry7_high, entry8_high};
+ CTxMemPool::setEntries all_children{entry2_normal, entry4_high, entry6_low_prioritised};
const std::vector<CTransactionRef> parent_inputs({m_coinbase_txns[0], m_coinbase_txns[1], m_coinbase_txns[2],
m_coinbase_txns[3], m_coinbase_txns[4]});
const auto conflicts_with_parents = make_tx(parent_inputs, {50 * CENT});
@@ -215,15 +298,328 @@ BOOST_FIXTURE_TEST_CASE(rbf_helper_functions, TestChain100Setup)
BOOST_CHECK(HasNoNewUnconfirmed(/*tx=*/ *spends_unconfirmed.get(),
/*pool=*/ pool,
/*iters_conflicting=*/ all_entries) == std::nullopt);
- BOOST_CHECK(HasNoNewUnconfirmed(*spends_unconfirmed.get(), pool, {entry2}) == std::nullopt);
+ BOOST_CHECK(HasNoNewUnconfirmed(*spends_unconfirmed.get(), pool, {entry2_normal}) == std::nullopt);
BOOST_CHECK(HasNoNewUnconfirmed(*spends_unconfirmed.get(), pool, empty_set).has_value());
const auto spends_new_unconfirmed = make_tx({tx1, tx8}, {36 * CENT});
- BOOST_CHECK(HasNoNewUnconfirmed(*spends_new_unconfirmed.get(), pool, {entry2}).has_value());
+ BOOST_CHECK(HasNoNewUnconfirmed(*spends_new_unconfirmed.get(), pool, {entry2_normal}).has_value());
BOOST_CHECK(HasNoNewUnconfirmed(*spends_new_unconfirmed.get(), pool, all_entries).has_value());
const auto spends_conflicting_confirmed = make_tx({m_coinbase_txns[0], m_coinbase_txns[1]}, {45 * CENT});
- BOOST_CHECK(HasNoNewUnconfirmed(*spends_conflicting_confirmed.get(), pool, {entry1, entry3}) == std::nullopt);
+ BOOST_CHECK(HasNoNewUnconfirmed(*spends_conflicting_confirmed.get(), pool, {entry1_normal, entry3_low}) == std::nullopt);
+
+ // Tests for CheckConflictTopology
+
+ // Tx4 has 23 descendants
+ BOOST_CHECK_EQUAL(pool.CheckConflictTopology(set_34_cpfp).value(), strprintf("%s has 23 descendants, max 1 allowed", entry4_high->GetSharedTx()->GetHash().ToString()));
+
+ // No descendants yet
+ BOOST_CHECK(pool.CheckConflictTopology({entry9_unchained}) == std::nullopt);
+
+ // Add 1 descendant, still ok
+ add_descendants(tx9, 1, pool);
+ BOOST_CHECK(pool.CheckConflictTopology({entry9_unchained}) == std::nullopt);
+
+ // N direct conflicts; ok
+ BOOST_CHECK(pool.CheckConflictTopology({entry9_unchained, entry10_unchained, entry11_unchained}) == std::nullopt);
+
+ // Add 1 descendant, still ok, even if it's considered a direct conflict as well
+ const auto child_tx = add_descendants(tx10, 1, pool);
+ const auto entry10_child = pool.GetIter(child_tx->GetHash()).value();
+ BOOST_CHECK(pool.CheckConflictTopology({entry9_unchained, entry10_unchained, entry11_unchained}) == std::nullopt);
+ BOOST_CHECK(pool.CheckConflictTopology({entry9_unchained, entry10_unchained, entry11_unchained, entry10_child}) == std::nullopt);
+
+ // One more, size 3 cluster too much
+ const auto grand_child_tx = add_descendants(child_tx, 1, pool);
+ const auto entry10_grand_child = pool.GetIter(grand_child_tx->GetHash()).value();
+ BOOST_CHECK_EQUAL(pool.CheckConflictTopology({entry9_unchained, entry10_unchained, entry11_unchained}).value(), strprintf("%s has 2 descendants, max 1 allowed", entry10_unchained->GetSharedTx()->GetHash().ToString()));
+ // even if direct conflict is descendent itself
+ BOOST_CHECK_EQUAL(pool.CheckConflictTopology({entry9_unchained, entry10_grand_child, entry11_unchained}).value(), strprintf("%s has 2 ancestors, max 1 allowed", entry10_grand_child->GetSharedTx()->GetHash().ToString()));
+
+ // Make a single child from two singleton parents
+ const auto two_parent_child_tx = add_descendant_to_parents({tx11, tx12}, pool);
+ const auto entry_two_parent_child = pool.GetIter(two_parent_child_tx->GetHash()).value();
+ BOOST_CHECK_EQUAL(pool.CheckConflictTopology({entry11_unchained}).value(), strprintf("%s is not the only parent of child %s", entry11_unchained->GetSharedTx()->GetHash().ToString(), entry_two_parent_child->GetSharedTx()->GetHash().ToString()));
+ BOOST_CHECK_EQUAL(pool.CheckConflictTopology({entry12_unchained}).value(), strprintf("%s is not the only parent of child %s", entry12_unchained->GetSharedTx()->GetHash().ToString(), entry_two_parent_child->GetSharedTx()->GetHash().ToString()));
+ BOOST_CHECK_EQUAL(pool.CheckConflictTopology({entry_two_parent_child}).value(), strprintf("%s has 2 ancestors, max 1 allowed", entry_two_parent_child->GetSharedTx()->GetHash().ToString()));
+
+ // Single parent with two children, we will conflict with the siblings directly only
+ const auto two_siblings = add_children_to_parent(tx13, pool);
+ const auto entry_sibling_1 = pool.GetIter(two_siblings.first->GetHash()).value();
+ const auto entry_sibling_2 = pool.GetIter(two_siblings.second->GetHash()).value();
+ BOOST_CHECK_EQUAL(pool.CheckConflictTopology({entry_sibling_1}).value(), strprintf("%s is not the only child of parent %s", entry_sibling_1->GetSharedTx()->GetHash().ToString(), entry13_unchained->GetSharedTx()->GetHash().ToString()));
+ BOOST_CHECK_EQUAL(pool.CheckConflictTopology({entry_sibling_2}).value(), strprintf("%s is not the only child of parent %s", entry_sibling_2->GetSharedTx()->GetHash().ToString(), entry13_unchained->GetSharedTx()->GetHash().ToString()));
+
+}
+
+BOOST_FIXTURE_TEST_CASE(improves_feerate, TestChain100Setup)
+{
+ CTxMemPool& pool = *Assert(m_node.mempool);
+ LOCK2(::cs_main, pool.cs);
+ TestMemPoolEntryHelper entry;
+
+ const CAmount low_fee{CENT/100};
+ const CAmount normal_fee{CENT/10};
+
+ // low feerate parent with normal feerate child
+ const auto tx1 = make_tx(/*inputs=*/ {m_coinbase_txns[0]}, /*output_values=*/ {10 * COIN});
+ pool.addUnchecked(entry.Fee(low_fee).FromTx(tx1));
+ const auto tx2 = make_tx(/*inputs=*/ {tx1}, /*output_values=*/ {995 * CENT});
+ pool.addUnchecked(entry.Fee(normal_fee).FromTx(tx2));
+
+ const auto entry1 = pool.GetIter(tx1->GetHash()).value();
+ const auto tx1_fee = entry1->GetModifiedFee();
+ const auto tx1_size = entry1->GetTxSize();
+ const auto entry2 = pool.GetIter(tx2->GetHash()).value();
+ const auto tx2_fee = entry2->GetModifiedFee();
+ const auto tx2_size = entry2->GetTxSize();
+
+ // Now test ImprovesFeerateDiagram with various levels of "package rbf" feerates
+
+ // It doesn't improve itself
+ const auto res1 = ImprovesFeerateDiagram(pool, {entry1}, {entry1, entry2}, tx1_fee + tx2_fee, tx1_size + tx2_size);
+ BOOST_CHECK(res1.has_value());
+ BOOST_CHECK(res1.value().first == DiagramCheckError::FAILURE);
+ BOOST_CHECK(res1.value().second == "insufficient feerate: does not improve feerate diagram");
+
+ // With one more satoshi it does
+ BOOST_CHECK(ImprovesFeerateDiagram(pool, {entry1}, {entry1, entry2}, tx1_fee + tx2_fee + 1, tx1_size + tx2_size) == std::nullopt);
+
+ // With prioritisation of in-mempool conflicts, it affects the results of the comparison using the same args as just above
+ pool.PrioritiseTransaction(entry1->GetSharedTx()->GetHash(), /*nFeeDelta=*/1);
+ const auto res2 = ImprovesFeerateDiagram(pool, {entry1}, {entry1, entry2}, tx1_fee + tx2_fee + 1, tx1_size + tx2_size);
+ BOOST_CHECK(res2.has_value());
+ BOOST_CHECK(res2.value().first == DiagramCheckError::FAILURE);
+ BOOST_CHECK(res2.value().second == "insufficient feerate: does not improve feerate diagram");
+ pool.PrioritiseTransaction(entry1->GetSharedTx()->GetHash(), /*nFeeDelta=*/-1);
+
+ // With one less vB it does
+ BOOST_CHECK(ImprovesFeerateDiagram(pool, {entry1}, {entry1, entry2}, tx1_fee + tx2_fee, tx1_size + tx2_size - 1) == std::nullopt);
+
+ // Adding a grandchild makes the cluster size 3, which is uncalculable
+ const auto tx3 = make_tx(/*inputs=*/ {tx2}, /*output_values=*/ {995 * CENT});
+ pool.addUnchecked(entry.Fee(normal_fee).FromTx(tx3));
+ const auto res3 = ImprovesFeerateDiagram(pool, {entry1}, {entry1, entry2}, tx1_fee + tx2_fee + 1, tx1_size + tx2_size);
+ BOOST_CHECK(res3.has_value());
+ BOOST_CHECK(res3.value().first == DiagramCheckError::UNCALCULABLE);
+ BOOST_CHECK(res3.value().second == strprintf("%s has 2 descendants, max 1 allowed", tx1->GetHash().GetHex()));
+
+}
+
+BOOST_FIXTURE_TEST_CASE(calc_feerate_diagram_rbf, TestChain100Setup)
+{
+ CTxMemPool& pool = *Assert(m_node.mempool);
+ LOCK2(::cs_main, pool.cs);
+ TestMemPoolEntryHelper entry;
+
+ const CAmount low_fee{CENT/100};
+ const CAmount normal_fee{CENT/10};
+ const CAmount high_fee{CENT};
+
+ // low -> high -> medium fee transactions that would result in two chunks together since they
+ // are all same size
+ const auto low_tx = make_tx(/*inputs=*/ {m_coinbase_txns[0]}, /*output_values=*/ {10 * COIN});
+ pool.addUnchecked(entry.Fee(low_fee).FromTx(low_tx));
+
+ const auto entry_low = pool.GetIter(low_tx->GetHash()).value();
+ const auto low_size = entry_low->GetTxSize();
+
+ // Replacement of size 1
+ {
+ const auto replace_one{pool.CalculateChunksForRBF(/*replacement_fees=*/0, /*replacement_vsize=*/1, {entry_low}, {entry_low})};
+ BOOST_CHECK(replace_one.has_value());
+ std::vector<FeeFrac> expected_old_chunks{{low_fee, low_size}};
+ BOOST_CHECK(replace_one->first == expected_old_chunks);
+ std::vector<FeeFrac> expected_new_chunks{{0, 1}};
+ BOOST_CHECK(replace_one->second == expected_new_chunks);
+ }
+
+ // Non-zero replacement fee/size
+ {
+ const auto replace_one_fee{pool.CalculateChunksForRBF(/*replacement_fees=*/high_fee, /*replacement_vsize=*/low_size, {entry_low}, {entry_low})};
+ BOOST_CHECK(replace_one_fee.has_value());
+ std::vector<FeeFrac> expected_old_diagram{{low_fee, low_size}};
+ BOOST_CHECK(replace_one_fee->first == expected_old_diagram);
+ std::vector<FeeFrac> expected_new_diagram{{high_fee, low_size}};
+ BOOST_CHECK(replace_one_fee->second == expected_new_diagram);
+ }
+
+ // Add a second transaction to the cluster that will make a single chunk, to be evicted in the RBF
+ const auto high_tx = make_tx(/*inputs=*/ {low_tx}, /*output_values=*/ {995 * CENT});
+ pool.addUnchecked(entry.Fee(high_fee).FromTx(high_tx));
+ const auto entry_high = pool.GetIter(high_tx->GetHash()).value();
+ const auto high_size = entry_high->GetTxSize();
+
+ {
+ const auto replace_single_chunk{pool.CalculateChunksForRBF(/*replacement_fees=*/high_fee, /*replacement_vsize=*/low_size, {entry_low}, {entry_low, entry_high})};
+ BOOST_CHECK(replace_single_chunk.has_value());
+ std::vector<FeeFrac> expected_old_chunks{{low_fee + high_fee, low_size + high_size}};
+ BOOST_CHECK(replace_single_chunk->first == expected_old_chunks);
+ std::vector<FeeFrac> expected_new_chunks{{high_fee, low_size}};
+ BOOST_CHECK(replace_single_chunk->second == expected_new_chunks);
+ }
+
+ // Conflict with the 2nd tx, resulting in new diagram with three entries
+ {
+ const auto replace_cpfp_child{pool.CalculateChunksForRBF(/*replacement_fees=*/high_fee, /*replacement_vsize=*/low_size, {entry_high}, {entry_high})};
+ BOOST_CHECK(replace_cpfp_child.has_value());
+ std::vector<FeeFrac> expected_old_chunks{{low_fee + high_fee, low_size + high_size}};
+ BOOST_CHECK(replace_cpfp_child->first == expected_old_chunks);
+ std::vector<FeeFrac> expected_new_chunks{{high_fee, low_size}, {low_fee, low_size}};
+ BOOST_CHECK(replace_cpfp_child->second == expected_new_chunks);
+ }
+
+ // third transaction causes the topology check to fail
+ const auto normal_tx = make_tx(/*inputs=*/ {high_tx}, /*output_values=*/ {995 * CENT});
+ pool.addUnchecked(entry.Fee(normal_fee).FromTx(normal_tx));
+ const auto entry_normal = pool.GetIter(normal_tx->GetHash()).value();
+ const auto normal_size = entry_normal->GetTxSize();
+
+ {
+ const auto replace_too_large{pool.CalculateChunksForRBF(/*replacement_fees=*/normal_fee, /*replacement_vsize=*/normal_size, {entry_low}, {entry_low, entry_high, entry_normal})};
+ BOOST_CHECK(!replace_too_large.has_value());
+ BOOST_CHECK_EQUAL(util::ErrorString(replace_too_large).original, strprintf("%s has 2 descendants, max 1 allowed", low_tx->GetHash().GetHex()));
+ }
+
+ // Make a size 2 cluster that is itself two chunks; evict both txns
+ const auto high_tx_2 = make_tx(/*inputs=*/ {m_coinbase_txns[1]}, /*output_values=*/ {10 * COIN});
+ pool.addUnchecked(entry.Fee(high_fee).FromTx(high_tx_2));
+ const auto entry_high_2 = pool.GetIter(high_tx_2->GetHash()).value();
+ const auto high_size_2 = entry_high_2->GetTxSize();
+
+ const auto low_tx_2 = make_tx(/*inputs=*/ {high_tx_2}, /*output_values=*/ {9 * COIN});
+ pool.addUnchecked(entry.Fee(low_fee).FromTx(low_tx_2));
+ const auto entry_low_2 = pool.GetIter(low_tx_2->GetHash()).value();
+ const auto low_size_2 = entry_low_2->GetTxSize();
+
+ {
+ const auto replace_two_chunks_single_cluster{pool.CalculateChunksForRBF(/*replacement_fees=*/high_fee, /*replacement_vsize=*/low_size, {entry_high_2}, {entry_high_2, entry_low_2})};
+ BOOST_CHECK(replace_two_chunks_single_cluster.has_value());
+ std::vector<FeeFrac> expected_old_chunks{{high_fee, high_size_2}, {low_fee, low_size_2}};
+ BOOST_CHECK(replace_two_chunks_single_cluster->first == expected_old_chunks);
+ std::vector<FeeFrac> expected_new_chunks{{high_fee, low_size_2}};
+ BOOST_CHECK(replace_two_chunks_single_cluster->second == expected_new_chunks);
+ }
+
+ // You can have more than two direct conflicts if the there are multiple affected clusters, all of size 2 or less
+ const auto conflict_1 = make_tx(/*inputs=*/ {m_coinbase_txns[2]}, /*output_values=*/ {10 * COIN});
+ pool.addUnchecked(entry.Fee(low_fee).FromTx(conflict_1));
+ const auto conflict_1_entry = pool.GetIter(conflict_1->GetHash()).value();
+
+ const auto conflict_2 = make_tx(/*inputs=*/ {m_coinbase_txns[3]}, /*output_values=*/ {10 * COIN});
+ pool.addUnchecked(entry.Fee(low_fee).FromTx(conflict_2));
+ const auto conflict_2_entry = pool.GetIter(conflict_2->GetHash()).value();
+
+ const auto conflict_3 = make_tx(/*inputs=*/ {m_coinbase_txns[4]}, /*output_values=*/ {10 * COIN});
+ pool.addUnchecked(entry.Fee(low_fee).FromTx(conflict_3));
+ const auto conflict_3_entry = pool.GetIter(conflict_3->GetHash()).value();
+
+ {
+ const auto replace_multiple_clusters{pool.CalculateChunksForRBF(/*replacement_fees=*/high_fee, /*replacement_vsize=*/low_size, {conflict_1_entry, conflict_2_entry, conflict_3_entry}, {conflict_1_entry, conflict_2_entry, conflict_3_entry})};
+ BOOST_CHECK(replace_multiple_clusters.has_value());
+ BOOST_CHECK(replace_multiple_clusters->first.size() == 3);
+ BOOST_CHECK(replace_multiple_clusters->second.size() == 1);
+ }
+
+ // Add a child transaction to conflict_1 and make it cluster size 2, two chunks due to same feerate
+ const auto conflict_1_child = make_tx(/*inputs=*/{conflict_1}, /*output_values=*/ {995 * CENT});
+ pool.addUnchecked(entry.Fee(low_fee).FromTx(conflict_1_child));
+ const auto conflict_1_child_entry = pool.GetIter(conflict_1_child->GetHash()).value();
+
+ {
+ const auto replace_multiple_clusters_2{pool.CalculateChunksForRBF(/*replacement_fees=*/high_fee, /*replacement_vsize=*/low_size, {conflict_1_entry, conflict_2_entry, conflict_3_entry}, {conflict_1_entry, conflict_2_entry, conflict_3_entry, conflict_1_child_entry})};
+
+ BOOST_CHECK(replace_multiple_clusters_2.has_value());
+ BOOST_CHECK(replace_multiple_clusters_2->first.size() == 4);
+ BOOST_CHECK(replace_multiple_clusters_2->second.size() == 1);
+ }
+
+ // Add another descendant to conflict_1, making the cluster size > 2 should fail at this point.
+ const auto conflict_1_grand_child = make_tx(/*inputs=*/{conflict_1_child}, /*output_values=*/ {995 * CENT});
+ pool.addUnchecked(entry.Fee(high_fee).FromTx(conflict_1_grand_child));
+ const auto conflict_1_grand_child_entry = pool.GetIter(conflict_1_child->GetHash()).value();
+
+ {
+ const auto replace_cluster_size_3{pool.CalculateChunksForRBF(/*replacement_fees=*/high_fee, /*replacement_vsize=*/low_size, {conflict_1_entry, conflict_2_entry, conflict_3_entry}, {conflict_1_entry, conflict_2_entry, conflict_3_entry, conflict_1_child_entry, conflict_1_grand_child_entry})};
+
+ BOOST_CHECK(!replace_cluster_size_3.has_value());
+ BOOST_CHECK_EQUAL(util::ErrorString(replace_cluster_size_3).original, strprintf("%s has 2 descendants, max 1 allowed", conflict_1->GetHash().GetHex()));
+ }
+}
+
+BOOST_AUTO_TEST_CASE(feerate_chunks_utilities)
+{
+ // Sanity check the correctness of the feerate chunks comparison.
+
+ // A strictly better case.
+ std::vector<FeeFrac> old_chunks{{{950, 300}, {100, 100}}};
+ std::vector<FeeFrac> new_chunks{{{1000, 300}, {50, 100}}};
+
+ BOOST_CHECK(std::is_lt(CompareChunks(old_chunks, new_chunks)));
+ BOOST_CHECK(std::is_gt(CompareChunks(new_chunks, old_chunks)));
+
+ // Incomparable diagrams
+ old_chunks = {{950, 300}, {100, 100}};
+ new_chunks = {{1000, 300}, {0, 100}};
+
+ BOOST_CHECK(CompareChunks(old_chunks, new_chunks) == std::partial_ordering::unordered);
+ BOOST_CHECK(CompareChunks(new_chunks, old_chunks) == std::partial_ordering::unordered);
+
+ // Strictly better but smaller size.
+ old_chunks = {{950, 300}, {100, 100}};
+ new_chunks = {{1100, 300}};
+
+ BOOST_CHECK(std::is_lt(CompareChunks(old_chunks, new_chunks)));
+ BOOST_CHECK(std::is_gt(CompareChunks(new_chunks, old_chunks)));
+
+ // New diagram is strictly better due to the first chunk, even though
+ // second chunk contributes no fees
+ old_chunks = {{950, 300}, {100, 100}};
+ new_chunks = {{1100, 100}, {0, 100}};
+
+ BOOST_CHECK(std::is_lt(CompareChunks(old_chunks, new_chunks)));
+ BOOST_CHECK(std::is_gt(CompareChunks(new_chunks, old_chunks)));
+
+ // Feerate of first new chunk is better with, but second chunk is worse
+ old_chunks = {{950, 300}, {100, 100}};
+ new_chunks = {{750, 100}, {249, 250}, {151, 650}};
+
+ BOOST_CHECK(CompareChunks(old_chunks, new_chunks) == std::partial_ordering::unordered);
+ BOOST_CHECK(CompareChunks(new_chunks, old_chunks) == std::partial_ordering::unordered);
+
+ // If we make the second chunk slightly better, the new diagram now wins.
+ old_chunks = {{950, 300}, {100, 100}};
+ new_chunks = {{750, 100}, {250, 250}, {150, 150}};
+
+ BOOST_CHECK(std::is_lt(CompareChunks(old_chunks, new_chunks)));
+ BOOST_CHECK(std::is_gt(CompareChunks(new_chunks, old_chunks)));
+
+ // Identical diagrams, cannot be strictly better
+ old_chunks = {{950, 300}, {100, 100}};
+ new_chunks = {{950, 300}, {100, 100}};
+
+ BOOST_CHECK(std::is_eq(CompareChunks(old_chunks, new_chunks)));
+ BOOST_CHECK(std::is_eq(CompareChunks(new_chunks, old_chunks)));
+
+ // Same aggregate fee, but different total size (trigger single tail fee check step)
+ old_chunks = {{950, 300}, {100, 99}};
+ new_chunks = {{950, 300}, {100, 100}};
+
+ // No change in evaluation when tail check needed.
+ BOOST_CHECK(std::is_gt(CompareChunks(old_chunks, new_chunks)));
+ BOOST_CHECK(std::is_lt(CompareChunks(new_chunks, old_chunks)));
+
+ // Trigger multiple tail fee check steps
+ old_chunks = {{950, 300}, {100, 99}};
+ new_chunks = {{950, 300}, {100, 100}, {0, 1}, {0, 1}};
+
+ BOOST_CHECK(std::is_gt(CompareChunks(old_chunks, new_chunks)));
+ BOOST_CHECK(std::is_lt(CompareChunks(new_chunks, old_chunks)));
+
+ // Multiple tail fee check steps, unordered result
+ new_chunks = {{950, 300}, {100, 100}, {0, 1}, {0, 1}, {1, 1}};
+ BOOST_CHECK(CompareChunks(old_chunks, new_chunks) == std::partial_ordering::unordered);
+ BOOST_CHECK(CompareChunks(new_chunks, old_chunks) == std::partial_ordering::unordered);
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp
index 9926fdfdcb..acacb6257d 100644
--- a/src/test/rpc_tests.cpp
+++ b/src/test/rpc_tests.cpp
@@ -291,6 +291,7 @@ BOOST_AUTO_TEST_CASE(rpc_parse_monetary_values)
BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("1e-8")), COIN/100000000);
BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("0.1e-7")), COIN/100000000);
BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("0.01e-6")), COIN/100000000);
+ BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("0.00000000000000000000000000000000000001e+30")), 1);
BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("0.0000000000000000000000000000000000000000000000000000000000000000000000000001e+68")), COIN/100000000);
BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("10000000000000000000000000000000000000000000000000000000000000000e-64")), COIN);
BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("0.000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000e64")), COIN);
diff --git a/src/test/sanity_tests.cpp b/src/test/sanity_tests.cpp
index 451bc99d44..68f82b760c 100644
--- a/src/test/sanity_tests.cpp
+++ b/src/test/sanity_tests.cpp
@@ -4,7 +4,6 @@
#include <key.h>
#include <test/util/setup_common.h>
-#include <util/time.h>
#include <boost/test/unit_test.hpp>
@@ -13,7 +12,6 @@ BOOST_FIXTURE_TEST_SUITE(sanity_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(basic_sanity)
{
BOOST_CHECK_MESSAGE(ECC_InitSanityCheck() == true, "secp256k1 sanity test");
- BOOST_CHECK_MESSAGE(ChronoSanityCheck() == true, "chrono epoch test");
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/scheduler_tests.cpp b/src/test/scheduler_tests.cpp
index 3fb3378598..95e725de46 100644
--- a/src/test/scheduler_tests.cpp
+++ b/src/test/scheduler_tests.cpp
@@ -129,8 +129,8 @@ BOOST_AUTO_TEST_CASE(singlethreadedscheduler_ordered)
CScheduler scheduler;
// each queue should be well ordered with respect to itself but not other queues
- SingleThreadedSchedulerClient queue1(scheduler);
- SingleThreadedSchedulerClient queue2(scheduler);
+ SerialTaskRunner queue1(scheduler);
+ SerialTaskRunner queue2(scheduler);
// create more threads than queues
// if the queues only permit execution of one task at once then
@@ -142,7 +142,7 @@ BOOST_AUTO_TEST_CASE(singlethreadedscheduler_ordered)
threads.emplace_back([&] { scheduler.serviceQueue(); });
}
- // these are not atomic, if SinglethreadedSchedulerClient prevents
+ // these are not atomic, if SerialTaskRunner prevents
// parallel execution at the queue level no synchronization should be required here
int counter1 = 0;
int counter2 = 0;
@@ -150,12 +150,12 @@ BOOST_AUTO_TEST_CASE(singlethreadedscheduler_ordered)
// just simply count up on each queue - if execution is properly ordered then
// the callbacks should run in exactly the order in which they were enqueued
for (int i = 0; i < 100; ++i) {
- queue1.AddToProcessQueue([i, &counter1]() {
+ queue1.insert([i, &counter1]() {
bool expectation = i == counter1++;
assert(expectation);
});
- queue2.AddToProcessQueue([i, &counter2]() {
+ queue2.insert([i, &counter2]() {
bool expectation = i == counter2++;
assert(expectation);
});
diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp
index ac457d9c77..e4142e203c 100644
--- a/src/test/script_tests.cpp
+++ b/src/test/script_tests.cpp
@@ -2,10 +2,6 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#if defined(HAVE_CONFIG_H)
-#include <config/bitcoin-config.h>
-#endif
-
#include <test/data/script_tests.json.h>
#include <test/data/bip341_wallet_vectors.json.h>
@@ -27,10 +23,6 @@
#include <util/fs.h>
#include <util/strencodings.h>
-#if defined(HAVE_CONSENSUS_LIB)
-#include <script/bitcoinconsensus.h>
-#endif
-
#include <cstdint>
#include <fstream>
#include <string>
@@ -143,21 +135,6 @@ void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, const CScript
if (combined_flags & SCRIPT_VERIFY_WITNESS && ~combined_flags & SCRIPT_VERIFY_P2SH) continue;
BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, &scriptWitness, combined_flags, MutableTransactionSignatureChecker(&tx, 0, txCredit.vout[0].nValue, MissingDataBehavior::ASSERT_FAIL), &err) == expect, message + strprintf(" (with flags %x)", combined_flags));
}
-
-#if defined(HAVE_CONSENSUS_LIB)
- DataStream stream;
- stream << TX_WITH_WITNESS(tx2);
- uint32_t libconsensus_flags{flags & bitcoinconsensus_SCRIPT_FLAGS_VERIFY_ALL};
- if (libconsensus_flags == flags) {
- int expectedSuccessCode = expect ? 1 : 0;
- if (flags & bitcoinconsensus_SCRIPT_FLAGS_VERIFY_WITNESS) {
- BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script_with_amount(scriptPubKey.data(), scriptPubKey.size(), txCredit.vout[0].nValue, UCharCast(stream.data()), stream.size(), 0, libconsensus_flags, nullptr) == expectedSuccessCode, message);
- } else {
- BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script_with_amount(scriptPubKey.data(), scriptPubKey.size(), 0, UCharCast(stream.data()), stream.size(), 0, libconsensus_flags, nullptr) == expectedSuccessCode, message);
- BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), UCharCast(stream.data()), stream.size(), 0, libconsensus_flags, nullptr) == expectedSuccessCode, message);
- }
- }
-#endif
}
void static NegateSignatureS(std::vector<unsigned char>& vchSig) {
@@ -1277,6 +1254,30 @@ BOOST_AUTO_TEST_CASE(script_combineSigs)
BOOST_CHECK(combined.scriptSig == partial3c);
}
+/**
+ * Reproduction of an exception incorrectly raised when parsing a public key inside a TapMiniscript.
+ */
+BOOST_AUTO_TEST_CASE(sign_invalid_miniscript)
+{
+ FillableSigningProvider keystore;
+ SignatureData sig_data;
+ CMutableTransaction prev, curr;
+
+ // Create a Taproot output which contains a leaf in which a non-32 bytes push is used where a public key is expected
+ // by the Miniscript parser. This offending Script was found by the RPC fuzzer.
+ const auto invalid_pubkey{ParseHex("173d36c8c9c9c9ffffffffffff0200000000021e1e37373721361818181818181e1e1e1e19000000000000000000b19292929292926b006c9b9b9292")};
+ TaprootBuilder builder;
+ builder.Add(0, {invalid_pubkey}, 0xc0);
+ XOnlyPubKey nums{ParseHex("50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0")};
+ builder.Finalize(nums);
+ prev.vout.emplace_back(0, GetScriptForDestination(builder.GetOutput()));
+ curr.vin.emplace_back(COutPoint{prev.GetHash(), 0});
+ sig_data.tr_spenddata = builder.GetSpendData();
+
+ // SignSignature can fail but it shouldn't raise an exception (nor crash).
+ BOOST_CHECK(!SignSignature(keystore, CTransaction(prev), curr, 0, SIGHASH_ALL, sig_data));
+}
+
BOOST_AUTO_TEST_CASE(script_standard_push)
{
ScriptError err;
@@ -1498,179 +1499,6 @@ static CScriptWitness ScriptWitnessFromJSON(const UniValue& univalue)
return scriptwitness;
}
-#if defined(HAVE_CONSENSUS_LIB)
-
-/* Test simple (successful) usage of bitcoinconsensus_verify_script */
-BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_returns_true)
-{
- unsigned int libconsensus_flags = 0;
- int nIn = 0;
-
- CScript scriptPubKey;
- CScript scriptSig;
- CScriptWitness wit;
-
- scriptPubKey << OP_1;
- CTransaction creditTx{BuildCreditingTransaction(scriptPubKey, 1)};
- CTransaction spendTx{BuildSpendingTransaction(scriptSig, wit, creditTx)};
-
- DataStream stream;
- stream << TX_WITH_WITNESS(spendTx);
-
- bitcoinconsensus_error err;
- int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), UCharCast(stream.data()), stream.size(), nIn, libconsensus_flags, &err);
- BOOST_CHECK_EQUAL(result, 1);
- BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_OK);
-}
-
-/* Test bitcoinconsensus_verify_script returns invalid tx index err*/
-BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_tx_index_err)
-{
- unsigned int libconsensus_flags = 0;
- int nIn = 3;
-
- CScript scriptPubKey;
- CScript scriptSig;
- CScriptWitness wit;
-
- scriptPubKey << OP_EQUAL;
- CTransaction creditTx{BuildCreditingTransaction(scriptPubKey, 1)};
- CTransaction spendTx{BuildSpendingTransaction(scriptSig, wit, creditTx)};
-
- DataStream stream;
- stream << TX_WITH_WITNESS(spendTx);
-
- bitcoinconsensus_error err;
- int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), UCharCast(stream.data()), stream.size(), nIn, libconsensus_flags, &err);
- BOOST_CHECK_EQUAL(result, 0);
- BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_TX_INDEX);
-}
-
-/* Test bitcoinconsensus_verify_script returns tx size mismatch err*/
-BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_tx_size)
-{
- unsigned int libconsensus_flags = 0;
- int nIn = 0;
-
- CScript scriptPubKey;
- CScript scriptSig;
- CScriptWitness wit;
-
- scriptPubKey << OP_EQUAL;
- CTransaction creditTx{BuildCreditingTransaction(scriptPubKey, 1)};
- CTransaction spendTx{BuildSpendingTransaction(scriptSig, wit, creditTx)};
-
- DataStream stream;
- stream << TX_WITH_WITNESS(spendTx);
-
- bitcoinconsensus_error err;
- int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), UCharCast(stream.data()), stream.size() * 2, nIn, libconsensus_flags, &err);
- BOOST_CHECK_EQUAL(result, 0);
- BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_TX_SIZE_MISMATCH);
-}
-
-/* Test bitcoinconsensus_verify_script returns invalid tx serialization error */
-BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_tx_serialization)
-{
- unsigned int libconsensus_flags = 0;
- int nIn = 0;
-
- CScript scriptPubKey;
- CScript scriptSig;
- CScriptWitness wit;
-
- scriptPubKey << OP_EQUAL;
- CTransaction creditTx{BuildCreditingTransaction(scriptPubKey, 1)};
- CTransaction spendTx{BuildSpendingTransaction(scriptSig, wit, creditTx)};
-
- DataStream stream;
- stream << 0xffffffff;
-
- bitcoinconsensus_error err;
- int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), UCharCast(stream.data()), stream.size(), nIn, libconsensus_flags, &err);
- BOOST_CHECK_EQUAL(result, 0);
- BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_TX_DESERIALIZE);
-}
-
-/* Test bitcoinconsensus_verify_script returns amount required error */
-BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_amount_required_err)
-{
- unsigned int libconsensus_flags = bitcoinconsensus_SCRIPT_FLAGS_VERIFY_WITNESS;
- int nIn = 0;
-
- CScript scriptPubKey;
- CScript scriptSig;
- CScriptWitness wit;
-
- scriptPubKey << OP_EQUAL;
- CTransaction creditTx{BuildCreditingTransaction(scriptPubKey, 1)};
- CTransaction spendTx{BuildSpendingTransaction(scriptSig, wit, creditTx)};
-
- DataStream stream;
- stream << TX_WITH_WITNESS(spendTx);
-
- bitcoinconsensus_error err;
- int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), UCharCast(stream.data()), stream.size(), nIn, libconsensus_flags, &err);
- BOOST_CHECK_EQUAL(result, 0);
- BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_AMOUNT_REQUIRED);
-}
-
-/* Test bitcoinconsensus_verify_script returns invalid flags err */
-BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_invalid_flags)
-{
- unsigned int libconsensus_flags = 1 << 3;
- int nIn = 0;
-
- CScript scriptPubKey;
- CScript scriptSig;
- CScriptWitness wit;
-
- scriptPubKey << OP_EQUAL;
- CTransaction creditTx{BuildCreditingTransaction(scriptPubKey, 1)};
- CTransaction spendTx{BuildSpendingTransaction(scriptSig, wit, creditTx)};
-
- DataStream stream;
- stream << TX_WITH_WITNESS(spendTx);
-
- bitcoinconsensus_error err;
- int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), UCharCast(stream.data()), stream.size(), nIn, libconsensus_flags, &err);
- BOOST_CHECK_EQUAL(result, 0);
- BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_INVALID_FLAGS);
-}
-
-/* Test bitcoinconsensus_verify_script returns spent outputs required err */
-BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_spent_outputs_required_err)
-{
- unsigned int libconsensus_flags{bitcoinconsensus_SCRIPT_FLAGS_VERIFY_TAPROOT};
- const int nIn{0};
-
- CScript scriptPubKey;
- CScript scriptSig;
- CScriptWitness wit;
-
- scriptPubKey << OP_EQUAL;
- CTransaction creditTx{BuildCreditingTransaction(scriptPubKey, 1)};
- CTransaction spendTx{BuildSpendingTransaction(scriptSig, wit, creditTx)};
-
- DataStream stream;
- stream << TX_WITH_WITNESS(spendTx);
-
- bitcoinconsensus_error err;
- int result{bitcoinconsensus_verify_script_with_spent_outputs(scriptPubKey.data(), scriptPubKey.size(), creditTx.vout[0].nValue, UCharCast(stream.data()), stream.size(), nullptr, 0, nIn, libconsensus_flags, &err)};
- BOOST_CHECK_EQUAL(result, 0);
- BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_SPENT_OUTPUTS_REQUIRED);
-
- result = bitcoinconsensus_verify_script_with_amount(scriptPubKey.data(), scriptPubKey.size(), creditTx.vout[0].nValue, UCharCast(stream.data()), stream.size(), nIn, libconsensus_flags, &err);
- BOOST_CHECK_EQUAL(result, 0);
- BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_SPENT_OUTPUTS_REQUIRED);
-
- result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), UCharCast(stream.data()), stream.size(), nIn, libconsensus_flags, &err);
- BOOST_CHECK_EQUAL(result, 0);
- BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_SPENT_OUTPUTS_REQUIRED);
-}
-
-#endif // defined(HAVE_CONSENSUS_LIB)
-
static std::vector<unsigned int> AllConsensusFlags()
{
std::vector<unsigned int> ret;
@@ -1718,28 +1546,12 @@ static void AssetTest(const UniValue& test)
txdata.Init(tx, std::vector<CTxOut>(prevouts));
CachingTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, true, txdata);
-#if defined(HAVE_CONSENSUS_LIB)
- DataStream stream;
- stream << TX_WITH_WITNESS(tx);
- std::vector<UTXO> utxos;
- utxos.resize(prevouts.size());
- for (size_t i = 0; i < prevouts.size(); i++) {
- utxos[i].scriptPubKey = prevouts[i].scriptPubKey.data();
- utxos[i].scriptPubKeySize = prevouts[i].scriptPubKey.size();
- utxos[i].value = prevouts[i].nValue;
- }
-#endif
-
for (const auto flags : ALL_CONSENSUS_FLAGS) {
// "final": true tests are valid for all flags. Others are only valid with flags that are
// a subset of test_flags.
if (fin || ((flags & test_flags) == flags)) {
bool ret = VerifyScript(tx.vin[idx].scriptSig, prevouts[idx].scriptPubKey, &tx.vin[idx].scriptWitness, flags, txcheck, nullptr);
BOOST_CHECK(ret);
-#if defined(HAVE_CONSENSUS_LIB)
- int lib_ret = bitcoinconsensus_verify_script_with_spent_outputs(prevouts[idx].scriptPubKey.data(), prevouts[idx].scriptPubKey.size(), prevouts[idx].nValue, UCharCast(stream.data()), stream.size(), utxos.data(), utxos.size(), idx, flags, nullptr);
- BOOST_CHECK(lib_ret == 1);
-#endif
}
}
}
@@ -1752,27 +1564,11 @@ static void AssetTest(const UniValue& test)
txdata.Init(tx, std::vector<CTxOut>(prevouts));
CachingTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, true, txdata);
-#if defined(HAVE_CONSENSUS_LIB)
- DataStream stream;
- stream << TX_WITH_WITNESS(tx);
- std::vector<UTXO> utxos;
- utxos.resize(prevouts.size());
- for (size_t i = 0; i < prevouts.size(); i++) {
- utxos[i].scriptPubKey = prevouts[i].scriptPubKey.data();
- utxos[i].scriptPubKeySize = prevouts[i].scriptPubKey.size();
- utxos[i].value = prevouts[i].nValue;
- }
-#endif
-
for (const auto flags : ALL_CONSENSUS_FLAGS) {
// If a test is supposed to fail with test_flags, it should also fail with any superset thereof.
if ((flags & test_flags) == test_flags) {
bool ret = VerifyScript(tx.vin[idx].scriptSig, prevouts[idx].scriptPubKey, &tx.vin[idx].scriptWitness, flags, txcheck, nullptr);
BOOST_CHECK(!ret);
-#if defined(HAVE_CONSENSUS_LIB)
- int lib_ret = bitcoinconsensus_verify_script_with_spent_outputs(prevouts[idx].scriptPubKey.data(), prevouts[idx].scriptPubKey.size(), prevouts[idx].nValue, UCharCast(stream.data()), stream.size(), utxos.data(), utxos.size(), idx, flags, nullptr);
- BOOST_CHECK(lib_ret == 0);
-#endif
}
}
}
diff --git a/src/test/serfloat_tests.cpp b/src/test/serfloat_tests.cpp
index b36bdc02ca..304541074f 100644
--- a/src/test/serfloat_tests.cpp
+++ b/src/test/serfloat_tests.cpp
@@ -37,6 +37,7 @@ uint64_t TestDouble(double f) {
} // namespace
BOOST_AUTO_TEST_CASE(double_serfloat_tests) {
+ // Test specific values against their expected encoding.
BOOST_CHECK_EQUAL(TestDouble(0.0), 0U);
BOOST_CHECK_EQUAL(TestDouble(-0.0), 0x8000000000000000);
BOOST_CHECK_EQUAL(TestDouble(std::numeric_limits<double>::infinity()), 0x7ff0000000000000U);
@@ -46,55 +47,76 @@ BOOST_AUTO_TEST_CASE(double_serfloat_tests) {
BOOST_CHECK_EQUAL(TestDouble(2.0), 0x4000000000000000ULL);
BOOST_CHECK_EQUAL(TestDouble(4.0), 0x4010000000000000ULL);
BOOST_CHECK_EQUAL(TestDouble(785.066650390625), 0x4088888880000000ULL);
+ BOOST_CHECK_EQUAL(TestDouble(3.7243058682384174), 0x400dcb60e0031440);
+ BOOST_CHECK_EQUAL(TestDouble(91.64070592566159), 0x4056e901536d447a);
+ BOOST_CHECK_EQUAL(TestDouble(-98.63087668642575), 0xc058a860489c007a);
+ BOOST_CHECK_EQUAL(TestDouble(4.908737756962054), 0x4013a28c268b2b70);
+ BOOST_CHECK_EQUAL(TestDouble(77.9247330021754), 0x40537b2ed3547804);
+ BOOST_CHECK_EQUAL(TestDouble(40.24732825357566), 0x40441fa873c43dfc);
+ BOOST_CHECK_EQUAL(TestDouble(71.39395607929222), 0x4051d936938f27b6);
+ BOOST_CHECK_EQUAL(TestDouble(58.80100710817612), 0x404d668766a2bd70);
+ BOOST_CHECK_EQUAL(TestDouble(-30.10665786964975), 0xc03e1b4dee1e01b8);
+ BOOST_CHECK_EQUAL(TestDouble(60.15231509068704), 0x404e137f0f969814);
+ BOOST_CHECK_EQUAL(TestDouble(-48.15848711335961), 0xc04814494e445bc6);
+ BOOST_CHECK_EQUAL(TestDouble(26.68450101125353), 0x403aaf3b755169b0);
+ BOOST_CHECK_EQUAL(TestDouble(-65.72071986604303), 0xc0506e2046378ede);
+ BOOST_CHECK_EQUAL(TestDouble(17.95575825512381), 0x4031f4ac92b0a388);
+ BOOST_CHECK_EQUAL(TestDouble(-35.27171863226279), 0xc041a2c7ad17a42a);
+ BOOST_CHECK_EQUAL(TestDouble(-8.58810329425124), 0xc0212d1bdffef538);
+ BOOST_CHECK_EQUAL(TestDouble(88.51393044338977), 0x405620e43c83b1c8);
+ BOOST_CHECK_EQUAL(TestDouble(48.07224932612732), 0x4048093f77466ffc);
+ BOOST_CHECK_EQUAL(TestDouble(9.867348871395659e+117), 0x586f4daeb2459b9f);
+ BOOST_CHECK_EQUAL(TestDouble(-1.5166424385129721e+206), 0xeabe3bbc484bd458);
+ BOOST_CHECK_EQUAL(TestDouble(-8.585156555624594e-275), 0x8707c76eee012429);
+ BOOST_CHECK_EQUAL(TestDouble(2.2794371091628822e+113), 0x5777b2184458f4ee);
+ BOOST_CHECK_EQUAL(TestDouble(-1.1290476594131867e+163), 0xe1c91893d3488bb0);
+ BOOST_CHECK_EQUAL(TestDouble(9.143848423979275e-246), 0x0d0ff76e5f2620a3);
+ BOOST_CHECK_EQUAL(TestDouble(-2.8366718125941117e+81), 0xd0d7ec7e754b394a);
+ BOOST_CHECK_EQUAL(TestDouble(-1.2754409481684012e+229), 0xef80d32f8ec55342);
+ BOOST_CHECK_EQUAL(TestDouble(6.000577060053642e-186), 0x197a1be7c8209b6a);
+ BOOST_CHECK_EQUAL(TestDouble(2.0839423284378986e-302), 0x014c94f8689cb0a5);
+ BOOST_CHECK_EQUAL(TestDouble(-1.422140051483753e+259), 0xf5bd99271d04bb35);
+ BOOST_CHECK_EQUAL(TestDouble(-1.0593973991188853e+46), 0xc97db0cdb72d1046);
+ BOOST_CHECK_EQUAL(TestDouble(2.62945125875249e+190), 0x67779b36366c993b);
+ BOOST_CHECK_EQUAL(TestDouble(-2.920377657275094e+115), 0xd7e7b7b45908e23b);
+ BOOST_CHECK_EQUAL(TestDouble(9.790289014855851e-118), 0x27a3c031cc428bcc);
+ BOOST_CHECK_EQUAL(TestDouble(-4.629317182034961e-114), 0xa866ccf0b753705a);
+ BOOST_CHECK_EQUAL(TestDouble(-1.7674605603846528e+279), 0xf9e8ed383ffc3e25);
+ BOOST_CHECK_EQUAL(TestDouble(2.5308171727712605e+120), 0x58ef5cd55f0ec997);
+ BOOST_CHECK_EQUAL(TestDouble(-1.05034156412799e+54), 0xcb25eea1b9350fa0);
- // Roundtrip test on IEC559-compatible systems
- if (std::numeric_limits<double>::is_iec559) {
- BOOST_CHECK_EQUAL(sizeof(double), 8U);
- BOOST_CHECK_EQUAL(sizeof(uint64_t), 8U);
- // Test extreme values
- TestDouble(std::numeric_limits<double>::min());
- TestDouble(-std::numeric_limits<double>::min());
- TestDouble(std::numeric_limits<double>::max());
- TestDouble(-std::numeric_limits<double>::max());
- TestDouble(std::numeric_limits<double>::lowest());
- TestDouble(-std::numeric_limits<double>::lowest());
- TestDouble(std::numeric_limits<double>::quiet_NaN());
- TestDouble(-std::numeric_limits<double>::quiet_NaN());
- TestDouble(std::numeric_limits<double>::signaling_NaN());
- TestDouble(-std::numeric_limits<double>::signaling_NaN());
- TestDouble(std::numeric_limits<double>::denorm_min());
- TestDouble(-std::numeric_limits<double>::denorm_min());
- // Test exact encoding: on currently supported platforms, EncodeDouble
- // should produce exactly the same as the in-memory representation for non-NaN.
- for (int j = 0; j < 1000; ++j) {
- // Iterate over 9 specific bits exhaustively; the others are chosen randomly.
- // These specific bits are the sign bit, and the 2 top and bottom bits of
- // exponent and mantissa in the IEEE754 binary64 format.
- for (int x = 0; x < 512; ++x) {
- uint64_t v = InsecureRandBits(64);
- v &= ~(uint64_t{1} << 0);
- if (x & 1) v |= (uint64_t{1} << 0);
- v &= ~(uint64_t{1} << 1);
- if (x & 2) v |= (uint64_t{1} << 1);
- v &= ~(uint64_t{1} << 50);
- if (x & 4) v |= (uint64_t{1} << 50);
- v &= ~(uint64_t{1} << 51);
- if (x & 8) v |= (uint64_t{1} << 51);
- v &= ~(uint64_t{1} << 52);
- if (x & 16) v |= (uint64_t{1} << 52);
- v &= ~(uint64_t{1} << 53);
- if (x & 32) v |= (uint64_t{1} << 53);
- v &= ~(uint64_t{1} << 61);
- if (x & 64) v |= (uint64_t{1} << 61);
- v &= ~(uint64_t{1} << 62);
- if (x & 128) v |= (uint64_t{1} << 62);
- v &= ~(uint64_t{1} << 63);
- if (x & 256) v |= (uint64_t{1} << 63);
- double f;
- memcpy(&f, &v, 8);
- uint64_t v2 = TestDouble(f);
- if (!std::isnan(f)) BOOST_CHECK_EQUAL(v, v2);
+ // Test extreme values
+ BOOST_CHECK_EQUAL(TestDouble(std::numeric_limits<double>::min()), 0x10000000000000);
+ BOOST_CHECK_EQUAL(TestDouble(-std::numeric_limits<double>::min()), 0x8010000000000000);
+ BOOST_CHECK_EQUAL(TestDouble(std::numeric_limits<double>::max()), 0x7fefffffffffffff);
+ BOOST_CHECK_EQUAL(TestDouble(-std::numeric_limits<double>::max()), 0xffefffffffffffff);
+ BOOST_CHECK_EQUAL(TestDouble(std::numeric_limits<double>::lowest()), 0xffefffffffffffff);
+ BOOST_CHECK_EQUAL(TestDouble(-std::numeric_limits<double>::lowest()), 0x7fefffffffffffff);
+ BOOST_CHECK_EQUAL(TestDouble(std::numeric_limits<double>::denorm_min()), 0x1);
+ BOOST_CHECK_EQUAL(TestDouble(-std::numeric_limits<double>::denorm_min()), 0x8000000000000001);
+ // Note that all NaNs are encoded the same way.
+ BOOST_CHECK_EQUAL(TestDouble(std::numeric_limits<double>::quiet_NaN()), 0x7ff8000000000000);
+ BOOST_CHECK_EQUAL(TestDouble(-std::numeric_limits<double>::quiet_NaN()), 0x7ff8000000000000);
+ BOOST_CHECK_EQUAL(TestDouble(std::numeric_limits<double>::signaling_NaN()), 0x7ff8000000000000);
+ BOOST_CHECK_EQUAL(TestDouble(-std::numeric_limits<double>::signaling_NaN()), 0x7ff8000000000000);
+
+ // Construct doubles to test from the encoding.
+ static_assert(sizeof(double) == 8);
+ static_assert(sizeof(uint64_t) == 8);
+ for (int j = 0; j < 1000; ++j) {
+ // Iterate over 9 specific bits exhaustively; the others are chosen randomly.
+ // These specific bits are the sign bit, and the 2 top and bottom bits of
+ // exponent and mantissa in the IEEE754 binary64 format.
+ for (int x = 0; x < 512; ++x) {
+ uint64_t v = InsecureRandBits(64);
+ int x_pos = 0;
+ for (int v_pos : {0, 1, 50, 51, 52, 53, 61, 62, 63}) {
+ v &= ~(uint64_t{1} << v_pos);
+ if ((x >> (x_pos++)) & 1) v |= (uint64_t{1} << v_pos);
}
+ double f;
+ memcpy(&f, &v, 8);
+ TestDouble(f);
}
}
}
diff --git a/src/test/system_tests.cpp b/src/test/system_tests.cpp
index 90fce9adf9..2de147deea 100644
--- a/src/test/system_tests.cpp
+++ b/src/test/system_tests.cpp
@@ -11,7 +11,7 @@
#include <univalue.h>
#ifdef ENABLE_EXTERNAL_SIGNER
-#include <boost/process.hpp>
+#include <util/subprocess.h>
#endif // ENABLE_EXTERNAL_SIGNER
#include <boost/test/unit_test.hpp>
@@ -34,20 +34,16 @@ BOOST_AUTO_TEST_CASE(run_command)
BOOST_CHECK(result.isNull());
}
{
- const UniValue result = RunCommandParseJSON("echo \"{\"success\": true}\"");
+ const UniValue result = RunCommandParseJSON("echo {\"success\": true}");
BOOST_CHECK(result.isObject());
const UniValue& success = result.find_value("success");
BOOST_CHECK(!success.isNull());
BOOST_CHECK_EQUAL(success.get_bool(), true);
}
{
- // An invalid command is handled by Boost
- const int expected_error{2};
- BOOST_CHECK_EXCEPTION(RunCommandParseJSON("invalid_command"), boost::process::process_error, [&](const boost::process::process_error& e) {
- BOOST_CHECK(std::string(e.what()).find("RunCommandParseJSON error:") == std::string::npos);
- BOOST_CHECK_EQUAL(e.code().value(), expected_error);
- return true;
- });
+ // An invalid command is handled by cpp-subprocess
+ const std::string expected{"execve failed: "};
+ BOOST_CHECK_EXCEPTION(RunCommandParseJSON("invalid_command"), subprocess::CalledProcessError, HasReason(expected));
}
{
// Return non-zero exit code, no output to stderr
diff --git a/src/test/txindex_tests.cpp b/src/test/txindex_tests.cpp
index e2432a4718..5a32b02ad9 100644
--- a/src/test/txindex_tests.cpp
+++ b/src/test/txindex_tests.cpp
@@ -71,7 +71,7 @@ BOOST_FIXTURE_TEST_CASE(txindex_initial_sync, TestChain100Setup)
// SyncWithValidationInterfaceQueue() call below is also needed to ensure
// TSAN always sees the test thread waiting for the notification thread, and
// avoid potential false positive reports.
- SyncWithValidationInterfaceQueue();
+ m_node.validation_signals->SyncWithValidationInterfaceQueue();
// shutdown sequence (c.f. Shutdown() in init.cpp)
txindex.Stop();
diff --git a/src/test/txpackage_tests.cpp b/src/test/txpackage_tests.cpp
index f6456526bb..b948ea8acb 100644
--- a/src/test/txpackage_tests.cpp
+++ b/src/test/txpackage_tests.cpp
@@ -132,7 +132,7 @@ BOOST_FIXTURE_TEST_CASE(package_validation_tests, TestChain100Setup)
/*output_amount=*/CAmount(48 * COIN), /*submit=*/false);
CTransactionRef tx_child = MakeTransactionRef(mtx_child);
Package package_parent_child{tx_parent, tx_child};
- const auto result_parent_child = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, package_parent_child, /*test_accept=*/true);
+ const auto result_parent_child = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, package_parent_child, /*test_accept=*/true, /*client_maxfeerate=*/{});
if (auto err_parent_child{CheckPackageMempoolAcceptResult(package_parent_child, result_parent_child, /*expect_valid=*/true, nullptr)}) {
BOOST_ERROR(err_parent_child.value());
} else {
@@ -151,7 +151,7 @@ BOOST_FIXTURE_TEST_CASE(package_validation_tests, TestChain100Setup)
CTransactionRef giant_ptx = create_placeholder_tx(999, 999);
BOOST_CHECK(GetVirtualTransactionSize(*giant_ptx) > DEFAULT_ANCESTOR_SIZE_LIMIT_KVB * 1000);
Package package_single_giant{giant_ptx};
- auto result_single_large = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, package_single_giant, /*test_accept=*/true);
+ auto result_single_large = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, package_single_giant, /*test_accept=*/true, /*client_maxfeerate=*/{});
if (auto err_single_large{CheckPackageMempoolAcceptResult(package_single_giant, result_single_large, /*expect_valid=*/false, nullptr)}) {
BOOST_ERROR(err_single_large.value());
} else {
@@ -275,7 +275,7 @@ BOOST_FIXTURE_TEST_CASE(package_submission_tests, TestChain100Setup)
package_unrelated.emplace_back(MakeTransactionRef(mtx));
}
auto result_unrelated_submit = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,
- package_unrelated, /*test_accept=*/false);
+ package_unrelated, /*test_accept=*/false, /*client_maxfeerate=*/{});
// We don't expect m_tx_results for each transaction when basic sanity checks haven't passed.
BOOST_CHECK(result_unrelated_submit.m_state.IsInvalid());
BOOST_CHECK_EQUAL(result_unrelated_submit.m_state.GetResult(), PackageValidationResult::PCKG_POLICY);
@@ -315,7 +315,7 @@ BOOST_FIXTURE_TEST_CASE(package_submission_tests, TestChain100Setup)
// 3 Generations is not allowed.
{
auto result_3gen_submit = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,
- package_3gen, /*test_accept=*/false);
+ package_3gen, /*test_accept=*/false, /*client_maxfeerate=*/{});
BOOST_CHECK(result_3gen_submit.m_state.IsInvalid());
BOOST_CHECK_EQUAL(result_3gen_submit.m_state.GetResult(), PackageValidationResult::PCKG_POLICY);
BOOST_CHECK_EQUAL(result_3gen_submit.m_state.GetRejectReason(), "package-not-child-with-parents");
@@ -332,7 +332,7 @@ BOOST_FIXTURE_TEST_CASE(package_submission_tests, TestChain100Setup)
CTransactionRef tx_parent_invalid = MakeTransactionRef(mtx_parent_invalid);
Package package_invalid_parent{tx_parent_invalid, tx_child};
auto result_quit_early = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,
- package_invalid_parent, /*test_accept=*/ false);
+ package_invalid_parent, /*test_accept=*/ false, /*client_maxfeerate=*/{});
if (auto err_parent_invalid{CheckPackageMempoolAcceptResult(package_invalid_parent, result_quit_early, /*expect_valid=*/false, m_node.mempool.get())}) {
BOOST_ERROR(err_parent_invalid.value());
} else {
@@ -353,7 +353,7 @@ BOOST_FIXTURE_TEST_CASE(package_submission_tests, TestChain100Setup)
package_missing_parent.push_back(MakeTransactionRef(mtx_child));
{
const auto result_missing_parent = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,
- package_missing_parent, /*test_accept=*/false);
+ package_missing_parent, /*test_accept=*/false, /*client_maxfeerate=*/{});
BOOST_CHECK(result_missing_parent.m_state.IsInvalid());
BOOST_CHECK_EQUAL(result_missing_parent.m_state.GetResult(), PackageValidationResult::PCKG_POLICY);
BOOST_CHECK_EQUAL(result_missing_parent.m_state.GetRejectReason(), "package-not-child-with-unconfirmed-parents");
@@ -363,7 +363,7 @@ BOOST_FIXTURE_TEST_CASE(package_submission_tests, TestChain100Setup)
// Submit package with parent + child.
{
const auto submit_parent_child = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,
- package_parent_child, /*test_accept=*/false);
+ package_parent_child, /*test_accept=*/false, /*client_maxfeerate=*/{});
expected_pool_size += 2;
BOOST_CHECK_MESSAGE(submit_parent_child.m_state.IsValid(),
"Package validation unexpectedly failed: " << submit_parent_child.m_state.GetRejectReason());
@@ -385,7 +385,7 @@ BOOST_FIXTURE_TEST_CASE(package_submission_tests, TestChain100Setup)
// Already-in-mempool transactions should be detected and de-duplicated.
{
const auto submit_deduped = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,
- package_parent_child, /*test_accept=*/false);
+ package_parent_child, /*test_accept=*/false, /*client_maxfeerate=*/{});
if (auto err_deduped{CheckPackageMempoolAcceptResult(package_parent_child, submit_deduped, /*expect_valid=*/true, m_node.mempool.get())}) {
BOOST_ERROR(err_deduped.value());
} else {
@@ -456,7 +456,7 @@ BOOST_FIXTURE_TEST_CASE(package_witness_swap_tests, TestChain100Setup)
{
Package package_parent_child1{ptx_parent, ptx_child1};
const auto submit_witness1 = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,
- package_parent_child1, /*test_accept=*/false);
+ package_parent_child1, /*test_accept=*/false, /*client_maxfeerate=*/{});
if (auto err_witness1{CheckPackageMempoolAcceptResult(package_parent_child1, submit_witness1, /*expect_valid=*/true, m_node.mempool.get())}) {
BOOST_ERROR(err_witness1.value());
}
@@ -464,7 +464,7 @@ BOOST_FIXTURE_TEST_CASE(package_witness_swap_tests, TestChain100Setup)
// Child2 would have been validated individually.
Package package_parent_child2{ptx_parent, ptx_child2};
const auto submit_witness2 = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,
- package_parent_child2, /*test_accept=*/false);
+ package_parent_child2, /*test_accept=*/false, /*client_maxfeerate=*/{});
if (auto err_witness2{CheckPackageMempoolAcceptResult(package_parent_child2, submit_witness2, /*expect_valid=*/true, m_node.mempool.get())}) {
BOOST_ERROR(err_witness2.value());
} else {
@@ -478,7 +478,7 @@ BOOST_FIXTURE_TEST_CASE(package_witness_swap_tests, TestChain100Setup)
// Deduplication should work when wtxid != txid. Submit package with the already-in-mempool
// transactions again, which should not fail.
const auto submit_segwit_dedup = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,
- package_parent_child1, /*test_accept=*/false);
+ package_parent_child1, /*test_accept=*/false, /*client_maxfeerate=*/{});
if (auto err_segwit_dedup{CheckPackageMempoolAcceptResult(package_parent_child1, submit_segwit_dedup, /*expect_valid=*/true, m_node.mempool.get())}) {
BOOST_ERROR(err_segwit_dedup.value());
} else {
@@ -508,7 +508,7 @@ BOOST_FIXTURE_TEST_CASE(package_witness_swap_tests, TestChain100Setup)
{
Package package_child2_grandchild{ptx_child2, ptx_grandchild};
const auto submit_spend_ignored = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,
- package_child2_grandchild, /*test_accept=*/false);
+ package_child2_grandchild, /*test_accept=*/false, /*client_maxfeerate=*/{});
if (auto err_spend_ignored{CheckPackageMempoolAcceptResult(package_child2_grandchild, submit_spend_ignored, /*expect_valid=*/true, m_node.mempool.get())}) {
BOOST_ERROR(err_spend_ignored.value());
} else {
@@ -606,7 +606,7 @@ BOOST_FIXTURE_TEST_CASE(package_witness_swap_tests, TestChain100Setup)
// parent3 should be accepted
// child should be accepted
{
- const auto mixed_result = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, package_mixed, false);
+ const auto mixed_result = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, package_mixed, false, /*client_maxfeerate=*/{});
if (auto err_mixed{CheckPackageMempoolAcceptResult(package_mixed, mixed_result, /*expect_valid=*/true, m_node.mempool.get())}) {
BOOST_ERROR(err_mixed.value());
} else {
@@ -670,7 +670,7 @@ BOOST_FIXTURE_TEST_CASE(package_cpfp_tests, TestChain100Setup)
{
BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);
const auto submit_cpfp_deprio = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,
- package_cpfp, /*test_accept=*/ false);
+ package_cpfp, /*test_accept=*/ false, /*client_maxfeerate=*/{});
if (auto err_cpfp_deprio{CheckPackageMempoolAcceptResult(package_cpfp, submit_cpfp_deprio, /*expect_valid=*/false, m_node.mempool.get())}) {
BOOST_ERROR(err_cpfp_deprio.value());
} else {
@@ -692,7 +692,7 @@ BOOST_FIXTURE_TEST_CASE(package_cpfp_tests, TestChain100Setup)
{
BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);
const auto submit_cpfp = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,
- package_cpfp, /*test_accept=*/ false);
+ package_cpfp, /*test_accept=*/ false, /*client_maxfeerate=*/{});
if (auto err_cpfp{CheckPackageMempoolAcceptResult(package_cpfp, submit_cpfp, /*expect_valid=*/true, m_node.mempool.get())}) {
BOOST_ERROR(err_cpfp.value());
} else {
@@ -744,7 +744,7 @@ BOOST_FIXTURE_TEST_CASE(package_cpfp_tests, TestChain100Setup)
// Cheap package should fail for being too low fee.
{
const auto submit_package_too_low = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,
- package_still_too_low, /*test_accept=*/false);
+ package_still_too_low, /*test_accept=*/false, /*client_maxfeerate=*/{});
if (auto err_package_too_low{CheckPackageMempoolAcceptResult(package_still_too_low, submit_package_too_low, /*expect_valid=*/false, m_node.mempool.get())}) {
BOOST_ERROR(err_package_too_low.value());
} else {
@@ -770,7 +770,7 @@ BOOST_FIXTURE_TEST_CASE(package_cpfp_tests, TestChain100Setup)
// Now that the child's fees have "increased" by 1 BTC, the cheap package should succeed.
{
const auto submit_prioritised_package = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,
- package_still_too_low, /*test_accept=*/false);
+ package_still_too_low, /*test_accept=*/false, /*client_maxfeerate=*/{});
if (auto err_prioritised{CheckPackageMempoolAcceptResult(package_still_too_low, submit_prioritised_package, /*expect_valid=*/true, m_node.mempool.get())}) {
BOOST_ERROR(err_prioritised.value());
} else {
@@ -818,7 +818,7 @@ BOOST_FIXTURE_TEST_CASE(package_cpfp_tests, TestChain100Setup)
{
BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);
const auto submit_rich_parent = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,
- package_rich_parent, /*test_accept=*/false);
+ package_rich_parent, /*test_accept=*/false, /*client_maxfeerate=*/{});
if (auto err_rich_parent{CheckPackageMempoolAcceptResult(package_rich_parent, submit_rich_parent, /*expect_valid=*/false, m_node.mempool.get())}) {
BOOST_ERROR(err_rich_parent.value());
} else {
diff --git a/src/test/txvalidation_tests.cpp b/src/test/txvalidation_tests.cpp
index e045949b43..95583b53bf 100644
--- a/src/test/txvalidation_tests.cpp
+++ b/src/test/txvalidation_tests.cpp
@@ -115,7 +115,9 @@ BOOST_FIXTURE_TEST_CASE(version3_tests, RegTestingSetup)
const auto expected_error_str{strprintf("non-v3 tx %s (wtxid=%s) cannot spend from v3 tx %s (wtxid=%s)",
tx_v2_from_v3->GetHash().ToString(), tx_v2_from_v3->GetWitnessHash().ToString(),
mempool_tx_v3->GetHash().ToString(), mempool_tx_v3->GetWitnessHash().ToString())};
- BOOST_CHECK(*SingleV3Checks(tx_v2_from_v3, *ancestors_v2_from_v3, empty_conflicts_set, GetVirtualTransactionSize(*tx_v2_from_v3)) == expected_error_str);
+ auto result_v2_from_v3{SingleV3Checks(tx_v2_from_v3, *ancestors_v2_from_v3, empty_conflicts_set, GetVirtualTransactionSize(*tx_v2_from_v3))};
+ BOOST_CHECK_EQUAL(result_v2_from_v3->first, expected_error_str);
+ BOOST_CHECK_EQUAL(result_v2_from_v3->second, nullptr);
Package package_v3_v2{mempool_tx_v3, tx_v2_from_v3};
BOOST_CHECK_EQUAL(*PackageV3Checks(tx_v2_from_v3, GetVirtualTransactionSize(*tx_v2_from_v3), package_v3_v2, empty_ancestors), expected_error_str);
@@ -130,8 +132,9 @@ BOOST_FIXTURE_TEST_CASE(version3_tests, RegTestingSetup)
const auto expected_error_str_2{strprintf("non-v3 tx %s (wtxid=%s) cannot spend from v3 tx %s (wtxid=%s)",
tx_v2_from_v2_and_v3->GetHash().ToString(), tx_v2_from_v2_and_v3->GetWitnessHash().ToString(),
mempool_tx_v3->GetHash().ToString(), mempool_tx_v3->GetWitnessHash().ToString())};
- BOOST_CHECK(*SingleV3Checks(tx_v2_from_v2_and_v3, *ancestors_v2_from_both, empty_conflicts_set, GetVirtualTransactionSize(*tx_v2_from_v2_and_v3))
- == expected_error_str_2);
+ auto result_v2_from_both{SingleV3Checks(tx_v2_from_v2_and_v3, *ancestors_v2_from_both, empty_conflicts_set, GetVirtualTransactionSize(*tx_v2_from_v2_and_v3))};
+ BOOST_CHECK_EQUAL(result_v2_from_both->first, expected_error_str_2);
+ BOOST_CHECK_EQUAL(result_v2_from_both->second, nullptr);
Package package_v3_v2_v2{mempool_tx_v3, mempool_tx_v2, tx_v2_from_v2_and_v3};
BOOST_CHECK_EQUAL(*PackageV3Checks(tx_v2_from_v2_and_v3, GetVirtualTransactionSize(*tx_v2_from_v2_and_v3), package_v3_v2_v2, empty_ancestors), expected_error_str_2);
@@ -147,7 +150,9 @@ BOOST_FIXTURE_TEST_CASE(version3_tests, RegTestingSetup)
const auto expected_error_str{strprintf("v3 tx %s (wtxid=%s) cannot spend from non-v3 tx %s (wtxid=%s)",
tx_v3_from_v2->GetHash().ToString(), tx_v3_from_v2->GetWitnessHash().ToString(),
mempool_tx_v2->GetHash().ToString(), mempool_tx_v2->GetWitnessHash().ToString())};
- BOOST_CHECK(*SingleV3Checks(tx_v3_from_v2, *ancestors_v3_from_v2, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_from_v2)) == expected_error_str);
+ auto result_v3_from_v2{SingleV3Checks(tx_v3_from_v2, *ancestors_v3_from_v2, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_from_v2))};
+ BOOST_CHECK_EQUAL(result_v3_from_v2->first, expected_error_str);
+ BOOST_CHECK_EQUAL(result_v3_from_v2->second, nullptr);
Package package_v2_v3{mempool_tx_v2, tx_v3_from_v2};
BOOST_CHECK_EQUAL(*PackageV3Checks(tx_v3_from_v2, GetVirtualTransactionSize(*tx_v3_from_v2), package_v2_v3, empty_ancestors), expected_error_str);
@@ -162,8 +167,9 @@ BOOST_FIXTURE_TEST_CASE(version3_tests, RegTestingSetup)
const auto expected_error_str_2{strprintf("v3 tx %s (wtxid=%s) cannot spend from non-v3 tx %s (wtxid=%s)",
tx_v3_from_v2_and_v3->GetHash().ToString(), tx_v3_from_v2_and_v3->GetWitnessHash().ToString(),
mempool_tx_v2->GetHash().ToString(), mempool_tx_v2->GetWitnessHash().ToString())};
- BOOST_CHECK(*SingleV3Checks(tx_v3_from_v2_and_v3, *ancestors_v3_from_both, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_from_v2_and_v3))
- == expected_error_str_2);
+ auto result_v3_from_both{SingleV3Checks(tx_v3_from_v2_and_v3, *ancestors_v3_from_both, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_from_v2_and_v3))};
+ BOOST_CHECK_EQUAL(result_v3_from_both->first, expected_error_str_2);
+ BOOST_CHECK_EQUAL(result_v3_from_both->second, nullptr);
// tx_v3_from_v2_and_v3 also violates V3_ANCESTOR_LIMIT.
const auto expected_error_str_3{strprintf("tx %s (wtxid=%s) would have too many ancestors",
@@ -215,8 +221,9 @@ BOOST_FIXTURE_TEST_CASE(version3_tests, RegTestingSetup)
BOOST_CHECK_EQUAL(ancestors->size(), 3);
const auto expected_error_str{strprintf("tx %s (wtxid=%s) would have too many ancestors",
tx_v3_multi_parent->GetHash().ToString(), tx_v3_multi_parent->GetWitnessHash().ToString())};
- BOOST_CHECK_EQUAL(*SingleV3Checks(tx_v3_multi_parent, *ancestors, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_multi_parent)),
- expected_error_str);
+ auto result{SingleV3Checks(tx_v3_multi_parent, *ancestors, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_multi_parent))};
+ BOOST_CHECK_EQUAL(result->first, expected_error_str);
+ BOOST_CHECK_EQUAL(result->second, nullptr);
BOOST_CHECK_EQUAL(*PackageV3Checks(tx_v3_multi_parent, GetVirtualTransactionSize(*tx_v3_multi_parent), package_multi_parents, empty_ancestors),
expected_error_str);
@@ -239,8 +246,9 @@ BOOST_FIXTURE_TEST_CASE(version3_tests, RegTestingSetup)
auto ancestors{pool.CalculateMemPoolAncestors(entry.FromTx(tx_v3_multi_gen), m_limits)};
const auto expected_error_str{strprintf("tx %s (wtxid=%s) would have too many ancestors",
tx_v3_multi_gen->GetHash().ToString(), tx_v3_multi_gen->GetWitnessHash().ToString())};
- BOOST_CHECK_EQUAL(*SingleV3Checks(tx_v3_multi_gen, *ancestors, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_multi_gen)),
- expected_error_str);
+ auto result{SingleV3Checks(tx_v3_multi_gen, *ancestors, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_multi_gen))};
+ BOOST_CHECK_EQUAL(result->first, expected_error_str);
+ BOOST_CHECK_EQUAL(result->second, nullptr);
// Middle tx is what triggers a failure for the grandchild:
BOOST_CHECK_EQUAL(*PackageV3Checks(middle_tx, GetVirtualTransactionSize(*middle_tx), package_multi_gen, empty_ancestors), expected_error_str);
@@ -256,8 +264,9 @@ BOOST_FIXTURE_TEST_CASE(version3_tests, RegTestingSetup)
auto ancestors{pool.CalculateMemPoolAncestors(entry.FromTx(tx_v3_child_big), m_limits)};
const auto expected_error_str{strprintf("v3 child tx %s (wtxid=%s) is too big: %u > %u virtual bytes",
tx_v3_child_big->GetHash().ToString(), tx_v3_child_big->GetWitnessHash().ToString(), vsize, V3_CHILD_MAX_VSIZE)};
- BOOST_CHECK_EQUAL(*SingleV3Checks(tx_v3_child_big, *ancestors, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_child_big)),
- expected_error_str);
+ auto result{SingleV3Checks(tx_v3_child_big, *ancestors, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_child_big))};
+ BOOST_CHECK_EQUAL(result->first, expected_error_str);
+ BOOST_CHECK_EQUAL(result->second, nullptr);
Package package_child_big{mempool_tx_v3, tx_v3_child_big};
BOOST_CHECK_EQUAL(*PackageV3Checks(tx_v3_child_big, GetVirtualTransactionSize(*tx_v3_child_big), package_child_big, empty_ancestors),
@@ -298,9 +307,10 @@ BOOST_FIXTURE_TEST_CASE(version3_tests, RegTestingSetup)
const auto expected_error_str{strprintf("v3 child tx %s (wtxid=%s) is too big: %u > %u virtual bytes",
tx_many_sigops->GetHash().ToString(), tx_many_sigops->GetWitnessHash().ToString(),
total_sigops * DEFAULT_BYTES_PER_SIGOP / WITNESS_SCALE_FACTOR, V3_CHILD_MAX_VSIZE)};
- BOOST_CHECK_EQUAL(*SingleV3Checks(tx_many_sigops, *ancestors, empty_conflicts_set,
- GetVirtualTransactionSize(*tx_many_sigops, /*nSigOpCost=*/total_sigops, /*bytes_per_sigop=*/ DEFAULT_BYTES_PER_SIGOP)),
- expected_error_str);
+ auto result{SingleV3Checks(tx_many_sigops, *ancestors, empty_conflicts_set,
+ GetVirtualTransactionSize(*tx_many_sigops, /*nSigOpCost=*/total_sigops, /*bytes_per_sigop=*/ DEFAULT_BYTES_PER_SIGOP))};
+ BOOST_CHECK_EQUAL(result->first, expected_error_str);
+ BOOST_CHECK_EQUAL(result->second, nullptr);
Package package_child_sigops{mempool_tx_v3, tx_many_sigops};
BOOST_CHECK_EQUAL(*PackageV3Checks(tx_many_sigops, total_sigops * DEFAULT_BYTES_PER_SIGOP / WITNESS_SCALE_FACTOR, package_child_sigops, empty_ancestors),
@@ -319,22 +329,58 @@ BOOST_FIXTURE_TEST_CASE(version3_tests, RegTestingSetup)
BOOST_CHECK(PackageV3Checks(tx_mempool_v3_child, GetVirtualTransactionSize(*tx_mempool_v3_child), package_v3_1p1c, empty_ancestors) == std::nullopt);
}
- // A v3 transaction cannot have more than 1 descendant.
- // Configuration where tx has multiple direct children.
+ // A v3 transaction cannot have more than 1 descendant. Sibling is returned when exactly 1 exists.
{
auto tx_v3_child2 = make_tx({COutPoint{mempool_tx_v3->GetHash(), 1}}, /*version=*/3);
- auto ancestors{pool.CalculateMemPoolAncestors(entry.FromTx(tx_v3_child2), m_limits)};
+
+ // Configuration where parent already has 1 other child in mempool
+ auto ancestors_1sibling{pool.CalculateMemPoolAncestors(entry.FromTx(tx_v3_child2), m_limits)};
const auto expected_error_str{strprintf("tx %s (wtxid=%s) would exceed descendant count limit",
mempool_tx_v3->GetHash().ToString(), mempool_tx_v3->GetWitnessHash().ToString())};
- BOOST_CHECK_EQUAL(*SingleV3Checks(tx_v3_child2, *ancestors, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_child2)),
- expected_error_str);
- // If replacing the child, make sure there is no double-counting.
- BOOST_CHECK(SingleV3Checks(tx_v3_child2, *ancestors, {tx_mempool_v3_child->GetHash()}, GetVirtualTransactionSize(*tx_v3_child2))
+ auto result_with_sibling_eviction{SingleV3Checks(tx_v3_child2, *ancestors_1sibling, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_child2))};
+ BOOST_CHECK_EQUAL(result_with_sibling_eviction->first, expected_error_str);
+ // The other mempool child is returned to allow for sibling eviction.
+ BOOST_CHECK_EQUAL(result_with_sibling_eviction->second, tx_mempool_v3_child);
+
+ // If directly replacing the child, make sure there is no double-counting.
+ BOOST_CHECK(SingleV3Checks(tx_v3_child2, *ancestors_1sibling, {tx_mempool_v3_child->GetHash()}, GetVirtualTransactionSize(*tx_v3_child2))
== std::nullopt);
Package package_v3_1p2c{mempool_tx_v3, tx_mempool_v3_child, tx_v3_child2};
BOOST_CHECK_EQUAL(*PackageV3Checks(tx_v3_child2, GetVirtualTransactionSize(*tx_v3_child2), package_v3_1p2c, empty_ancestors),
expected_error_str);
+
+ // Configuration where parent already has 2 other children in mempool (no sibling eviction allowed). This may happen as the result of a reorg.
+ pool.addUnchecked(entry.FromTx(tx_v3_child2));
+ auto tx_v3_child3 = make_tx({COutPoint{mempool_tx_v3->GetHash(), 24}}, /*version=*/3);
+ auto entry_mempool_parent = pool.GetIter(mempool_tx_v3->GetHash().ToUint256()).value();
+ BOOST_CHECK_EQUAL(entry_mempool_parent->GetCountWithDescendants(), 3);
+ auto ancestors_2siblings{pool.CalculateMemPoolAncestors(entry.FromTx(tx_v3_child3), m_limits)};
+
+ auto result_2children{SingleV3Checks(tx_v3_child3, *ancestors_2siblings, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_child3))};
+ BOOST_CHECK_EQUAL(result_2children->first, expected_error_str);
+ // The other mempool child is not returned because sibling eviction is not allowed.
+ BOOST_CHECK_EQUAL(result_2children->second, nullptr);
+ }
+
+ // Sibling eviction: parent already has 1 other child, which also has its own child (no sibling eviction allowed). This may happen as the result of a reorg.
+ {
+ auto tx_mempool_grandparent = make_tx(random_outpoints(1), /*version=*/3);
+ auto tx_mempool_sibling = make_tx({COutPoint{tx_mempool_grandparent->GetHash(), 0}}, /*version=*/3);
+ auto tx_mempool_nibling = make_tx({COutPoint{tx_mempool_sibling->GetHash(), 0}}, /*version=*/3);
+ auto tx_to_submit = make_tx({COutPoint{tx_mempool_grandparent->GetHash(), 1}}, /*version=*/3);
+
+ pool.addUnchecked(entry.FromTx(tx_mempool_grandparent));
+ pool.addUnchecked(entry.FromTx(tx_mempool_sibling));
+ pool.addUnchecked(entry.FromTx(tx_mempool_nibling));
+
+ auto ancestors_3gen{pool.CalculateMemPoolAncestors(entry.FromTx(tx_to_submit), m_limits)};
+ const auto expected_error_str{strprintf("tx %s (wtxid=%s) would exceed descendant count limit",
+ tx_mempool_grandparent->GetHash().ToString(), tx_mempool_grandparent->GetWitnessHash().ToString())};
+ auto result_3gen{SingleV3Checks(tx_to_submit, *ancestors_3gen, empty_conflicts_set, GetVirtualTransactionSize(*tx_to_submit))};
+ BOOST_CHECK_EQUAL(result_3gen->first, expected_error_str);
+ // The other mempool child is not returned because sibling eviction is not allowed.
+ BOOST_CHECK_EQUAL(result_3gen->second, nullptr);
}
// Configuration where tx has multiple generations of descendants is not tested because that is
diff --git a/src/test/util/chainstate.h b/src/test/util/chainstate.h
index e2a88eacdd..ff95e64b7e 100644
--- a/src/test/util/chainstate.h
+++ b/src/test/util/chainstate.h
@@ -91,13 +91,16 @@ CreateAndActivateUTXOSnapshot(
// these blocks instead
CBlockIndex *pindex = orig_tip;
while (pindex && pindex != chain.m_chain.Tip()) {
- pindex->nStatus &= ~BLOCK_HAVE_DATA;
- pindex->nStatus &= ~BLOCK_HAVE_UNDO;
- // We have to set the ASSUMED_VALID flag, because otherwise it
- // would not be possible to have a block index entry without HAVE_DATA
- // and with nTx > 0 (since we aren't setting the pruned flag);
- // see CheckBlockIndex().
- pindex->nStatus |= BLOCK_ASSUMED_VALID;
+ // Remove all data and validity flags by just setting
+ // BLOCK_VALID_TREE. Also reset transaction counts and sequence
+ // ids that are set when blocks are received, to make test setup
+ // more realistic and satisfy consistency checks in
+ // CheckBlockIndex().
+ assert(pindex->IsValid(BlockStatus::BLOCK_VALID_TREE));
+ pindex->nStatus = BlockStatus::BLOCK_VALID_TREE;
+ pindex->nTx = 0;
+ pindex->nChainTx = 0;
+ pindex->nSequenceId = 0;
pindex = pindex->pprev;
}
}
diff --git a/src/test/util/mining.cpp b/src/test/util/mining.cpp
index 08d1b4c902..ad7a38d3fe 100644
--- a/src/test/util/mining.cpp
+++ b/src/test/util/mining.cpp
@@ -95,12 +95,12 @@ COutPoint MineBlock(const NodeContext& node, std::shared_ptr<CBlock>& block)
const auto old_height = WITH_LOCK(chainman.GetMutex(), return chainman.ActiveHeight());
bool new_block;
BlockValidationStateCatcher bvsc{block->GetHash()};
- RegisterValidationInterface(&bvsc);
+ node.validation_signals->RegisterValidationInterface(&bvsc);
const bool processed{chainman.ProcessNewBlock(block, true, true, &new_block)};
const bool duplicate{!new_block && processed};
assert(!duplicate);
- UnregisterValidationInterface(&bvsc);
- SyncWithValidationInterfaceQueue();
+ node.validation_signals->UnregisterValidationInterface(&bvsc);
+ node.validation_signals->SyncWithValidationInterfaceQueue();
const bool was_valid{bvsc.m_state && bvsc.m_state->IsValid()};
assert(old_height + was_valid == WITH_LOCK(chainman.GetMutex(), return chainman.ActiveHeight()));
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index 1f8dbac5be..38350b33cc 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -14,7 +14,6 @@
#include <banman.h>
#include <chainparams.h>
#include <common/system.h>
-#include <common/url.h>
#include <consensus/consensus.h>
#include <consensus/params.h>
#include <consensus/validation.h>
@@ -52,6 +51,7 @@
#include <txmempool.h>
#include <util/chaintype.h>
#include <util/check.h>
+#include <util/fs_helpers.h>
#include <util/rbf.h>
#include <util/strencodings.h>
#include <util/string.h>
@@ -80,7 +80,6 @@ using node::RegenerateCommitments;
using node::VerifyLoadedChainstate;
const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr;
-UrlDecodeFn* const URL_DECODE = nullptr;
/** Random context to get unique temp data dirs. Separate from g_insecure_rand_ctx, which can be seeded from a const env var */
static FastRandomContext g_insecure_rand_ctx_temp_path;
@@ -100,9 +99,22 @@ struct NetworkSetup
};
static NetworkSetup g_networksetup_instance;
+/** Register test-only arguments */
+static void SetupUnitTestArgs(ArgsManager& argsman)
+{
+ argsman.AddArg("-testdatadir", strprintf("Custom data directory (default: %s<random_string>)", fs::PathToString(fs::temp_directory_path() / "test_common_" PACKAGE_NAME / "")),
+ ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
+}
+
+/** Test setup failure */
+static void ExitFailure(std::string_view str_err)
+{
+ std::cerr << str_err << std::endl;
+ exit(EXIT_FAILURE);
+}
+
BasicTestingSetup::BasicTestingSetup(const ChainType chainType, const std::vector<const char*>& extra_args)
- : m_path_root{fs::temp_directory_path() / "test_common_" PACKAGE_NAME / g_insecure_rand_ctx_temp_path.rand256().ToString()},
- m_args{}
+ : m_args{}
{
m_node.shutdown = &m_interrupt;
m_node.args = &gArgs;
@@ -123,18 +135,49 @@ BasicTestingSetup::BasicTestingSetup(const ChainType chainType, const std::vecto
arguments = Cat(arguments, G_TEST_COMMAND_LINE_ARGUMENTS());
}
util::ThreadRename("test");
- fs::create_directories(m_path_root);
- m_args.ForceSetArg("-datadir", fs::PathToString(m_path_root));
- gArgs.ForceSetArg("-datadir", fs::PathToString(m_path_root));
gArgs.ClearPathCache();
{
SetupServerArgs(*m_node.args);
+ SetupUnitTestArgs(*m_node.args);
std::string error;
if (!m_node.args->ParseParameters(arguments.size(), arguments.data(), error)) {
m_node.args->ClearArgs();
throw std::runtime_error{error};
}
}
+
+ if (!m_node.args->IsArgSet("-testdatadir")) {
+ // By default, the data directory has a random name
+ const auto rand_str{g_insecure_rand_ctx_temp_path.rand256().ToString()};
+ m_path_root = fs::temp_directory_path() / "test_common_" PACKAGE_NAME / rand_str;
+ TryCreateDirectories(m_path_root);
+ } else {
+ // Custom data directory
+ m_has_custom_datadir = true;
+ fs::path root_dir{m_node.args->GetPathArg("-testdatadir")};
+ if (root_dir.empty()) ExitFailure("-testdatadir argument is empty, please specify a path");
+
+ root_dir = fs::absolute(root_dir);
+ const std::string test_path{G_TEST_GET_FULL_NAME ? G_TEST_GET_FULL_NAME() : ""};
+ m_path_lock = root_dir / "test_common_" PACKAGE_NAME / fs::PathFromString(test_path);
+ m_path_root = m_path_lock / "datadir";
+
+ // Try to obtain the lock; if unsuccessful don't disturb the existing test.
+ TryCreateDirectories(m_path_lock);
+ if (util::LockDirectory(m_path_lock, ".lock", /*probe_only=*/false) != util::LockResult::Success) {
+ ExitFailure("Cannot obtain a lock on test data lock directory " + fs::PathToString(m_path_lock) + '\n' + "The test executable is probably already running.");
+ }
+
+ // Always start with a fresh data directory; this doesn't delete the .lock file located one level above.
+ fs::remove_all(m_path_root);
+ if (!TryCreateDirectories(m_path_root)) ExitFailure("Cannot create test data directory");
+
+ // Print the test directory name if custom.
+ std::cout << "Test directory (will not be deleted): " << m_path_root << std::endl;
+ }
+ m_args.ForceSetArg("-datadir", fs::PathToString(m_path_root));
+ gArgs.ForceSetArg("-datadir", fs::PathToString(m_path_root));
+
SelectParams(chainType);
SeedInsecureRand();
if (G_TEST_LOG_FUN) LogInstance().PushBackCallback(G_TEST_LOG_FUN);
@@ -162,7 +205,13 @@ BasicTestingSetup::~BasicTestingSetup()
m_node.kernel.reset();
SetMockTime(0s); // Reset mocktime for following tests
LogInstance().DisconnectTestLogger();
- fs::remove_all(m_path_root);
+ if (m_has_custom_datadir) {
+ // Only remove the lock file, preserve the data directory.
+ UnlockDirectory(m_path_lock, ".lock");
+ fs::remove(m_path_lock / ".lock");
+ } else {
+ fs::remove_all(m_path_root);
+ }
gArgs.ClearArgs();
}
@@ -175,7 +224,7 @@ ChainTestingSetup::ChainTestingSetup(const ChainType chainType, const std::vecto
// from blocking due to queue overrun.
m_node.scheduler = std::make_unique<CScheduler>();
m_node.scheduler->m_service_thread = std::thread(util::TraceThread, "scheduler", [&] { m_node.scheduler->serviceQueue(); });
- GetMainSignals().RegisterBackgroundSignalScheduler(*m_node.scheduler);
+ m_node.validation_signals = std::make_unique<ValidationSignals>(std::make_unique<SerialTaskRunner>(*m_node.scheduler));
m_node.fee_estimator = std::make_unique<CBlockPolicyEstimator>(FeeestPath(*m_node.args), DEFAULT_ACCEPT_STALE_FEE_ESTIMATES);
m_node.mempool = std::make_unique<CTxMemPool>(MemPoolOptionsForTest(m_node));
@@ -189,6 +238,7 @@ ChainTestingSetup::ChainTestingSetup(const ChainType chainType, const std::vecto
.datadir = m_args.GetDataDirNet(),
.check_block_index = true,
.notifications = *m_node.notifications,
+ .signals = m_node.validation_signals.get(),
.worker_threads_num = 2,
};
const BlockManager::Options blockman_opts{
@@ -206,8 +256,7 @@ ChainTestingSetup::ChainTestingSetup(const ChainType chainType, const std::vecto
ChainTestingSetup::~ChainTestingSetup()
{
if (m_node.scheduler) m_node.scheduler->stop();
- GetMainSignals().FlushBackgroundCallbacks();
- GetMainSignals().UnregisterBackgroundSignalScheduler();
+ m_node.validation_signals->FlushBackgroundCallbacks();
m_node.connman.reset();
m_node.banman.reset();
m_node.addrman.reset();
@@ -216,6 +265,7 @@ ChainTestingSetup::~ChainTestingSetup()
m_node.mempool.reset();
m_node.fee_estimator.reset();
m_node.chainman.reset();
+ m_node.validation_signals.reset();
m_node.scheduler.reset();
}
diff --git a/src/test/util/setup_common.h b/src/test/util/setup_common.h
index 9ff4c372a5..8ccf9b571c 100644
--- a/src/test/util/setup_common.h
+++ b/src/test/util/setup_common.h
@@ -32,6 +32,9 @@ extern const std::function<void(const std::string&)> G_TEST_LOG_FUN;
/** Retrieve the command line arguments. */
extern const std::function<std::vector<const char*>()> G_TEST_COMMAND_LINE_ARGUMENTS;
+/** Retrieve the unit test name. */
+extern const std::function<std::string()> G_TEST_GET_FULL_NAME;
+
// Enable BOOST_CHECK_EQUAL for enum class types
namespace std {
template <typename T>
@@ -53,7 +56,9 @@ struct BasicTestingSetup {
explicit BasicTestingSetup(const ChainType chainType = ChainType::MAIN, const std::vector<const char*>& extra_args = {});
~BasicTestingSetup();
- const fs::path m_path_root;
+ fs::path m_path_root;
+ fs::path m_path_lock;
+ bool m_has_custom_datadir{false};
ArgsManager m_args;
};
diff --git a/src/test/util/txmempool.cpp b/src/test/util/txmempool.cpp
index 3b4161ddd3..71cf8aca60 100644
--- a/src/test/util/txmempool.cpp
+++ b/src/test/util/txmempool.cpp
@@ -22,6 +22,7 @@ CTxMemPool::Options MemPoolOptionsForTest(const NodeContext& node)
// Default to always checking mempool regardless of
// chainparams.DefaultConsistencyChecks for tests
.check_ratio = 1,
+ .signals = node.validation_signals.get(),
};
const auto result{ApplyArgsManOptions(*node.args, ::Params(), mempool_opts)};
Assert(result);
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index 47808a2a58..9a2add748e 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -290,13 +290,18 @@ BOOST_AUTO_TEST_CASE(util_TrimString)
BOOST_AUTO_TEST_CASE(util_FormatISO8601DateTime)
{
+ BOOST_CHECK_EQUAL(FormatISO8601DateTime(971890963199), "32767-12-31T23:59:59Z");
+ BOOST_CHECK_EQUAL(FormatISO8601DateTime(971890876800), "32767-12-31T00:00:00Z");
BOOST_CHECK_EQUAL(FormatISO8601DateTime(1317425777), "2011-09-30T23:36:17Z");
BOOST_CHECK_EQUAL(FormatISO8601DateTime(0), "1970-01-01T00:00:00Z");
}
BOOST_AUTO_TEST_CASE(util_FormatISO8601Date)
{
+ BOOST_CHECK_EQUAL(FormatISO8601Date(971890963199), "32767-12-31");
+ BOOST_CHECK_EQUAL(FormatISO8601Date(971890876800), "32767-12-31");
BOOST_CHECK_EQUAL(FormatISO8601Date(1317425777), "2011-09-30");
+ BOOST_CHECK_EQUAL(FormatISO8601Date(0), "1970-01-01");
}
BOOST_AUTO_TEST_CASE(util_FormatMoney)
diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp
index 35e5c6a037..69f4e305ab 100644
--- a/src/test/validation_block_tests.cpp
+++ b/src/test/validation_block_tests.cpp
@@ -127,6 +127,7 @@ std::shared_ptr<const CBlock> MinerTestingSetup::BadBlock(const uint256& prev_ha
return ret;
}
+// NOLINTNEXTLINE(misc-no-recursion)
void MinerTestingSetup::BuildChain(const uint256& root, int height, const unsigned int invalid_rate, const unsigned int branch_rate, const unsigned int max_size, std::vector<std::shared_ptr<const CBlock>>& blocks)
{
if (height <= 0 || blocks.size() >= max_size) return;
@@ -158,7 +159,7 @@ BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering)
bool ignored;
// Connect the genesis block and drain any outstanding events
BOOST_CHECK(Assert(m_node.chainman)->ProcessNewBlock(std::make_shared<CBlock>(Params().GenesisBlock()), true, true, &ignored));
- SyncWithValidationInterfaceQueue();
+ m_node.validation_signals->SyncWithValidationInterfaceQueue();
// subscribe to events (this subscriber will validate event ordering)
const CBlockIndex* initial_tip = nullptr;
@@ -167,7 +168,7 @@ BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering)
initial_tip = m_node.chainman->ActiveChain().Tip();
}
auto sub = std::make_shared<TestSubscriber>(initial_tip->GetBlockHash());
- RegisterSharedValidationInterface(sub);
+ m_node.validation_signals->RegisterSharedValidationInterface(sub);
// create a bunch of threads that repeatedly process a block generated above at random
// this will create parallelism and randomness inside validation - the ValidationInterface
@@ -196,9 +197,9 @@ BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering)
for (auto& t : threads) {
t.join();
}
- SyncWithValidationInterfaceQueue();
+ m_node.validation_signals->SyncWithValidationInterfaceQueue();
- UnregisterSharedValidationInterface(sub);
+ m_node.validation_signals->UnregisterSharedValidationInterface(sub);
LOCK(cs_main);
BOOST_CHECK_EQUAL(sub->m_expected_tip, m_node.chainman->ActiveChain().Tip()->GetBlockHash());
diff --git a/src/test/validation_chainstatemanager_tests.cpp b/src/test/validation_chainstatemanager_tests.cpp
index a33e71d50e..4bf66a55eb 100644
--- a/src/test/validation_chainstatemanager_tests.cpp
+++ b/src/test/validation_chainstatemanager_tests.cpp
@@ -102,7 +102,7 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager, TestChain100Setup)
BOOST_CHECK_EQUAL(active_tip2, c2.m_chain.Tip());
// Let scheduler events finish running to avoid accessing memory that is going to be unloaded
- SyncWithValidationInterfaceQueue();
+ m_node.validation_signals->SyncWithValidationInterfaceQueue();
}
//! Test rebalancing the caches associated with each chainstate.
@@ -276,9 +276,6 @@ struct SnapshotTestSetup : TestChain100Setup {
BOOST_CHECK_EQUAL(
*node::ReadSnapshotBaseBlockhash(found),
*chainman.SnapshotBlockhash());
-
- // Ensure that the genesis block was not marked assumed-valid.
- BOOST_CHECK(!chainman.ActiveChain().Genesis()->IsAssumedValid());
}
const auto& au_data = ::Params().AssumeutxoForHeight(snapshot_height);
@@ -374,7 +371,7 @@ struct SnapshotTestSetup : TestChain100Setup {
cs->ForceFlushStateToDisk();
}
// Process all callbacks referring to the old manager before wiping it.
- SyncWithValidationInterfaceQueue();
+ m_node.validation_signals->SyncWithValidationInterfaceQueue();
LOCK(::cs_main);
chainman.ResetChainstates();
BOOST_CHECK_EQUAL(chainman.GetAll().size(), 0);
@@ -383,6 +380,7 @@ struct SnapshotTestSetup : TestChain100Setup {
.chainparams = ::Params(),
.datadir = chainman.m_options.datadir,
.notifications = *m_node.notifications,
+ .signals = m_node.validation_signals.get(),
};
const BlockManager::Options blockman_opts{
.chainparams = chainman_opts.chainparams,
@@ -409,7 +407,7 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_activate_snapshot, SnapshotTestSetup)
//! - First, verify that setBlockIndexCandidates is as expected when using a single,
//! fully-validating chainstate.
//!
-//! - Then mark a region of the chain BLOCK_ASSUMED_VALID and introduce a second chainstate
+//! - Then mark a region of the chain as missing data and introduce a second chainstate
//! that will tolerate assumed-valid blocks. Run LoadBlockIndex() and ensure that the first
//! chainstate only contains fully validated blocks and the other chainstate contains all blocks,
//! except those marked assume-valid, because those entries don't HAVE_DATA.
@@ -420,7 +418,6 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_loadblockindex, TestChain100Setup)
Chainstate& cs1 = chainman.ActiveChainstate();
int num_indexes{0};
- int num_assumed_valid{0};
// Blocks in range [assumed_valid_start_idx, last_assumed_valid_idx) will be
// marked as assumed-valid and not having data.
const int expected_assumed_valid{20};
@@ -455,35 +452,30 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_loadblockindex, TestChain100Setup)
reload_all_block_indexes();
BOOST_CHECK_EQUAL(cs1.setBlockIndexCandidates.size(), 1);
- // Mark some region of the chain assumed-valid, and remove the HAVE_DATA flag.
+ // Reset some region of the chain's nStatus, removing the HAVE_DATA flag.
for (int i = 0; i <= cs1.m_chain.Height(); ++i) {
LOCK(::cs_main);
auto index = cs1.m_chain[i];
- // Blocks with heights in range [91, 110] are marked ASSUMED_VALID
+ // Blocks with heights in range [91, 110] are marked as missing data.
if (i < last_assumed_valid_idx && i >= assumed_valid_start_idx) {
- index->nStatus = BlockStatus::BLOCK_VALID_TREE | BlockStatus::BLOCK_ASSUMED_VALID;
+ index->nStatus = BlockStatus::BLOCK_VALID_TREE;
+ index->nTx = 0;
+ index->nChainTx = 0;
}
++num_indexes;
- if (index->IsAssumedValid()) ++num_assumed_valid;
// Note the last fully-validated block as the expected validated tip.
if (i == (assumed_valid_start_idx - 1)) {
validated_tip = index;
- BOOST_CHECK(!index->IsAssumedValid());
}
// Note the last assumed valid block as the snapshot base
if (i == last_assumed_valid_idx - 1) {
assumed_base = index;
- BOOST_CHECK(index->IsAssumedValid());
- } else if (i == last_assumed_valid_idx) {
- BOOST_CHECK(!index->IsAssumedValid());
}
}
- BOOST_CHECK_EQUAL(expected_assumed_valid, num_assumed_valid);
-
// Note: cs2's tip is not set when ActivateExistingSnapshot is called.
Chainstate& cs2 = WITH_LOCK(::cs_main,
return chainman.ActivateExistingSnapshot(*assumed_base->phashBlock));
diff --git a/src/test/validationinterface_tests.cpp b/src/test/validationinterface_tests.cpp
index 5979441057..a46cfc3029 100644
--- a/src/test/validationinterface_tests.cpp
+++ b/src/test/validationinterface_tests.cpp
@@ -28,7 +28,7 @@ BOOST_AUTO_TEST_CASE(unregister_validation_interface_race)
const CBlock block_dummy;
BlockValidationState state_dummy;
while (generate) {
- GetMainSignals().BlockChecked(block_dummy, state_dummy);
+ m_node.validation_signals->BlockChecked(block_dummy, state_dummy);
}
}};
@@ -37,8 +37,8 @@ BOOST_AUTO_TEST_CASE(unregister_validation_interface_race)
// keep going for about 1 sec, which is 250k iterations
for (int i = 0; i < 250000; i++) {
auto sub = std::make_shared<TestSubscriberNoop>();
- RegisterSharedValidationInterface(sub);
- UnregisterSharedValidationInterface(sub);
+ m_node.validation_signals->RegisterSharedValidationInterface(sub);
+ m_node.validation_signals->UnregisterSharedValidationInterface(sub);
}
// tell the other thread we are done
generate = false;
@@ -52,8 +52,8 @@ BOOST_AUTO_TEST_CASE(unregister_validation_interface_race)
class TestInterface : public CValidationInterface
{
public:
- TestInterface(std::function<void()> on_call = nullptr, std::function<void()> on_destroy = nullptr)
- : m_on_call(std::move(on_call)), m_on_destroy(std::move(on_destroy))
+ TestInterface(ValidationSignals& signals, std::function<void()> on_call = nullptr, std::function<void()> on_destroy = nullptr)
+ : m_on_call(std::move(on_call)), m_on_destroy(std::move(on_destroy)), m_signals{signals}
{
}
virtual ~TestInterface()
@@ -64,14 +64,15 @@ public:
{
if (m_on_call) m_on_call();
}
- static void Call()
+ void Call()
{
CBlock block;
BlockValidationState state;
- GetMainSignals().BlockChecked(block, state);
+ m_signals.BlockChecked(block, state);
}
std::function<void()> m_on_call;
std::function<void()> m_on_destroy;
+ ValidationSignals& m_signals;
};
// Regression test to ensure UnregisterAllValidationInterfaces calls don't
@@ -80,17 +81,23 @@ public:
BOOST_AUTO_TEST_CASE(unregister_all_during_call)
{
bool destroyed = false;
- RegisterSharedValidationInterface(std::make_shared<TestInterface>(
+ auto shared{std::make_shared<TestInterface>(
+ *m_node.validation_signals,
[&] {
// First call should decrements reference count 2 -> 1
- UnregisterAllValidationInterfaces();
+ m_node.validation_signals->UnregisterAllValidationInterfaces();
BOOST_CHECK(!destroyed);
// Second call should not decrement reference count 1 -> 0
- UnregisterAllValidationInterfaces();
+ m_node.validation_signals->UnregisterAllValidationInterfaces();
BOOST_CHECK(!destroyed);
},
- [&] { destroyed = true; }));
- TestInterface::Call();
+ [&] { destroyed = true; })};
+ m_node.validation_signals->RegisterSharedValidationInterface(shared);
+ BOOST_CHECK(shared.use_count() == 2);
+ shared->Call();
+ BOOST_CHECK(shared.use_count() == 1);
+ BOOST_CHECK(!destroyed);
+ shared.reset();
BOOST_CHECK(destroyed);
}