| 623 | } |
| 624 | |
| 625 | void RequestedTx(NodeId peer, const uint256& txhash, std::chrono::microseconds expiry) |
| 626 | { |
| 627 | auto it = m_index.get<ByPeer>().find(ByPeerView{peer, true, txhash}); |
| 628 | if (it == m_index.get<ByPeer>().end()) { |
| 629 | // There is no CANDIDATE_BEST announcement, look for a _READY or _DELAYED instead. If the caller only |
| 630 | // ever invokes RequestedTx with the values returned by GetRequestable, and no other non-const functions |
| 631 | // other than ForgetTxHash and GetRequestable in between, this branch will never execute (as txhashes |
| 632 | // returned by GetRequestable always correspond to CANDIDATE_BEST announcements). |
| 633 | |
| 634 | it = m_index.get<ByPeer>().find(ByPeerView{peer, false, txhash}); |
| 635 | if (it == m_index.get<ByPeer>().end() || (it->GetState() != State::CANDIDATE_DELAYED && |
| 636 | it->GetState() != State::CANDIDATE_READY)) { |
| 637 | // There is no CANDIDATE announcement tracked for this peer, so we have nothing to do. Either this |
| 638 | // txhash wasn't tracked at all (and the caller should have called ReceivedInv), or it was already |
| 639 | // requested and/or completed for other reasons and this is just a superfluous RequestedTx call. |
| 640 | return; |
| 641 | } |
| 642 | |
| 643 | // Look for an existing CANDIDATE_BEST or REQUESTED with the same txhash. We only need to do this if the |
| 644 | // found announcement had a different state than CANDIDATE_BEST. If it did, invariants guarantee that no |
| 645 | // other CANDIDATE_BEST or REQUESTED can exist. |
| 646 | auto it_old = m_index.get<ByTxHash>().lower_bound(ByTxHashView{txhash, State::CANDIDATE_BEST, 0}); |
| 647 | if (it_old != m_index.get<ByTxHash>().end() && it_old->m_txhash == txhash) { |
| 648 | if (it_old->GetState() == State::CANDIDATE_BEST) { |
| 649 | // The data structure's invariants require that there can be at most one CANDIDATE_BEST or one |
| 650 | // REQUESTED announcement per txhash (but not both simultaneously), so we have to convert any |
| 651 | // existing CANDIDATE_BEST to another CANDIDATE_* when constructing another REQUESTED. |
| 652 | // It doesn't matter whether we pick CANDIDATE_READY or _DELAYED here, as SetTimePoint() |
| 653 | // will correct it at GetRequestable() time. If time only goes forward, it will always be |
| 654 | // _READY, so pick that to avoid extra work in SetTimePoint(). |
| 655 | Modify<ByTxHash>(it_old, [](Announcement& ann) { ann.SetState(State::CANDIDATE_READY); }); |
| 656 | } else if (it_old->GetState() == State::REQUESTED) { |
| 657 | // As we're no longer waiting for a response to the previous REQUESTED announcement, convert it |
| 658 | // to COMPLETED. This also helps guaranteeing progress. |
| 659 | Modify<ByTxHash>(it_old, [](Announcement& ann) { ann.SetState(State::COMPLETED); }); |
| 660 | } |
| 661 | } |
| 662 | } |
| 663 | |
| 664 | Modify<ByPeer>(it, [expiry](Announcement& ann) { |
| 665 | ann.SetState(State::REQUESTED); |
| 666 | ann.m_time = expiry; |
| 667 | }); |
| 668 | } |
| 669 | |
| 670 | void ReceivedResponse(NodeId peer, const uint256& txhash) |
| 671 | { |
nothing calls this directly
no test coverage detected