19constexpr int MAX_TXHASHES = 16;
 
   20constexpr int MAX_PEERS = 16;
 
   26std::chrono::microseconds DELAYS[256];
 
   32        for (uint8_t txhash = 0; txhash < MAX_TXHASHES; txhash += 1) {
 
   38            DELAYS[i] = std::chrono::microseconds{i};
 
   42        for (; i < 128; ++i) {
 
   43            int diff_bits = ((i - 10) * 2) / 9;
 
   45            DELAYS[i] = DELAYS[i - 1] + std::chrono::microseconds{diff};
 
   48        for (; i < 256; ++i) {
 
   49            DELAYS[i] = -DELAYS[255 - i];
 
   84    uint64_t m_current_sequence{0};
 
   87    std::priority_queue<std::chrono::microseconds, std::vector<std::chrono::microseconds>,
 
   88        std::greater<std::chrono::microseconds>> m_events;
 
   93        std::chrono::microseconds m_time;
 
   95        State m_state{State::NOTHING};
 
  102    Announcement m_announcements[MAX_TXHASHES][MAX_PEERS];
 
  105    std::chrono::microseconds m_now{244466666};
 
  108    void Cleanup(
int txhash)
 
  110        bool all_nothing = 
true;
 
  111        for (
int peer = 0; peer < MAX_PEERS; ++peer) {
 
  112            const Announcement& ann = m_announcements[txhash][peer];
 
  113            if (ann.m_state != State::NOTHING) {
 
  114                if (ann.m_state != State::COMPLETED) 
return;
 
  118        if (all_nothing) 
return;
 
  119        for (
int peer = 0; peer < MAX_PEERS; ++peer) {
 
  120            m_announcements[txhash][peer].m_state = State::NOTHING;
 
  125    int GetSelected(
int txhash)
 const 
  128        uint64_t ret_priority = 0;
 
  129        for (
int peer = 0; peer < MAX_PEERS; ++peer) {
 
  130            const Announcement& ann = m_announcements[txhash][peer];
 
  132            if (ann.m_state == State::REQUESTED) 
return -1;
 
  134            if (ann.m_state == State::CANDIDATE && ann.m_time <= m_now) {
 
  135                if (ret == -1 || ann.m_priority > ret_priority) {
 
  136                    std::tie(ret, ret_priority) = std::tie(peer, ann.m_priority);
 
  144    Tester() : m_tracker(true) {}
 
  146    std::chrono::microseconds Now()
 const { 
return m_now; }
 
  148    void AdvanceTime(std::chrono::microseconds offset)
 
  151        while (!m_events.empty() && m_events.top() <= m_now) m_events.pop();
 
  154    void AdvanceToEvent()
 
  156        while (!m_events.empty() && m_events.top() <= m_now) m_events.pop();
 
  157        if (!m_events.empty()) {
 
  158            m_now = m_events.top();
 
  163    void DisconnectedPeer(
int peer)
 
  166        for (
int txhash = 0; txhash < MAX_TXHASHES; ++txhash) {
 
  167            if (m_announcements[txhash][peer].m_state != State::NOTHING) {
 
  168                m_announcements[txhash][peer].m_state = State::NOTHING;
 
  177    void ForgetTxHash(
int txhash)
 
  180        for (
int peer = 0; peer < MAX_PEERS; ++peer) {
 
  181            m_announcements[txhash][peer].m_state = State::NOTHING;
 
  189    void ReceivedInv(
int peer, 
int txhash, 
bool is_wtxid, 
bool preferred, std::chrono::microseconds reqtime)
 
  193        Announcement& ann = m_announcements[txhash][peer];
 
  194        if (ann.m_state == State::NOTHING) {
 
  195            ann.m_preferred = preferred;
 
  196            ann.m_state = State::CANDIDATE;
 
  197            ann.m_time = reqtime;
 
  198            ann.m_is_wtxid = is_wtxid;
 
  199            ann.m_sequence = m_current_sequence++;
 
  200            ann.m_priority = m_tracker.
ComputePriority(TXHASHES[txhash], peer, ann.m_preferred);
 
  203            if (reqtime > m_now) m_events.push(reqtime);
 
  210    void RequestedTx(
int peer, 
int txhash, std::chrono::microseconds exptime)
 
  214        if (m_announcements[txhash][peer].m_state == State::CANDIDATE) {
 
  215            for (
int peer2 = 0; peer2 < MAX_PEERS; ++peer2) {
 
  216                if (m_announcements[txhash][peer2].m_state == State::REQUESTED) {
 
  217                    m_announcements[txhash][peer2].m_state = State::COMPLETED;
 
  220            m_announcements[txhash][peer].m_state = State::REQUESTED;
 
  221            m_announcements[txhash][peer].m_time = exptime;
 
  225        if (exptime > m_now) m_events.push(exptime);
 
  228        m_tracker.
RequestedTx(peer, TXHASHES[txhash], exptime);
 
  231    void ReceivedResponse(
int peer, 
int txhash)
 
  234        if (m_announcements[txhash][peer].m_state != State::NOTHING) {
 
  235            m_announcements[txhash][peer].m_state = State::COMPLETED;
 
  243    void GetRequestable(
int peer)
 
  248        std::vector<std::tuple<uint64_t, int, bool>> result;
 
  249        std::vector<std::pair<NodeId, GenTxid>> expected_expired;
 
  250        for (
int txhash = 0; txhash < MAX_TXHASHES; ++txhash) {
 
  252            for (
int peer2 = 0; peer2 < MAX_PEERS; ++peer2) {
 
  253                Announcement& ann2 = m_announcements[txhash][peer2];
 
  254                if (ann2.m_state == State::REQUESTED && ann2.m_time <= m_now) {
 
  256                    ann2.m_state = State::COMPLETED;
 
  263            const Announcement& ann = m_announcements[txhash][peer];
 
  264            if (ann.m_state == State::CANDIDATE && GetSelected(txhash) == peer) {
 
  265                result.emplace_back(ann.m_sequence, txhash, ann.m_is_wtxid);
 
  269        std::sort(result.begin(), result.end());
 
  270        std::sort(expected_expired.begin(), expected_expired.end());
 
  273        std::vector<std::pair<NodeId, GenTxid>> expired;
 
  274        const auto actual = m_tracker.
GetRequestable(peer, m_now, &expired);
 
  275        std::sort(expired.begin(), expired.end());
 
  276        assert(expired == expected_expired);
 
  279        assert(result.size() == actual.size());
 
  280        for (
size_t pos = 0; pos < actual.size(); ++pos) {
 
  281            assert(TXHASHES[std::get<1>(result[pos])] == actual[pos].GetHash());
 
  282            assert(std::get<2>(result[pos]) == actual[pos].IsWtxid());
 
  290        for (
int peer = 0; peer < MAX_PEERS; ++peer) {
 
  293            size_t candidates = 0;
 
  294            for (
int txhash = 0; txhash < MAX_TXHASHES; ++txhash) {
 
  295                tracked += m_announcements[txhash][peer].m_state != State::NOTHING;
 
  296                inflight += m_announcements[txhash][peer].m_state == State::REQUESTED;
 
  297                candidates += m_announcements[txhash][peer].m_state == State::CANDIDATE;
 
  319    auto it = buffer.begin();
 
  320    while (it != buffer.end()) {
 
  321        int cmd = *(it++) % 11;
 
  322        int peer, txidnum, delaynum;
 
  325            tester.AdvanceToEvent();
 
  328            delaynum = it == buffer.end() ? 0 : *(it++);
 
  329            tester.AdvanceTime(DELAYS[delaynum]);
 
  332            peer = it == buffer.end() ? 0 : *(it++) % MAX_PEERS;
 
  333            tester.GetRequestable(peer);
 
  336            peer = it == buffer.end() ? 0 : *(it++) % MAX_PEERS;
 
  337            tester.DisconnectedPeer(peer);
 
  340            txidnum = it == buffer.end() ? 0 : *(it++);
 
  341            tester.ForgetTxHash(txidnum % MAX_TXHASHES);
 
  345            peer = it == buffer.end() ? 0 : *(it++) % MAX_PEERS;
 
  346            txidnum = it == buffer.end() ? 0 : *(it++);
 
  347            tester.ReceivedInv(peer, txidnum % MAX_TXHASHES, (txidnum / MAX_TXHASHES) & 1, cmd & 1,
 
  348                std::chrono::microseconds::min());
 
  352            peer = it == buffer.end() ? 0 : *(it++) % MAX_PEERS;
 
  353            txidnum = it == buffer.end() ? 0 : *(it++);
 
  354            delaynum = it == buffer.end() ? 0 : *(it++);
 
  355            tester.ReceivedInv(peer, txidnum % MAX_TXHASHES, (txidnum / MAX_TXHASHES) & 1, cmd & 1,
 
  356                tester.Now() + DELAYS[delaynum]);
 
  359            peer = it == buffer.end() ? 0 : *(it++) % MAX_PEERS;
 
  360            txidnum = it == buffer.end() ? 0 : *(it++);
 
  361            delaynum = it == buffer.end() ? 0 : *(it++);
 
  362            tester.RequestedTx(peer, txidnum % MAX_TXHASHES, tester.Now() + DELAYS[delaynum]);
 
  365            peer = it == buffer.end() ? 0 : *(it++) % MAX_PEERS;
 
  366            txidnum = it == buffer.end() ? 0 : *(it++);
 
  367            tester.ReceivedResponse(peer, txidnum % MAX_TXHASHES);
 
A hasher class for SHA-256.
void Finalize(unsigned char hash[OUTPUT_SIZE])
CSHA256 & Write(const unsigned char *data, size_t len)
uint64_t Finalize() const
Compute the 64-bit SipHash-2-4 of the data written so far.
CSipHasher & Write(uint64_t data)
Hash a 64-bit integer worth of data It is treated as if this was the little-endian interpretation of ...
static GenTxid Wtxid(const uint256 &hash)
static GenTxid Txid(const uint256 &hash)
Data structure to keep track of, and schedule, transaction downloads from peers.
void ReceivedInv(NodeId peer, const GenTxid >xid, bool preferred, std::chrono::microseconds reqtime)
Adds a new CANDIDATE announcement.
void SanityCheck() const
Run internal consistency check (testing only).
size_t CountInFlight(NodeId peer) const
Count how many REQUESTED announcements a peer has.
size_t CountCandidates(NodeId peer) const
Count how many CANDIDATE announcements a peer has.
void DisconnectedPeer(NodeId peer)
Deletes all announcements for a given peer.
void ReceivedResponse(NodeId peer, const uint256 &txhash)
Converts a CANDIDATE or REQUESTED announcement to a COMPLETED one.
uint64_t ComputePriority(const uint256 &txhash, NodeId peer, bool preferred) const
Access to the internal priority computation (testing only)
void PostGetRequestableSanityCheck(std::chrono::microseconds now) const
Run a time-dependent internal consistency check (testing only).
void RequestedTx(NodeId peer, const uint256 &txhash, std::chrono::microseconds expiry)
Marks a transaction as requested, with a specified expiry.
size_t Count(NodeId peer) const
Count how many announcements a peer has (REQUESTED, CANDIDATE, and COMPLETED combined).
size_t Size() const
Count how many announcements are being tracked in total across all peers and transaction hashes.
std::vector< GenTxid > GetRequestable(NodeId peer, std::chrono::microseconds now, std::vector< std::pair< NodeId, GenTxid > > *expired=nullptr)
Find the txids to request now from peer.
void ForgetTxHash(const uint256 &txhash)
Deletes all announcements for a given txhash (both txid and wtxid ones).