Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
251do {252ret = m_rng.randbits(63);253ok = m_runner.peerset.insert(ret).second;254} while(!ok);255return ret;256}258std::chrono::microseconds Now() const { return m_now; }259};261/** Add to scenario a test with a single tx announced by a single peer.262*263* config is an integer in [0, 32), which controls which variant of the test is used.264*/265void TxRequestTest::BuildSingleTest(Scenario& scenario, int config)266{267auto peer = scenario.NewPeer();268auto gtxid = scenario.NewGTxid();269bool immediate = config & 1;270bool preferred = config & 2;271auto delay = immediate ? NO_TIME : RandomTime8s();273scenario.SetTestName(strprintf("Single(config=%i)", config));275// Receive an announcement, either immediately requestable or delayed.276scenario.ReceivedInv(peer, gtxid, preferred, immediate ? MIN_TIME : scenario.Now() + delay);277if (immediate) {278scenario.Check(peer, {gtxid}, 1, 0, 0, "s1");279} else {280scenario.Check(peer, {}, 1, 0, 0, "s2");281scenario.AdvanceTime(delay - MICROSECOND);282scenario.Check(peer, {}, 1, 0, 0, "s3");283scenario.AdvanceTime(MICROSECOND);284scenario.Check(peer, {gtxid}, 1, 0, 0, "s4");285}287if (config >> 3) { // We'll request the transaction288scenario.AdvanceTime(RandomTime8s());289auto expiry = RandomTime8s();290scenario.Check(peer, {gtxid}, 1, 0, 0, "s5");291scenario.RequestedTx(peer, gtxid.ToUint256(), scenario.Now() + expiry);292scenario.Check(peer, {}, 0, 1, 0, "s6");294if ((config >> 3) == 1) { // The request will time out295scenario.AdvanceTime(expiry - MICROSECOND);296scenario.Check(peer, {}, 0, 1, 0, "s7");297scenario.AdvanceTime(MICROSECOND);298scenario.Check(peer, {}, 0, 0, 0, "s8");299scenario.CheckExpired(peer, gtxid);300return;301} else {302scenario.AdvanceTime(std::chrono::microseconds{m_rng.randrange(expiry.count())});303scenario.Check(peer, {}, 0, 1, 0, "s9");304if ((config >> 3) == 3) { // A response will arrive for the transaction305scenario.ReceivedResponse(peer, gtxid.ToUint256());306scenario.Check(peer, {}, 0, 0, 0, "s10");307return;308}309}310}312if (config & 4) { // The peer will go offline313scenario.DisconnectedPeer(peer);314} else { // The transaction is no longer needed315scenario.ForgetTxHash(gtxid.ToUint256());316}317scenario.Check(peer, {}, 0, 0, 0, "s11");318}320/** Add to scenario a test with a single tx announced by two peers, to verify the321* right peer is selected for requests.322*323* config is an integer in [0, 32), which controls which variant of the test is used.324*/325void TxRequestTest::BuildPriorityTest(Scenario& scenario, int config)326{327scenario.SetTestName(strprintf("Priority(config=%i)", config));329// Two peers. They will announce in order {peer1, peer2}.330auto peer1 = scenario.NewPeer(), peer2 = scenario.NewPeer();331// Construct a transaction that under random rules would be preferred by peer2 or peer1,332// depending on configuration.333bool prio1 = config & 1;334auto gtxid = prio1 ? scenario.NewGTxid({{peer1, peer2}}) : scenario.NewGTxid({{peer2, peer1}});335bool pref1 = config & 2, pref2 = config & 4;337scenario.ReceivedInv(peer1, gtxid, pref1, MIN_TIME);338scenario.Check(peer1, {gtxid}, 1, 0, 0, "p1");339if (m_rng.randbool()) {340scenario.AdvanceTime(RandomTime8s());341scenario.Check(peer1, {gtxid}, 1, 0, 0, "p2");342}344scenario.ReceivedInv(peer2, gtxid, pref2, MIN_TIME);345bool stage2_prio =346// At this point, peer2 will be given priority if:347// - It is preferred and peer1 is not348(pref2 && !pref1) ||349// - They're in the same preference class,350// and the randomized priority favors peer2 over peer1.351(pref1 == pref2 && !prio1);352NodeId priopeer = stage2_prio ? peer2 : peer1, otherpeer = stage2_prio ? peer1 : peer2;353scenario.Check(otherpeer, {}, 1, 0, 0, "p3");354scenario.Check(priopeer, {gtxid}, 1, 0, 0, "p4");355if (m_rng.randbool()) scenario.AdvanceTime(RandomTime8s());356scenario.Check(otherpeer, {}, 1, 0, 0, "p5");357scenario.Check(priopeer, {gtxid}, 1, 0, 0, "p6");359// We possibly request from the selected peer.360if (config & 8) {361scenario.RequestedTx(priopeer, gtxid.ToUint256(), MAX_TIME);362scenario.Check(priopeer, {}, 0, 1, 0, "p7");363scenario.Check(otherpeer, {}, 1, 0, 0, "p8");364if (m_rng.randbool()) scenario.AdvanceTime(RandomTime8s());365}367// The peer which was selected (or requested from) now goes offline, or a NOTFOUND is received from them.368if (config & 16) {369scenario.DisconnectedPeer(priopeer);370} else {371scenario.ReceivedResponse(priopeer, gtxid.ToUint256());372}373if (m_rng.randbool()) scenario.AdvanceTime(RandomTime8s());374scenario.Check(priopeer, {}, 0, 0, !(config & 16), "p8");375scenario.Check(otherpeer, {gtxid}, 1, 0, 0, "p9");376if (m_rng.randbool()) scenario.AdvanceTime(RandomTime8s());378// Now the other peer goes offline.379scenario.DisconnectedPeer(otherpeer);380if (m_rng.randbool()) scenario.AdvanceTime(RandomTime8s());381scenario.Check(peer1, {}, 0, 0, 0, "p10");382scenario.Check(peer2, {}, 0, 0, 0, "p11");383}385/** Add to scenario a randomized test in which N peers announce the same transaction, to verify386* the order in which they are requested. */387void TxRequestTest::BuildBigPriorityTest(Scenario& scenario, int peers)388{389scenario.SetTestName(strprintf("BigPriority(peers=%i)", peers));391// We will have N peers announce the same transaction.392std::map<NodeId, bool> preferred;393std::vector<NodeId> pref_peers, npref_peers;394int num_pref = m_rng.randrange(peers + 1) ; // Some preferred, ...395int num_npref = peers - num_pref; // some not preferred.396for (int i = 0; i < num_pref; ++i) {397pref_peers.push_back(scenario.NewPeer());398preferred[pref_peers.back()] = true;399}400for (int i = 0; i < num_npref; ++i) {401npref_peers.push_back(scenario.NewPeer());402preferred[npref_peers.back()] = false;403}404// Make a list of all peers, in order of intended request order (concatenation of pref_peers and npref_peers).405std::vector<NodeId> request_order;406request_order.reserve(num_pref + num_npref);407for (int i = 0; i < num_pref; ++i) request_order.push_back(pref_peers[i]);408for (int i = 0; i < num_npref; ++i) request_order.push_back(npref_peers[i]);410// Determine the announcement order randomly.411std::vector<NodeId> announce_order = request_order;412std::shuffle(announce_order.begin(), announce_order.end(), m_rng);414// Find a gtxid whose txhash prioritization is consistent with the required ordering within pref_peers and415// within npref_peers.416auto gtxid = scenario.NewGTxid({pref_peers, npref_peers});418// Decide reqtimes in opposite order of the expected request order. This means that as time passes we expect the419// to-be-requested-from-peer will change every time a subsequent reqtime is passed.420std::map<NodeId, std::chrono::microseconds> reqtimes;421auto reqtime = scenario.Now();422for (int i = peers - 1; i >= 0; --i) {423reqtime += RandomTime8s();424reqtimes[request_order[i]] = reqtime;425}427// Actually announce from all peers simultaneously (but in announce_order).428for (const auto peer : announce_order) {429scenario.ReceivedInv(peer, gtxid, preferred[peer], reqtimes[peer]);430}431for (const auto peer : announce_order) {432scenario.Check(peer, {}, 1, 0, 0, "b1");433}435// Let time pass and observe the to-be-requested-from peer change, from nonpreferred to preferred, and from436// high priority to low priority within each class.437for (int i = peers - 1; i >= 0; --i) {438scenario.AdvanceTime(reqtimes[request_order[i]] - scenario.Now() - MICROSECOND);439scenario.Check(request_order[i], {}, 1, 0, 0, "b2");440scenario.AdvanceTime(MICROSECOND);441scenario.Check(request_order[i], {gtxid}, 1, 0, 0, "b3");442}444// Peers now in random order go offline, or send NOTFOUNDs. At every point in time the new to-be-requested-from445// peer should be the best remaining one, so verify this after every response.446for (int i = 0; i < peers; ++i) {447if (m_rng.randbool()) scenario.AdvanceTime(RandomTime8s());448const int pos = m_rng.randrange(request_order.size());449const auto peer = request_order[pos];450request_order.erase(request_order.begin() + pos);451if (m_rng.randbool()) {452scenario.DisconnectedPeer(peer);453scenario.Check(peer, {}, 0, 0, 0, "b4");454} else {455scenario.ReceivedResponse(peer, gtxid.ToUint256());456scenario.Check(peer, {}, 0, 0, request_order.size() > 0, "b5");457}458if (request_order.size()) {459scenario.Check(request_order[0], {gtxid}, 1, 0, 0, "b6");460}461}463// Everything is gone in the end.464for (const auto peer : announce_order) {465scenario.Check(peer, {}, 0, 0, 0, "b7");466}467}469/** Add to scenario a test with one peer announcing two transactions, to verify they are470* fetched in announcement order.471*472* config is an integer in [0, 4) inclusive, and selects the variant of the test.473*/474void TxRequestTest::BuildRequestOrderTest(Scenario& scenario, int config)475{476scenario.SetTestName(strprintf("RequestOrder(config=%i)", config));478auto peer = scenario.NewPeer();479auto gtxid1 = scenario.NewGTxid();480auto gtxid2 = scenario.NewGTxid();482auto reqtime2 = scenario.Now() + RandomTime8s();483auto reqtime1 = reqtime2 + RandomTime8s();485scenario.ReceivedInv(peer, gtxid1, config & 1, reqtime1);486// Simulate time going backwards by giving the second announcement an earlier reqtime.487scenario.ReceivedInv(peer, gtxid2, config & 2, reqtime2);489scenario.AdvanceTime(reqtime2 - MICROSECOND - scenario.Now());490scenario.Check(peer, {}, 2, 0, 0, "o1");491scenario.AdvanceTime(MICROSECOND);492scenario.Check(peer, {gtxid2}, 2, 0, 0, "o2");493scenario.AdvanceTime(reqtime1 - MICROSECOND - scenario.Now());494scenario.Check(peer, {gtxid2}, 2, 0, 0, "o3");495scenario.AdvanceTime(MICROSECOND);496// Even with time going backwards in between announcements, the return value of GetRequestable is in497// announcement order.498scenario.Check(peer, {gtxid1, gtxid2}, 2, 0, 0, "o4");500scenario.DisconnectedPeer(peer);
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.