LCOV - code coverage report
Current view: top level - src/test - txrequest_tests.cpp (source / functions) Coverage Total Hit
Test: test_bitcoin_coverage.info Lines: 100.0 % 404 404
Test Date: 2024-11-04 04:45:35 Functions: 100.0 % 31 31
Branches: 59.6 % 674 402

             Branch data     Line data    Source code
       1                 :             : // Copyright (c) 2020-2021 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                 :             : 
       6                 :             : #include <txrequest.h>
       7                 :             : #include <uint256.h>
       8                 :             : 
       9                 :             : #include <test/util/random.h>
      10                 :             : #include <test/util/setup_common.h>
      11                 :             : 
      12                 :             : #include <algorithm>
      13                 :             : #include <functional>
      14                 :             : #include <vector>
      15                 :             : 
      16                 :             : #include <boost/test/unit_test.hpp>
      17                 :             : 
      18                 :             : namespace {
      19                 :             : 
      20                 :             : class Scenario;
      21                 :             : 
      22                 :           2 : struct TxRequestTest : BasicTestingSetup {
      23                 :             :     std::chrono::microseconds RandomTime8s();
      24                 :             :     std::chrono::microseconds RandomTime1y();
      25                 :             :     void BuildSingleTest(Scenario& scenario, int config);
      26                 :             :     void BuildPriorityTest(Scenario& scenario, int config);
      27                 :             :     void BuildBigPriorityTest(Scenario& scenario, int peers);
      28                 :             :     void BuildRequestOrderTest(Scenario& scenario, int config);
      29                 :             :     void BuildWtxidTest(Scenario& scenario, int config);
      30                 :             :     void BuildTimeBackwardsTest(Scenario& scenario);
      31                 :             :     void BuildWeirdRequestsTest(Scenario& scenario);
      32                 :             :     void TestInterleavedScenarios();
      33                 :             : };
      34                 :             : 
      35                 :             : constexpr std::chrono::microseconds MIN_TIME = std::chrono::microseconds::min();
      36                 :             : constexpr std::chrono::microseconds MAX_TIME = std::chrono::microseconds::max();
      37                 :             : constexpr std::chrono::microseconds MICROSECOND = std::chrono::microseconds{1};
      38                 :             : constexpr std::chrono::microseconds NO_TIME = std::chrono::microseconds{0};
      39                 :             : 
      40                 :             : /** An Action is a function to call at a particular (simulated) timestamp. */
      41                 :             : using Action = std::pair<std::chrono::microseconds, std::function<void()>>;
      42                 :             : 
      43                 :             : /** Object that stores actions from multiple interleaved scenarios, and data shared across them.
      44                 :             :  *
      45                 :             :  * The Scenario below is used to fill this.
      46                 :             :  */
      47                 :             : struct Runner
      48                 :             : {
      49                 :             :     /** The TxRequestTracker being tested. */
      50                 :             :     TxRequestTracker txrequest;
      51                 :             : 
      52                 :             :     /** List of actions to be executed (in order of increasing timestamp). */
      53                 :             :     std::vector<Action> actions;
      54                 :             : 
      55                 :             :     /** Which node ids have been assigned already (to prevent reuse). */
      56                 :             :     std::set<NodeId> peerset;
      57                 :             : 
      58                 :             :     /** Which txhashes have been assigned already (to prevent reuse). */
      59                 :             :     std::set<uint256> txhashset;
      60                 :             : 
      61                 :             :     /** Which (peer, gtxid) combinations are known to be expired. These need to be accumulated here instead of
      62                 :             :      *  checked directly in the GetRequestable return value to avoid introducing a dependency between the various
      63                 :             :      *  parallel tests. */
      64                 :             :     std::multiset<std::pair<NodeId, GenTxid>> expired;
      65                 :             : };
      66                 :             : 
      67                 :        8792 : std::chrono::microseconds TxRequestTest::RandomTime8s() { return std::chrono::microseconds{1 + m_rng.randbits(23)}; }
      68                 :           5 : std::chrono::microseconds TxRequestTest::RandomTime1y() { return std::chrono::microseconds{1 + m_rng.randbits(45)}; }
      69                 :             : 
      70                 :             : /** A proxy for a Runner that helps build a sequence of consecutive test actions on a TxRequestTracker.
      71                 :             :  *
      72                 :             :  * Each Scenario is a proxy through which actions for the (sequential) execution of various tests are added to a
      73                 :             :  * Runner. The actions from multiple scenarios are then run concurrently, resulting in these tests being performed
      74                 :             :  * against a TxRequestTracker in parallel. Every test has its own unique txhashes and NodeIds which are not
      75                 :             :  * reused in other tests, and thus they should be independent from each other. Running them in parallel however
      76                 :             :  * means that we verify the behavior (w.r.t. one test's txhashes and NodeIds) even when the state of the data
      77                 :             :  * structure is more complicated due to the presence of other tests.
      78                 :             :  */
      79                 :         225 : class Scenario
      80                 :             : {
      81                 :             :     FastRandomContext& m_rng;
      82                 :             :     Runner& m_runner;
      83                 :             :     std::chrono::microseconds m_now;
      84                 :             :     std::string m_testname;
      85                 :             : 
      86                 :             : public:
      87                 :         225 :     Scenario(FastRandomContext& rng, Runner& runner, std::chrono::microseconds starttime) : m_rng(rng), m_runner(runner), m_now(starttime) {}
      88                 :             : 
      89                 :             :     /** Set a name for the current test, to give more clear error messages. */
      90                 :        1600 :     void SetTestName(std::string testname)
      91                 :             :     {
      92                 :        1600 :         m_testname = std::move(testname);
      93                 :             :     }
      94                 :             : 
      95                 :             :     /** Advance this Scenario's time; this affects the timestamps newly scheduled events get. */
      96                 :        9981 :     void AdvanceTime(std::chrono::microseconds amount)
      97                 :             :     {
      98         [ -  + ]:        9981 :         assert(amount.count() >= 0);
      99                 :        9981 :         m_now += amount;
     100                 :        9981 :     }
     101                 :             : 
     102                 :             :     /** Schedule a ForgetTxHash call at the Scheduler's current time. */
     103                 :         400 :     void ForgetTxHash(const uint256& txhash)
     104                 :             :     {
     105                 :         400 :         auto& runner = m_runner;
     106                 :         400 :         runner.actions.emplace_back(m_now, [=,&runner]() {
     107                 :         400 :             runner.txrequest.ForgetTxHash(txhash);
     108                 :         400 :             runner.txrequest.SanityCheck();
     109                 :         400 :         });
     110                 :         400 :     }
     111                 :             : 
     112                 :             :     /** Schedule a ReceivedInv call at the Scheduler's current time. */
     113                 :        5600 :     void ReceivedInv(NodeId peer, const GenTxid& gtxid, bool pref, std::chrono::microseconds reqtime)
     114                 :             :     {
     115                 :        5600 :         auto& runner = m_runner;
     116                 :        5600 :         runner.actions.emplace_back(m_now, [=,&runner]() {
     117                 :        5600 :             runner.txrequest.ReceivedInv(peer, gtxid, pref, reqtime);
     118                 :        5600 :             runner.txrequest.SanityCheck();
     119                 :        5600 :         });
     120                 :        5600 :     }
     121                 :             : 
     122                 :             :     /** Schedule a DisconnectedPeer call at the Scheduler's current time. */
     123                 :        2242 :     void DisconnectedPeer(NodeId peer)
     124                 :             :     {
     125                 :        2242 :         auto& runner = m_runner;
     126                 :        2242 :         runner.actions.emplace_back(m_now, [=,&runner]() {
     127                 :        2242 :             runner.txrequest.DisconnectedPeer(peer);
     128                 :        2242 :             runner.txrequest.SanityCheck();
     129                 :        2242 :         });
     130                 :        2242 :     }
     131                 :             : 
     132                 :             :     /** Schedule a RequestedTx call at the Scheduler's current time. */
     133                 :        2960 :     void RequestedTx(NodeId peer, const uint256& txhash, std::chrono::microseconds exptime)
     134                 :             :     {
     135                 :        2960 :         auto& runner = m_runner;
     136                 :        2960 :         runner.actions.emplace_back(m_now, [=,&runner]() {
     137                 :        2960 :             runner.txrequest.RequestedTx(peer, txhash, exptime);
     138                 :        2960 :             runner.txrequest.SanityCheck();
     139                 :        2960 :         });
     140                 :        2960 :     }
     141                 :             : 
     142                 :             :     /** Schedule a ReceivedResponse call at the Scheduler's current time. */
     143                 :         958 :     void ReceivedResponse(NodeId peer, const uint256& txhash)
     144                 :             :     {
     145                 :         958 :         auto& runner = m_runner;
     146                 :         958 :         runner.actions.emplace_back(m_now, [=,&runner]() {
     147                 :         958 :             runner.txrequest.ReceivedResponse(peer, txhash);
     148                 :         958 :             runner.txrequest.SanityCheck();
     149                 :         958 :         });
     150                 :         958 :     }
     151                 :             : 
     152                 :             :     /** Schedule calls to verify the TxRequestTracker's state at the Scheduler's current time.
     153                 :             :      *
     154                 :             :      * @param peer       The peer whose state will be inspected.
     155                 :             :      * @param expected   The expected return value for GetRequestable(peer)
     156                 :             :      * @param candidates The expected return value CountCandidates(peer)
     157                 :             :      * @param inflight   The expected return value CountInFlight(peer)
     158                 :             :      * @param completed  The expected return value of Count(peer), minus candidates and inflight.
     159                 :             :      * @param checkname  An arbitrary string to include in error messages, for test identificatrion.
     160                 :             :      * @param offset     Offset with the current time to use (must be <= 0). This allows simulations of time going
     161                 :             :      *                   backwards (but note that the ordering of this event only follows the scenario's m_now.
     162                 :             :      */
     163                 :       29347 :     void Check(NodeId peer, const std::vector<GenTxid>& expected, size_t candidates, size_t inflight,
     164                 :             :         size_t completed, const std::string& checkname,
     165                 :             :         std::chrono::microseconds offset = std::chrono::microseconds{0})
     166                 :             :     {
     167         [ +  - ]:       29347 :         const auto comment = m_testname + " " + checkname;
     168                 :       29347 :         auto& runner = m_runner;
     169                 :       29347 :         const auto now = m_now;
     170         [ -  + ]:       29347 :         assert(offset.count() <= 0);
     171   [ +  -  +  -  :       58694 :         runner.actions.emplace_back(m_now, [=,&runner]() {
                   +  - ]
     172                 :       29347 :             std::vector<std::pair<NodeId, GenTxid>> expired_now;
     173         [ +  - ]:       29347 :             auto ret = runner.txrequest.GetRequestable(peer, now + offset, &expired_now);
     174   [ +  -  +  + ]:       30387 :             for (const auto& entry : expired_now) runner.expired.insert(entry);
     175         [ +  - ]:       29347 :             runner.txrequest.SanityCheck();
     176         [ +  - ]:       29347 :             runner.txrequest.PostGetRequestableSanityCheck(now + offset);
     177                 :       29347 :             size_t total = candidates + inflight + completed;
     178         [ +  - ]:       29347 :             size_t real_total = runner.txrequest.Count(peer);
     179         [ +  - ]:       29347 :             size_t real_candidates = runner.txrequest.CountCandidates(peer);
     180         [ +  - ]:       29347 :             size_t real_inflight = runner.txrequest.CountInFlight(peer);
     181   [ +  -  +  -  :       88041 :             BOOST_CHECK_MESSAGE(real_total == total, strprintf("[" + comment + "] total %i (%i expected)", real_total, total));
             +  -  +  - ]
     182   [ +  -  +  -  :       88041 :             BOOST_CHECK_MESSAGE(real_inflight == inflight, strprintf("[" + comment + "] inflight %i (%i expected)", real_inflight, inflight));
             +  -  +  - ]
     183   [ +  -  +  -  :       88041 :             BOOST_CHECK_MESSAGE(real_candidates == candidates, strprintf("[" + comment + "] candidates %i (%i expected)", real_candidates, candidates));
             +  -  +  - ]
     184   [ +  -  +  -  :       88041 :             BOOST_CHECK_MESSAGE(ret == expected, "[" + comment + "] mismatching requestables");
                   +  - ]
     185                 :       29347 :         });
     186                 :       29347 :     }
     187                 :             : 
     188                 :             :     /** Verify that an announcement for gtxid by peer has expired some time before this check is scheduled.
     189                 :             :      *
     190                 :             :      * Every expected expiration should be accounted for through exactly one call to this function.
     191                 :             :      */
     192                 :        1040 :     void CheckExpired(NodeId peer, GenTxid gtxid)
     193                 :             :     {
     194                 :        1040 :         const auto& testname = m_testname;
     195                 :        1040 :         auto& runner = m_runner;
     196         [ +  - ]:        2080 :         runner.actions.emplace_back(m_now, [=,&runner]() {
     197                 :        1040 :             auto it = runner.expired.find(std::pair<NodeId, GenTxid>{peer, gtxid});
     198   [ +  -  +  - ]:        3120 :             BOOST_CHECK_MESSAGE(it != runner.expired.end(), "[" + testname + "] missing expiration");
     199         [ +  - ]:        1040 :             if (it != runner.expired.end()) runner.expired.erase(it);
     200                 :        1040 :         });
     201                 :        1040 :     }
     202                 :             : 
     203                 :             :     /** Generate a random txhash, whose priorities for certain peers are constrained.
     204                 :             :      *
     205                 :             :      * For example, NewTxHash({{p1,p2,p3},{p2,p4,p5}}) will generate a txhash T such that both:
     206                 :             :      *  - priority(p1,T) > priority(p2,T) > priority(p3,T)
     207                 :             :      *  - priority(p2,T) > priority(p4,T) > priority(p5,T)
     208                 :             :      * where priority is the predicted internal TxRequestTracker's priority, assuming all announcements
     209                 :             :      * are within the same preferredness class.
     210                 :             :      */
     211                 :        2880 :     uint256 NewTxHash(const std::vector<std::vector<NodeId>>& orders = {})
     212                 :             :     {
     213                 :        2880 :         uint256 ret;
     214                 :      420594 :         bool ok;
     215                 :      420594 :         do {
     216                 :      420594 :             ret = m_rng.rand256();
     217                 :      420594 :             ok = true;
     218         [ +  + ]:      675213 :             for (const auto& order : orders) {
     219         [ +  + ]:      975128 :                 for (size_t pos = 1; pos < order.size(); ++pos) {
     220                 :      720509 :                     uint64_t prio_prev = m_runner.txrequest.ComputePriority(ret, order[pos - 1], true);
     221                 :      720509 :                     uint64_t prio_cur = m_runner.txrequest.ComputePriority(ret, order[pos], true);
     222         [ +  + ]:      720509 :                     if (prio_prev <= prio_cur) {
     223                 :             :                         ok = false;
     224                 :             :                         break;
     225                 :             :                     }
     226                 :             :                 }
     227         [ +  + ]:      672333 :                 if (!ok) break;
     228                 :             :             }
     229         [ +  + ]:      420594 :             if (ok) {
     230                 :        2880 :                 ok = m_runner.txhashset.insert(ret).second;
     231                 :             :             }
     232         [ +  + ]:      420594 :         } while(!ok);
     233                 :        2880 :         return ret;
     234                 :             :     }
     235                 :             : 
     236                 :             :     /** Generate a random GenTxid; the txhash follows NewTxHash; the is_wtxid flag is random. */
     237                 :        2560 :     GenTxid NewGTxid(const std::vector<std::vector<NodeId>>& orders = {})
     238                 :             :     {
     239         [ +  + ]:        2560 :         return m_rng.randbool() ? GenTxid::Wtxid(NewTxHash(orders)) : GenTxid::Txid(NewTxHash(orders));
     240                 :             :     }
     241                 :             : 
     242                 :             :     /** Generate a new random NodeId to use as peer. The same NodeId is never returned twice
     243                 :             :      *  (across all Scenarios combined). */
     244                 :        4640 :     NodeId NewPeer()
     245                 :             :     {
     246                 :        4640 :         bool ok;
     247                 :        4640 :         NodeId ret;
     248                 :        4640 :         do {
     249                 :        4640 :             ret = m_rng.randbits(63);
     250                 :        4640 :             ok = m_runner.peerset.insert(ret).second;
     251         [ -  + ]:        4640 :         } while(!ok);
     252                 :        4640 :         return ret;
     253                 :             :     }
     254                 :             : 
     255                 :        6186 :     std::chrono::microseconds Now() const { return m_now; }
     256                 :             : };
     257                 :             : 
     258                 :             : /** Add to scenario a test with a single tx announced by a single peer.
     259                 :             :  *
     260                 :             :  * config is an integer in [0, 32), which controls which variant of the test is used.
     261                 :             :  */
     262                 :         320 : void TxRequestTest::BuildSingleTest(Scenario& scenario, int config)
     263                 :             : {
     264                 :         320 :     auto peer = scenario.NewPeer();
     265         [ +  - ]:         320 :     auto gtxid = scenario.NewGTxid();
     266                 :         320 :     bool immediate = config & 1;
     267                 :         320 :     bool preferred = config & 2;
     268         [ +  + ]:         320 :     auto delay = immediate ? NO_TIME : RandomTime8s();
     269                 :             : 
     270                 :         320 :     scenario.SetTestName(strprintf("Single(config=%i)", config));
     271                 :             : 
     272                 :             :     // Receive an announcement, either immediately requestable or delayed.
     273         [ +  + ]:         320 :     scenario.ReceivedInv(peer, gtxid, preferred, immediate ? MIN_TIME : scenario.Now() + delay);
     274         [ +  + ]:         320 :     if (immediate) {
     275   [ +  -  +  - ]:         320 :         scenario.Check(peer, {gtxid}, 1, 0, 0, "s1");
     276                 :             :     } else {
     277         [ +  - ]:         320 :         scenario.Check(peer, {}, 1, 0, 0, "s2");
     278                 :         160 :         scenario.AdvanceTime(delay - MICROSECOND);
     279         [ +  - ]:         320 :         scenario.Check(peer, {}, 1, 0, 0, "s3");
     280                 :         160 :         scenario.AdvanceTime(MICROSECOND);
     281   [ +  -  +  - ]:         320 :         scenario.Check(peer, {gtxid}, 1, 0, 0, "s4");
     282                 :             :     }
     283                 :             : 
     284         [ +  + ]:         320 :     if (config >> 3) { // We'll request the transaction
     285                 :         240 :         scenario.AdvanceTime(RandomTime8s());
     286                 :         240 :         auto expiry = RandomTime8s();
     287   [ +  -  +  - ]:         480 :         scenario.Check(peer, {gtxid}, 1, 0, 0, "s5");
     288                 :         240 :         scenario.RequestedTx(peer, gtxid.GetHash(), scenario.Now() + expiry);
     289         [ +  - ]:         480 :         scenario.Check(peer, {}, 0, 1, 0, "s6");
     290                 :             : 
     291         [ +  + ]:         240 :         if ((config >> 3) == 1) { // The request will time out
     292                 :          80 :             scenario.AdvanceTime(expiry - MICROSECOND);
     293         [ +  - ]:         160 :             scenario.Check(peer, {}, 0, 1, 0, "s7");
     294                 :          80 :             scenario.AdvanceTime(MICROSECOND);
     295         [ +  - ]:         160 :             scenario.Check(peer, {}, 0, 0, 0, "s8");
     296                 :          80 :             scenario.CheckExpired(peer, gtxid);
     297                 :          80 :             return;
     298                 :             :         } else {
     299                 :         160 :             scenario.AdvanceTime(std::chrono::microseconds{m_rng.randrange(expiry.count())});
     300         [ +  - ]:         320 :             scenario.Check(peer, {}, 0, 1, 0, "s9");
     301         [ +  + ]:         160 :             if ((config >> 3) == 3) { // A response will arrive for the transaction
     302                 :          80 :                 scenario.ReceivedResponse(peer, gtxid.GetHash());
     303         [ +  - ]:         160 :                 scenario.Check(peer, {}, 0, 0, 0, "s10");
     304                 :          80 :                 return;
     305                 :             :             }
     306                 :             :         }
     307                 :             :     }
     308                 :             : 
     309         [ +  + ]:         160 :     if (config & 4) { // The peer will go offline
     310                 :          80 :         scenario.DisconnectedPeer(peer);
     311                 :             :     } else { // The transaction is no longer needed
     312                 :          80 :         scenario.ForgetTxHash(gtxid.GetHash());
     313                 :             :     }
     314         [ +  - ]:         320 :     scenario.Check(peer, {}, 0, 0, 0, "s11");
     315                 :             : }
     316                 :             : 
     317                 :             : /** Add to scenario a test with a single tx announced by two peers, to verify the
     318                 :             :  *  right peer is selected for requests.
     319                 :             :  *
     320                 :             :  * config is an integer in [0, 32), which controls which variant of the test is used.
     321                 :             :  */
     322                 :         320 : void TxRequestTest::BuildPriorityTest(Scenario& scenario, int config)
     323                 :             : {
     324                 :         320 :     scenario.SetTestName(strprintf("Priority(config=%i)", config));
     325                 :             : 
     326                 :             :     // Two peers. They will announce in order {peer1, peer2}.
     327                 :         320 :     auto peer1 = scenario.NewPeer(), peer2 = scenario.NewPeer();
     328                 :             :     // Construct a transaction that under random rules would be preferred by peer2 or peer1,
     329                 :             :     // depending on configuration.
     330                 :         320 :     bool prio1 = config & 1;
     331   [ +  +  +  -  :        1280 :     auto gtxid = prio1 ? scenario.NewGTxid({{peer1, peer2}}) : scenario.NewGTxid({{peer2, peer1}});
          +  -  +  -  +  
          -  +  +  +  +  
          +  +  +  +  +  
          +  -  -  -  -  
          -  -  -  -  -  
                      - ]
     332                 :         320 :     bool pref1 = config & 2, pref2 = config & 4;
     333                 :             : 
     334                 :         320 :     scenario.ReceivedInv(peer1, gtxid, pref1, MIN_TIME);
     335   [ +  -  +  - ]:         640 :     scenario.Check(peer1, {gtxid}, 1, 0, 0, "p1");
     336         [ +  + ]:         320 :     if (m_rng.randbool()) {
     337                 :         147 :         scenario.AdvanceTime(RandomTime8s());
     338   [ +  -  +  - ]:         294 :         scenario.Check(peer1, {gtxid}, 1, 0, 0, "p2");
     339                 :             :     }
     340                 :             : 
     341                 :         320 :     scenario.ReceivedInv(peer2, gtxid, pref2, MIN_TIME);
     342                 :         320 :     bool stage2_prio =
     343                 :             :         // At this point, peer2 will be given priority if:
     344                 :             :         // - It is preferred and peer1 is not
     345         [ +  + ]:         320 :         (pref2 && !pref1) ||
     346                 :             :         // - They're in the same preference class,
     347                 :             :         //   and the randomized priority favors peer2 over peer1.
     348         [ +  + ]:         240 :         (pref1 == pref2 && !prio1);
     349                 :         160 :     NodeId priopeer = stage2_prio ? peer2 : peer1, otherpeer = stage2_prio ? peer1 : peer2;
     350         [ +  - ]:         640 :     scenario.Check(otherpeer, {}, 1, 0, 0, "p3");
     351   [ +  -  +  - ]:         640 :     scenario.Check(priopeer, {gtxid}, 1, 0, 0, "p4");
     352         [ +  + ]:         320 :     if (m_rng.randbool()) scenario.AdvanceTime(RandomTime8s());
     353         [ +  - ]:         640 :     scenario.Check(otherpeer, {}, 1, 0, 0, "p5");
     354   [ +  -  +  - ]:         640 :     scenario.Check(priopeer, {gtxid}, 1, 0, 0, "p6");
     355                 :             : 
     356                 :             :     // We possibly request from the selected peer.
     357         [ +  + ]:         320 :     if (config & 8) {
     358                 :         160 :         scenario.RequestedTx(priopeer, gtxid.GetHash(), MAX_TIME);
     359         [ +  - ]:         320 :         scenario.Check(priopeer, {}, 0, 1, 0, "p7");
     360         [ +  - ]:         320 :         scenario.Check(otherpeer, {}, 1, 0, 0, "p8");
     361         [ +  + ]:         160 :         if (m_rng.randbool()) scenario.AdvanceTime(RandomTime8s());
     362                 :             :     }
     363                 :             : 
     364                 :             :     // The peer which was selected (or requested from) now goes offline, or a NOTFOUND is received from them.
     365         [ +  + ]:         320 :     if (config & 16) {
     366                 :         160 :         scenario.DisconnectedPeer(priopeer);
     367                 :             :     } else {
     368                 :         160 :         scenario.ReceivedResponse(priopeer, gtxid.GetHash());
     369                 :             :     }
     370         [ +  + ]:         320 :     if (m_rng.randbool()) scenario.AdvanceTime(RandomTime8s());
     371         [ +  - ]:         640 :     scenario.Check(priopeer, {}, 0, 0, !(config & 16), "p8");
     372   [ +  -  +  - ]:         640 :     scenario.Check(otherpeer, {gtxid}, 1, 0, 0, "p9");
     373         [ +  + ]:         320 :     if (m_rng.randbool()) scenario.AdvanceTime(RandomTime8s());
     374                 :             : 
     375                 :             :     // Now the other peer goes offline.
     376                 :         320 :     scenario.DisconnectedPeer(otherpeer);
     377         [ +  + ]:         320 :     if (m_rng.randbool()) scenario.AdvanceTime(RandomTime8s());
     378         [ +  - ]:         640 :     scenario.Check(peer1, {}, 0, 0, 0, "p10");
     379         [ +  - ]:         640 :     scenario.Check(peer2, {}, 0, 0, 0, "p11");
     380   [ +  -  +  - ]:         640 : }
     381                 :             : 
     382                 :             : /** Add to scenario a randomized test in which N peers announce the same transaction, to verify
     383                 :             :  *  the order in which they are requested. */
     384                 :         320 : void TxRequestTest::BuildBigPriorityTest(Scenario& scenario, int peers)
     385                 :             : {
     386                 :         320 :     scenario.SetTestName(strprintf("BigPriority(peers=%i)", peers));
     387                 :             : 
     388                 :             :     // We will have N peers announce the same transaction.
     389                 :         320 :     std::map<NodeId, bool> preferred;
     390                 :         320 :     std::vector<NodeId> pref_peers, npref_peers;
     391                 :         320 :     int num_pref = m_rng.randrange(peers + 1) ; // Some preferred, ...
     392                 :         320 :     int num_npref = peers - num_pref; // some not preferred.
     393         [ +  + ]:        1012 :     for (int i = 0; i < num_pref; ++i) {
     394   [ +  -  +  - ]:         692 :         pref_peers.push_back(scenario.NewPeer());
     395         [ +  - ]:         692 :         preferred[pref_peers.back()] = true;
     396                 :             :     }
     397         [ +  + ]:        1068 :     for (int i = 0; i < num_npref; ++i) {
     398   [ +  -  +  - ]:         748 :         npref_peers.push_back(scenario.NewPeer());
     399         [ +  - ]:         748 :         preferred[npref_peers.back()] = false;
     400                 :             :     }
     401                 :             :     // Make a list of all peers, in order of intended request order (concatenation of pref_peers and npref_peers).
     402                 :         320 :     std::vector<NodeId> request_order;
     403         [ +  - ]:         320 :     request_order.reserve(num_pref + num_npref);
     404   [ +  -  +  + ]:        1012 :     for (int i = 0; i < num_pref; ++i) request_order.push_back(pref_peers[i]);
     405   [ +  -  +  + ]:        1068 :     for (int i = 0; i < num_npref; ++i) request_order.push_back(npref_peers[i]);
     406                 :             : 
     407                 :             :     // Determine the announcement order randomly.
     408         [ +  - ]:         320 :     std::vector<NodeId> announce_order = request_order;
     409                 :         320 :     std::shuffle(announce_order.begin(), announce_order.end(), m_rng);
     410                 :             : 
     411                 :             :     // Find a gtxid whose txhash prioritization is consistent with the required ordering within pref_peers and
     412                 :             :     // within npref_peers.
     413   [ +  -  +  -  :         960 :     auto gtxid = scenario.NewGTxid({pref_peers, npref_peers});
             +  +  -  - ]
     414                 :             : 
     415                 :             :     // Decide reqtimes in opposite order of the expected request order. This means that as time passes we expect the
     416                 :             :     // to-be-requested-from-peer will change every time a subsequent reqtime is passed.
     417                 :         320 :     std::map<NodeId, std::chrono::microseconds> reqtimes;
     418                 :         320 :     auto reqtime = scenario.Now();
     419         [ +  + ]:        1760 :     for (int i = peers - 1; i >= 0; --i) {
     420         [ +  - ]:        1440 :         reqtime += RandomTime8s();
     421         [ +  - ]:        1440 :         reqtimes[request_order[i]] = reqtime;
     422                 :             :     }
     423                 :             : 
     424                 :             :     // Actually announce from all peers simultaneously (but in announce_order).
     425         [ +  + ]:        1760 :     for (const auto peer : announce_order) {
     426   [ +  -  +  -  :        1440 :         scenario.ReceivedInv(peer, gtxid, preferred[peer], reqtimes[peer]);
                   +  - ]
     427                 :             :     }
     428         [ +  + ]:        1760 :     for (const auto peer : announce_order) {
     429   [ +  -  +  - ]:        2880 :         scenario.Check(peer, {}, 1, 0, 0, "b1");
     430                 :             :     }
     431                 :             : 
     432                 :             :     // Let time pass and observe the to-be-requested-from peer change, from nonpreferred to preferred, and from
     433                 :             :     // high priority to low priority within each class.
     434         [ +  + ]:        1760 :     for (int i = peers - 1; i >= 0; --i) {
     435         [ +  - ]:        1440 :         scenario.AdvanceTime(reqtimes[request_order[i]] - scenario.Now() - MICROSECOND);
     436   [ +  -  +  - ]:        2880 :         scenario.Check(request_order[i], {}, 1, 0, 0, "b2");
     437                 :        1440 :         scenario.AdvanceTime(MICROSECOND);
     438   [ +  -  +  -  :        2880 :         scenario.Check(request_order[i], {gtxid}, 1, 0, 0, "b3");
                   +  - ]
     439                 :             :     }
     440                 :             : 
     441                 :             :     // Peers now in random order go offline, or send NOTFOUNDs. At every point in time the new to-be-requested-from
     442                 :             :     // peer should be the best remaining one, so verify this after every response.
     443         [ +  + ]:        1760 :     for (int i = 0; i < peers; ++i) {
     444         [ +  + ]:        1440 :         if (m_rng.randbool()) scenario.AdvanceTime(RandomTime8s());
     445                 :        1440 :         const int pos = m_rng.randrange(request_order.size());
     446                 :        1440 :         const auto peer = request_order[pos];
     447                 :        1440 :         request_order.erase(request_order.begin() + pos);
     448         [ +  + ]:        1440 :         if (m_rng.randbool()) {
     449         [ +  - ]:         722 :             scenario.DisconnectedPeer(peer);
     450   [ +  -  +  - ]:        1444 :             scenario.Check(peer, {}, 0, 0, 0, "b4");
     451                 :             :         } else {
     452         [ +  - ]:         718 :             scenario.ReceivedResponse(peer, gtxid.GetHash());
     453   [ +  -  +  - ]:        1436 :             scenario.Check(peer, {}, 0, 0, request_order.size() > 0, "b5");
     454                 :             :         }
     455         [ +  + ]:        1440 :         if (request_order.size()) {
     456   [ +  -  +  -  :        2240 :             scenario.Check(request_order[0], {gtxid}, 1, 0, 0, "b6");
                   +  - ]
     457                 :             :         }
     458                 :             :     }
     459                 :             : 
     460                 :             :     // Everything is gone in the end.
     461         [ +  + ]:        1760 :     for (const auto peer : announce_order) {
     462   [ +  -  +  - ]:        2880 :         scenario.Check(peer, {}, 0, 0, 0, "b7");
     463                 :             :     }
     464   [ +  -  +  -  :         640 : }
                   -  - ]
     465                 :             : 
     466                 :             : /** Add to scenario a test with one peer announcing two transactions, to verify they are
     467                 :             :  *  fetched in announcement order.
     468                 :             :  *
     469                 :             :  *  config is an integer in [0, 4) inclusive, and selects the variant of the test.
     470                 :             :  */
     471                 :         320 : void TxRequestTest::BuildRequestOrderTest(Scenario& scenario, int config)
     472                 :             : {
     473                 :         320 :     scenario.SetTestName(strprintf("RequestOrder(config=%i)", config));
     474                 :             : 
     475                 :         320 :     auto peer = scenario.NewPeer();
     476         [ +  - ]:         320 :     auto gtxid1 = scenario.NewGTxid();
     477         [ +  - ]:         320 :     auto gtxid2 = scenario.NewGTxid();
     478                 :             : 
     479                 :         320 :     auto reqtime2 = scenario.Now() + RandomTime8s();
     480                 :         320 :     auto reqtime1 = reqtime2 + RandomTime8s();
     481                 :             : 
     482                 :         320 :     scenario.ReceivedInv(peer, gtxid1, config & 1, reqtime1);
     483                 :             :     // Simulate time going backwards by giving the second announcement an earlier reqtime.
     484                 :         320 :     scenario.ReceivedInv(peer, gtxid2, config & 2, reqtime2);
     485                 :             : 
     486                 :         320 :     scenario.AdvanceTime(reqtime2 - MICROSECOND - scenario.Now());
     487         [ +  - ]:         640 :     scenario.Check(peer, {}, 2, 0, 0, "o1");
     488                 :         320 :     scenario.AdvanceTime(MICROSECOND);
     489   [ +  -  +  - ]:         640 :     scenario.Check(peer, {gtxid2}, 2, 0, 0, "o2");
     490                 :         320 :     scenario.AdvanceTime(reqtime1 - MICROSECOND - scenario.Now());
     491   [ +  -  +  - ]:         640 :     scenario.Check(peer, {gtxid2}, 2, 0, 0, "o3");
     492                 :         320 :     scenario.AdvanceTime(MICROSECOND);
     493                 :             :     // Even with time going backwards in between announcements, the return value of GetRequestable is in
     494                 :             :     // announcement order.
     495   [ +  -  +  - ]:         640 :     scenario.Check(peer, {gtxid1, gtxid2}, 2, 0, 0, "o4");
     496                 :             : 
     497                 :         320 :     scenario.DisconnectedPeer(peer);
     498         [ +  - ]:         640 :     scenario.Check(peer, {}, 0, 0, 0, "o5");
     499                 :         320 : }
     500                 :             : 
     501                 :             : /** Add to scenario a test that verifies behavior related to both txid and wtxid with the same
     502                 :             :  *  hash being announced.
     503                 :             :  *
     504                 :             :  *  config is an integer in [0, 4) inclusive, and selects the variant of the test used.
     505                 :             : */
     506                 :         320 : void TxRequestTest::BuildWtxidTest(Scenario& scenario, int config)
     507                 :             : {
     508                 :         320 :     scenario.SetTestName(strprintf("Wtxid(config=%i)", config));
     509                 :             : 
     510                 :         320 :     auto peerT = scenario.NewPeer();
     511                 :         320 :     auto peerW = scenario.NewPeer();
     512         [ +  - ]:         320 :     auto txhash = scenario.NewTxHash();
     513                 :         320 :     auto txid{GenTxid::Txid(txhash)};
     514                 :         320 :     auto wtxid{GenTxid::Wtxid(txhash)};
     515                 :             : 
     516         [ +  + ]:         320 :     auto reqtimeT = m_rng.randbool() ? MIN_TIME : scenario.Now() + RandomTime8s();
     517         [ +  + ]:         320 :     auto reqtimeW = m_rng.randbool() ? MIN_TIME : scenario.Now() + RandomTime8s();
     518                 :             : 
     519                 :             :     // Announce txid first or wtxid first.
     520         [ +  + ]:         320 :     if (config & 1) {
     521                 :         160 :         scenario.ReceivedInv(peerT, txid, config & 2, reqtimeT);
     522         [ +  + ]:         160 :         if (m_rng.randbool()) scenario.AdvanceTime(RandomTime8s());
     523                 :         160 :         scenario.ReceivedInv(peerW, wtxid, !(config & 2), reqtimeW);
     524                 :             :     } else {
     525                 :         160 :         scenario.ReceivedInv(peerW, wtxid, !(config & 2), reqtimeW);
     526         [ +  + ]:         160 :         if (m_rng.randbool()) scenario.AdvanceTime(RandomTime8s());
     527                 :         160 :         scenario.ReceivedInv(peerT, txid, config & 2, reqtimeT);
     528                 :             :     }
     529                 :             : 
     530                 :             :     // Let time pass if needed, and check that the preferred announcement (txid or wtxid)
     531                 :             :     // is correctly to-be-requested (and with the correct wtxidness).
     532                 :         320 :     auto max_reqtime = std::max(reqtimeT, reqtimeW);
     533         [ +  + ]:         320 :     if (max_reqtime > scenario.Now()) scenario.AdvanceTime(max_reqtime - scenario.Now());
     534         [ +  + ]:         320 :     if (config & 2) {
     535   [ +  -  +  - ]:         320 :         scenario.Check(peerT, {txid}, 1, 0, 0, "w1");
     536         [ +  - ]:         320 :         scenario.Check(peerW, {}, 1, 0, 0, "w2");
     537                 :             :     } else {
     538         [ +  - ]:         320 :         scenario.Check(peerT, {}, 1, 0, 0, "w3");
     539   [ +  -  +  - ]:         320 :         scenario.Check(peerW, {wtxid}, 1, 0, 0, "w4");
     540                 :             :     }
     541                 :             : 
     542                 :             :     // Let the preferred announcement be requested. It's not going to be delivered.
     543                 :         320 :     auto expiry = RandomTime8s();
     544         [ +  + ]:         320 :     if (config & 2) {
     545                 :         160 :         scenario.RequestedTx(peerT, txid.GetHash(), scenario.Now() + expiry);
     546         [ +  - ]:         320 :         scenario.Check(peerT, {}, 0, 1, 0, "w5");
     547         [ +  - ]:         320 :         scenario.Check(peerW, {}, 1, 0, 0, "w6");
     548                 :             :     } else {
     549                 :         160 :         scenario.RequestedTx(peerW, wtxid.GetHash(), scenario.Now() + expiry);
     550         [ +  - ]:         320 :         scenario.Check(peerT, {}, 1, 0, 0, "w7");
     551         [ +  - ]:         320 :         scenario.Check(peerW, {}, 0, 1, 0, "w8");
     552                 :             :     }
     553                 :             : 
     554                 :             :     // After reaching expiration time of the preferred announcement, verify that the
     555                 :             :     // remaining one is requestable
     556                 :         320 :     scenario.AdvanceTime(expiry);
     557         [ +  + ]:         320 :     if (config & 2) {
     558         [ +  - ]:         320 :         scenario.Check(peerT, {}, 0, 0, 1, "w9");
     559   [ +  -  +  - ]:         320 :         scenario.Check(peerW, {wtxid}, 1, 0, 0, "w10");
     560                 :         160 :         scenario.CheckExpired(peerT, txid);
     561                 :             :     } else {
     562   [ +  -  +  - ]:         320 :         scenario.Check(peerT, {txid}, 1, 0, 0, "w11");
     563         [ +  - ]:         320 :         scenario.Check(peerW, {}, 0, 0, 1, "w12");
     564                 :         160 :         scenario.CheckExpired(peerW, wtxid);
     565                 :             :     }
     566                 :             : 
     567                 :             :     // If a good transaction with either that hash as wtxid or txid arrives, both
     568                 :             :     // announcements are gone.
     569         [ +  + ]:         320 :     if (m_rng.randbool()) scenario.AdvanceTime(RandomTime8s());
     570                 :         320 :     scenario.ForgetTxHash(txhash);
     571         [ +  - ]:         640 :     scenario.Check(peerT, {}, 0, 0, 0, "w13");
     572         [ +  - ]:         640 :     scenario.Check(peerW, {}, 0, 0, 0, "w14");
     573                 :         320 : }
     574                 :             : 
     575                 :             : /** Add to scenario a test that exercises clocks that go backwards. */
     576                 :         320 : void TxRequestTest::BuildTimeBackwardsTest(Scenario& scenario)
     577                 :             : {
     578                 :         320 :     auto peer1 = scenario.NewPeer();
     579                 :         320 :     auto peer2 = scenario.NewPeer();
     580   [ +  -  +  -  :         640 :     auto gtxid = scenario.NewGTxid({{peer1, peer2}});
             +  +  -  - ]
     581                 :             : 
     582                 :             :     // Announce from peer2.
     583                 :         320 :     auto reqtime = scenario.Now() + RandomTime8s();
     584                 :         320 :     scenario.ReceivedInv(peer2, gtxid, true, reqtime);
     585         [ +  - ]:         640 :     scenario.Check(peer2, {}, 1, 0, 0, "r1");
     586                 :         320 :     scenario.AdvanceTime(reqtime - scenario.Now());
     587   [ +  -  +  - ]:         640 :     scenario.Check(peer2, {gtxid}, 1, 0, 0, "r2");
     588                 :             :     // Check that if the clock goes backwards by 1us, the transaction would stop being requested.
     589         [ +  - ]:         640 :     scenario.Check(peer2, {}, 1, 0, 0, "r3", -MICROSECOND);
     590                 :             :     // But it reverts to being requested if time goes forward again.
     591   [ +  -  +  - ]:         640 :     scenario.Check(peer2, {gtxid}, 1, 0, 0, "r4");
     592                 :             : 
     593                 :             :     // Announce from peer1.
     594         [ +  + ]:         320 :     if (m_rng.randbool()) scenario.AdvanceTime(RandomTime8s());
     595                 :         320 :     scenario.ReceivedInv(peer1, gtxid, true, MAX_TIME);
     596   [ +  -  +  - ]:         640 :     scenario.Check(peer2, {gtxid}, 1, 0, 0, "r5");
     597         [ +  - ]:         640 :     scenario.Check(peer1, {}, 1, 0, 0, "r6");
     598                 :             : 
     599                 :             :     // Request from peer1.
     600         [ +  + ]:         320 :     if (m_rng.randbool()) scenario.AdvanceTime(RandomTime8s());
     601                 :         320 :     auto expiry = scenario.Now() + RandomTime8s();
     602                 :         320 :     scenario.RequestedTx(peer1, gtxid.GetHash(), expiry);
     603         [ +  - ]:         640 :     scenario.Check(peer1, {}, 0, 1, 0, "r7");
     604         [ +  - ]:         640 :     scenario.Check(peer2, {}, 1, 0, 0, "r8");
     605                 :             : 
     606                 :             :     // Expiration passes.
     607                 :         320 :     scenario.AdvanceTime(expiry - scenario.Now());
     608         [ +  - ]:         640 :     scenario.Check(peer1, {}, 0, 0, 1, "r9");
     609   [ +  -  +  - ]:         640 :     scenario.Check(peer2, {gtxid}, 1, 0, 0, "r10"); // Request goes back to peer2.
     610                 :         320 :     scenario.CheckExpired(peer1, gtxid);
     611         [ +  - ]:         640 :     scenario.Check(peer1, {}, 0, 0, 1, "r11", -MICROSECOND); // Going back does not unexpire.
     612   [ +  -  +  - ]:         640 :     scenario.Check(peer2, {gtxid}, 1, 0, 0, "r12", -MICROSECOND);
     613                 :             : 
     614                 :             :     // Peer2 goes offline, meaning no viable announcements remain.
     615         [ +  + ]:         320 :     if (m_rng.randbool()) scenario.AdvanceTime(RandomTime8s());
     616                 :         320 :     scenario.DisconnectedPeer(peer2);
     617         [ +  - ]:         640 :     scenario.Check(peer1, {}, 0, 0, 0, "r13");
     618         [ +  - ]:         640 :     scenario.Check(peer2, {}, 0, 0, 0, "r14");
     619                 :         640 : }
     620                 :             : 
     621                 :             : /** Add to scenario a test that involves RequestedTx() calls for txhashes not returned by GetRequestable. */
     622                 :         320 : void TxRequestTest::BuildWeirdRequestsTest(Scenario& scenario)
     623                 :             : {
     624                 :         320 :     auto peer1 = scenario.NewPeer();
     625                 :         320 :     auto peer2 = scenario.NewPeer();
     626   [ +  -  +  -  :         640 :     auto gtxid1 = scenario.NewGTxid({{peer1, peer2}});
             +  +  -  - ]
     627   [ +  -  +  -  :         640 :     auto gtxid2 = scenario.NewGTxid({{peer2, peer1}});
             +  +  -  - ]
     628                 :             : 
     629                 :             :     // Announce gtxid1 by peer1.
     630                 :         320 :     scenario.ReceivedInv(peer1, gtxid1, true, MIN_TIME);
     631   [ +  -  +  - ]:         640 :     scenario.Check(peer1, {gtxid1}, 1, 0, 0, "q1");
     632                 :             : 
     633                 :             :     // Announce gtxid2 by peer2.
     634         [ +  + ]:         320 :     if (m_rng.randbool()) scenario.AdvanceTime(RandomTime8s());
     635                 :         320 :     scenario.ReceivedInv(peer2, gtxid2, true, MIN_TIME);
     636   [ +  -  +  - ]:         640 :     scenario.Check(peer1, {gtxid1}, 1, 0, 0, "q2");
     637   [ +  -  +  - ]:         640 :     scenario.Check(peer2, {gtxid2}, 1, 0, 0, "q3");
     638                 :             : 
     639                 :             :     // We request gtxid2 from *peer1* - no effect.
     640         [ +  + ]:         320 :     if (m_rng.randbool()) scenario.AdvanceTime(RandomTime8s());
     641                 :         320 :     scenario.RequestedTx(peer1, gtxid2.GetHash(), MAX_TIME);
     642   [ +  -  +  - ]:         640 :     scenario.Check(peer1, {gtxid1}, 1, 0, 0, "q4");
     643   [ +  -  +  - ]:         640 :     scenario.Check(peer2, {gtxid2}, 1, 0, 0, "q5");
     644                 :             : 
     645                 :             :     // Now request gtxid1 from peer1 - marks it as REQUESTED.
     646         [ +  + ]:         320 :     if (m_rng.randbool()) scenario.AdvanceTime(RandomTime8s());
     647                 :         320 :     auto expiryA = scenario.Now() + RandomTime8s();
     648                 :         320 :     scenario.RequestedTx(peer1, gtxid1.GetHash(), expiryA);
     649         [ +  - ]:         640 :     scenario.Check(peer1, {}, 0, 1, 0, "q6");
     650   [ +  -  +  - ]:         640 :     scenario.Check(peer2, {gtxid2}, 1, 0, 0, "q7");
     651                 :             : 
     652                 :             :     // Request it a second time - nothing happens, as it's already REQUESTED.
     653                 :         320 :     auto expiryB = expiryA + RandomTime8s();
     654                 :         320 :     scenario.RequestedTx(peer1, gtxid1.GetHash(), expiryB);
     655         [ +  - ]:         640 :     scenario.Check(peer1, {}, 0, 1, 0, "q8");
     656   [ +  -  +  - ]:         640 :     scenario.Check(peer2, {gtxid2}, 1, 0, 0, "q9");
     657                 :             : 
     658                 :             :     // Also announce gtxid1 from peer2 now, so that the txhash isn't forgotten when the peer1 request expires.
     659                 :         320 :     scenario.ReceivedInv(peer2, gtxid1, true, MIN_TIME);
     660         [ +  - ]:         640 :     scenario.Check(peer1, {}, 0, 1, 0, "q10");
     661   [ +  -  +  - ]:         640 :     scenario.Check(peer2, {gtxid2}, 2, 0, 0, "q11");
     662                 :             : 
     663                 :             :     // When reaching expiryA, it expires (not expiryB, which is later).
     664                 :         320 :     scenario.AdvanceTime(expiryA - scenario.Now());
     665         [ +  - ]:         640 :     scenario.Check(peer1, {}, 0, 0, 1, "q12");
     666   [ +  -  +  - ]:         640 :     scenario.Check(peer2, {gtxid2, gtxid1}, 2, 0, 0, "q13");
     667                 :         320 :     scenario.CheckExpired(peer1, gtxid1);
     668                 :             : 
     669                 :             :     // Requesting it yet again from peer1 doesn't do anything, as it's already COMPLETED.
     670         [ +  + ]:         320 :     if (m_rng.randbool()) scenario.AdvanceTime(RandomTime8s());
     671                 :         320 :     scenario.RequestedTx(peer1, gtxid1.GetHash(), MAX_TIME);
     672         [ +  - ]:         640 :     scenario.Check(peer1, {}, 0, 0, 1, "q14");
     673   [ +  -  +  - ]:         640 :     scenario.Check(peer2, {gtxid2, gtxid1}, 2, 0, 0, "q15");
     674                 :             : 
     675                 :             :     // Now announce gtxid2 from peer1.
     676         [ +  + ]:         320 :     if (m_rng.randbool()) scenario.AdvanceTime(RandomTime8s());
     677                 :         320 :     scenario.ReceivedInv(peer1, gtxid2, true, MIN_TIME);
     678         [ +  - ]:         640 :     scenario.Check(peer1, {}, 1, 0, 1, "q16");
     679   [ +  -  +  - ]:         640 :     scenario.Check(peer2, {gtxid2, gtxid1}, 2, 0, 0, "q17");
     680                 :             : 
     681                 :             :     // And request it from peer1 (weird as peer2 has the preference).
     682         [ +  + ]:         320 :     if (m_rng.randbool()) scenario.AdvanceTime(RandomTime8s());
     683                 :         320 :     scenario.RequestedTx(peer1, gtxid2.GetHash(), MAX_TIME);
     684         [ +  - ]:         640 :     scenario.Check(peer1, {}, 0, 1, 1, "q18");
     685   [ +  -  +  - ]:         640 :     scenario.Check(peer2, {gtxid1}, 2, 0, 0, "q19");
     686                 :             : 
     687                 :             :     // If peer2 now (normally) requests gtxid2, the existing request by peer1 becomes COMPLETED.
     688         [ +  + ]:         320 :     if (m_rng.randbool()) scenario.AdvanceTime(RandomTime8s());
     689                 :         320 :     scenario.RequestedTx(peer2, gtxid2.GetHash(), MAX_TIME);
     690         [ +  - ]:         640 :     scenario.Check(peer1, {}, 0, 0, 2, "q20");
     691   [ +  -  +  - ]:         640 :     scenario.Check(peer2, {gtxid1}, 1, 1, 0, "q21");
     692                 :             : 
     693                 :             :     // If peer2 goes offline, no viable announcements remain.
     694                 :         320 :     scenario.DisconnectedPeer(peer2);
     695         [ +  - ]:         640 :     scenario.Check(peer1, {}, 0, 0, 0, "q22");
     696         [ +  - ]:         640 :     scenario.Check(peer2, {}, 0, 0, 0, "q23");
     697                 :         960 : }
     698                 :             : 
     699                 :           5 : void TxRequestTest::TestInterleavedScenarios()
     700                 :             : {
     701                 :             :     // Create a list of functions which add tests to scenarios.
     702                 :           5 :     std::vector<std::function<void(Scenario&)>> builders;
     703                 :             :     // Add instances of every test, for every configuration.
     704         [ +  + ]:         325 :     for (int n = 0; n < 64; ++n) {
     705         [ +  - ]:         640 :         builders.emplace_back([this, n](Scenario& scenario) { BuildWtxidTest(scenario, n); });
     706         [ +  - ]:         640 :         builders.emplace_back([this, n](Scenario& scenario) { BuildRequestOrderTest(scenario, n & 3); });
     707         [ +  - ]:         640 :         builders.emplace_back([this, n](Scenario& scenario) { BuildSingleTest(scenario, n & 31); });
     708         [ +  - ]:         640 :         builders.emplace_back([this, n](Scenario& scenario) { BuildPriorityTest(scenario, n & 31); });
     709         [ +  - ]:         640 :         builders.emplace_back([this, n](Scenario& scenario) { BuildBigPriorityTest(scenario, (n & 7) + 1); });
     710         [ +  - ]:         640 :         builders.emplace_back([this](Scenario& scenario) { BuildTimeBackwardsTest(scenario); });
     711         [ +  - ]:         640 :         builders.emplace_back([this](Scenario& scenario) { BuildWeirdRequestsTest(scenario); });
     712                 :             :     }
     713                 :             :     // Randomly shuffle all those functions.
     714                 :           5 :     std::shuffle(builders.begin(), builders.end(), m_rng);
     715                 :             : 
     716         [ +  - ]:           5 :     Runner runner;
     717                 :           5 :     auto starttime = RandomTime1y();
     718                 :             :     // Construct many scenarios, and run (up to) 10 randomly-chosen tests consecutively in each.
     719         [ +  + ]:         230 :     while (builders.size()) {
     720                 :             :         // Introduce some variation in the start time of each scenario, so they don't all start off
     721                 :             :         // concurrently, but get a more random interleaving.
     722                 :         225 :         auto scenario_start = starttime + RandomTime8s() + RandomTime8s() + RandomTime8s();
     723                 :         225 :         Scenario scenario(m_rng, runner, scenario_start);
     724   [ +  +  +  + ]:        2465 :         for (int j = 0; builders.size() && j < 10; ++j) {
     725         [ +  - ]:        2240 :             builders.back()(scenario);
     726                 :        2240 :             builders.pop_back();
     727                 :             :         }
     728                 :         225 :     }
     729                 :             :     // Sort all the actions from all those scenarios chronologically, resulting in the actions from
     730                 :             :     // distinct scenarios to become interleaved. Use stable_sort so that actions from one scenario
     731                 :             :     // aren't reordered w.r.t. each other.
     732                 :           5 :     std::stable_sort(runner.actions.begin(), runner.actions.end(), [](const Action& a1, const Action& a2) {
     733   [ +  +  +  +  :      435059 :         return a1.first < a2.first;
          -  -  -  -  +  
          +  +  +  -  -  
             -  -  +  + ]
     734                 :             :     });
     735                 :             : 
     736                 :             :     // Run all actions from all scenarios, in order.
     737         [ +  + ]:       42552 :     for (auto& action : runner.actions) {
     738         [ +  - ]:       42547 :         action.second();
     739                 :             :     }
     740                 :             : 
     741   [ +  -  +  -  :           5 :     BOOST_CHECK_EQUAL(runner.txrequest.Size(), 0U);
                   +  - ]
     742   [ +  -  +  - ]:          10 :     BOOST_CHECK(runner.expired.empty());
     743                 :           5 : }
     744                 :             : 
     745                 :             : }  // namespace
     746                 :             : 
     747                 :             : BOOST_FIXTURE_TEST_SUITE(txrequest_tests, TxRequestTest)
     748                 :             : 
     749   [ +  -  +  -  :           7 : BOOST_AUTO_TEST_CASE(TxRequestTest)
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
                      - ]
     750                 :             : {
     751         [ +  + ]:           6 :     for (int i = 0; i < 5; ++i) {
     752                 :           5 :         TestInterleavedScenarios();
     753                 :             :     }
     754                 :           1 : }
     755                 :             : 
     756                 :             : BOOST_AUTO_TEST_SUITE_END()
        

Generated by: LCOV version 2.0-1