Files
rippled/include/xrpl/consensus/CensorshipDetector.h
2026-07-23 21:38:21 +00:00

127 lines
3.5 KiB
C++

#pragma once
#include <xrpl/basics/algorithm.h>
#include <algorithm>
#include <functional>
#include <utility>
#include <vector>
namespace xrpl {
template <class TxID, class Sequence>
class CensorshipDetector
{
public:
struct TxIDSeq
{
TxID txid;
Sequence seq;
TxIDSeq(TxID const& txid, Sequence const& seq) : txid(txid), seq(seq)
{
}
};
friend bool
operator<(TxIDSeq const& lhs, TxIDSeq const& rhs)
{
if (lhs.txid != rhs.txid)
return lhs.txid < rhs.txid;
return lhs.seq < rhs.seq;
}
friend bool
operator<(TxIDSeq const& lhs, TxID const& rhs)
{
return lhs.txid < rhs;
}
friend bool
operator<(TxID const& lhs, TxIDSeq const& rhs)
{
return lhs < rhs.txid;
}
using TxIDSeqVec = std::vector<TxIDSeq>;
private:
TxIDSeqVec tracker_;
public:
CensorshipDetector() = default;
/**
* Add transactions being proposed for the current consensus round.
*
* @param proposed The set of transactions that we are initially proposing
* for this round.
*/
void
propose(TxIDSeqVec proposed)
{
// We want to remove any entries that we proposed in a previous round
// that did not make it in yet if we are no longer proposing them.
// And we also want to preserve the Sequence of entries that we proposed
// in the last round and want to propose again.
std::sort(proposed.begin(), proposed.end());
generalizedSetIntersection(
proposed.begin(),
proposed.end(),
tracker_.cbegin(),
tracker_.cend(),
[](auto& x, auto const& y) { x.seq = y.seq; },
[](auto const& x, auto const& y) { return x.txid < y.txid; });
tracker_ = std::move(proposed);
}
/**
* Determine which transactions made it and perform censorship detection.
*
* This function is called when the server is proposing and a consensus
* round it participated in completed.
*
* @param accepted The set of transactions that the network agreed
* should be included in the ledger being built.
* @param pred A predicate invoked for every transaction we've proposed
* but which hasn't yet made it. The predicate must be
* callable as:
* bool pred(TxID const&, Sequence)
* It must return true for entries that should be removed.
*/
template <class Predicate>
void
check(std::vector<TxID> accepted, Predicate&& pred)
{
auto acceptTxid = accepted.begin();
auto const ae = accepted.end();
std::sort(acceptTxid, ae);
// We want to remove all tracking entries for transactions that were
// accepted as well as those which match the predicate.
auto i = removeIfIntersectOrMatch(
tracker_.begin(),
tracker_.end(),
accepted.begin(),
accepted.end(),
[&pred](auto const& x) { return pred(x.txid, x.seq); },
std::less<void>{});
tracker_.erase(i, tracker_.end());
}
/**
* Removes all elements from the tracker
*
* Typically, this function might be called after we reconnect to the
* network following an outage, or after we start tracking the network.
*/
void
reset()
{
tracker_.clear();
}
};
} // namespace xrpl