Branch data Line data Source code
1 : : // Copyright (c) 2023 The Bitcoin Core developers
2 : : // Distributed under the MIT software license, see the accompanying
3 : : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 : : //
5 : : #include <boost/test/unit_test.hpp>
6 : : #include <core_memusage.h>
7 : : #include <kernel/disconnected_transactions.h>
8 : : #include <test/util/setup_common.h>
9 : :
10 : : BOOST_FIXTURE_TEST_SUITE(disconnected_transactions, TestChain100Setup)
11 : :
12 : : //! Tests that DisconnectedBlockTransactions limits its own memory properly
13 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(disconnectpool_memory_limits)
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- ]
14 : : {
15 : : // Use the coinbase transactions from TestChain100Setup. It doesn't matter whether these
16 : : // transactions would realistically be in a block together, they just need distinct txids and
17 : : // uniform size for this test to work.
18 : 1 : std::vector<CTransactionRef> block_vtx(m_coinbase_txns);
19 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(block_vtx.size(), 100);
20 : :
21 : : // Roughly estimate sizes to sanity check that DisconnectedBlockTransactions::DynamicMemoryUsage
22 : : // is within an expected range.
23 : :
24 : : // Overhead for the hashmap depends on number of buckets
25 [ + - ]: 1 : std::unordered_map<uint256, CTransaction*, SaltedTxidHasher> temp_map;
26 [ + - ]: 1 : temp_map.reserve(1);
27 [ + - ]: 1 : const size_t MAP_1{memusage::DynamicUsage(temp_map)};
28 [ + - ]: 1 : temp_map.reserve(100);
29 [ + - ]: 1 : const size_t MAP_100{memusage::DynamicUsage(temp_map)};
30 : :
31 : 1 : const size_t TX_USAGE{RecursiveDynamicUsage(block_vtx.front())};
32 [ + + ]: 101 : for (const auto& tx : block_vtx)
33 [ + - + - ]: 100 : BOOST_CHECK_EQUAL(RecursiveDynamicUsage(tx), TX_USAGE);
34 : :
35 : : // Our overall formula is unordered map overhead + usage per entry.
36 : : // Implementations may vary, but we're trying to guess the usage of data structures.
37 : 1 : const size_t ENTRY_USAGE_ESTIMATE{
38 : 1 : TX_USAGE
39 : : // list entry: 2 pointers (next pointer and prev pointer) + element itself
40 : 1 : + memusage::MallocUsage((2 * sizeof(void*)) + sizeof(decltype(block_vtx)::value_type))
41 : : // unordered map: 1 pointer for the hashtable + key and value
42 : 1 : + memusage::MallocUsage(sizeof(void*) + sizeof(decltype(temp_map)::key_type)
43 : 1 : + sizeof(decltype(temp_map)::value_type))};
44 : :
45 : : // DisconnectedBlockTransactions that's just big enough for 1 transaction.
46 : 1 : {
47 [ + - ]: 1 : DisconnectedBlockTransactions disconnectpool{MAP_1 + ENTRY_USAGE_ESTIMATE};
48 : : // Add just 2 (and not all 100) transactions to keep the unordered map's hashtable overhead
49 : : // to a minimum and avoid all (instead of all but 1) transactions getting evicted.
50 [ + - + - : 5 : std::vector<CTransactionRef> two_txns({block_vtx.at(0), block_vtx.at(1)});
+ - + + +
- - - -
- ]
51 [ + - ]: 1 : auto evicted_txns{disconnectpool.AddTransactionsFromBlock(two_txns)};
52 [ + - + - : 2 : BOOST_CHECK(disconnectpool.DynamicMemoryUsage() <= MAP_1 + ENTRY_USAGE_ESTIMATE);
+ - + - ]
53 : :
54 : : // Only 1 transaction can be kept
55 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(1, evicted_txns.size());
56 : : // Transactions are added from back to front and eviction is FIFO.
57 [ + - + - : 1 : BOOST_CHECK_EQUAL(block_vtx.at(1), evicted_txns.front());
+ - ]
58 : :
59 [ + - ]: 1 : disconnectpool.clear();
60 : 1 : }
61 : :
62 : : // DisconnectedBlockTransactions with a comfortable maximum memory usage so that nothing is evicted.
63 : : // Record usage so we can check size limiting in the next test.
64 : 1 : size_t usage_full{0};
65 : 1 : {
66 : 1 : const size_t USAGE_100_OVERESTIMATE{MAP_100 + ENTRY_USAGE_ESTIMATE * 100};
67 [ + - ]: 1 : DisconnectedBlockTransactions disconnectpool{USAGE_100_OVERESTIMATE};
68 [ + - ]: 1 : auto evicted_txns{disconnectpool.AddTransactionsFromBlock(block_vtx)};
69 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(evicted_txns.size(), 0);
70 [ + - + - : 2 : BOOST_CHECK(disconnectpool.DynamicMemoryUsage() <= USAGE_100_OVERESTIMATE);
+ - + - ]
71 : :
72 [ + - ]: 1 : usage_full = disconnectpool.DynamicMemoryUsage();
73 : :
74 [ + - ]: 1 : disconnectpool.clear();
75 : 1 : }
76 : :
77 : : // DisconnectedBlockTransactions that's just a little too small for all of the transactions.
78 : 1 : {
79 : 1 : const size_t MAX_MEMUSAGE_99{usage_full - sizeof(void*)};
80 [ + - ]: 1 : DisconnectedBlockTransactions disconnectpool{MAX_MEMUSAGE_99};
81 [ + - ]: 1 : auto evicted_txns{disconnectpool.AddTransactionsFromBlock(block_vtx)};
82 [ + - + - : 2 : BOOST_CHECK(disconnectpool.DynamicMemoryUsage() <= MAX_MEMUSAGE_99);
+ - + - ]
83 : :
84 : : // Only 1 transaction needed to be evicted
85 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(1, evicted_txns.size());
86 : :
87 : : // Transactions are added from back to front and eviction is FIFO.
88 : : // The last transaction of block_vtx should be the first to be evicted.
89 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(block_vtx.back(), evicted_txns.front());
90 : :
91 [ + - ]: 1 : disconnectpool.clear();
92 : 1 : }
93 [ + - + - : 4 : }
- + - - -
- ]
94 : :
95 : : BOOST_AUTO_TEST_SUITE_END()
|