diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 23cb8f7d8c634..4363fc13310f7 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -187,6 +187,51 @@ struct QueuedBlock { std::unique_ptr partialBlock; }; +class TimeOffsets +{ +private: + // Historic maximum number of time offsets we include in our median. + static constexpr size_t N{199}; + + mutable Mutex m_mutex; + std::array m_offsets GUARDED_BY(m_mutex){}; + size_t m_index GUARDED_BY(m_mutex){0}; + +public: + /** Add a new time offset sample. */ + void Add(int64_t offset) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex) + { + LOCK(m_mutex); + + // Stop sampling after N samples. + // TODO This maintains a historic bug of adjusted time and could be + // changed in the future. + if (m_index >= N) return; + + m_offsets[m_index] = offset; + ++m_index; + } + + /** Compute and return the median of the collected time offset samples. */ + int64_t Median() const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex) + { + LOCK(m_mutex); + + // Only compute the median from 5 or more samples. + if (m_index < 4) return 0; + + std::array sorted_copy = m_offsets; + std::sort(sorted_copy.begin(), sorted_copy.begin() + m_index); + + if (m_index % 2 == 0) { + return (sorted_copy[m_index / 2] / 2) + + (sorted_copy[m_index / 2 - 1] / 2); + } else { + return sorted_copy[m_index / 2]; + } + } +}; + /** * Data structure for an individual peer. This struct is not protected by * cs_main since it does not contain validation-critical data. @@ -729,6 +774,8 @@ class PeerManagerImpl final : public PeerManager /** Next time to check for stale tip */ std::chrono::seconds m_stale_tip_check_time GUARDED_BY(cs_main){0s}; + TimeOffsets m_outbound_time_offsets; + const Options m_opts; bool RejectIncomingTxs(const CNode& peer) const; @@ -3558,6 +3605,8 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, // Don't use timedata samples from inbound peers to make it // harder for others to tamper with our adjusted time. AddTimeData(pfrom.addr, peer->m_time_offset); + + m_outbound_time_offsets.Add(peer->m_time_offset); } // If the peer is old enough to have the old alert system, send it the final alert.