Files
rippled/src/test/csf/Tx.h
2026-04-01 15:46:14 +00:00

214 lines
4.1 KiB
C++

#pragma once
#include <xrpl/beast/hash/hash_append.h>
#include <xrpl/beast/hash/uhash.h>
#include <boost/container/flat_set.hpp>
#include <boost/iterator/function_output_iterator.hpp>
#include <algorithm>
#include <map>
#include <ostream>
#include <sstream>
#include <string>
#include <type_traits>
namespace xrpl {
namespace test {
namespace csf {
//! A single transaction
class Tx
{
public:
using ID = std::uint32_t;
Tx(ID i) : id_{i}
{
}
template <typename T, typename = std::enable_if_t<std::is_same_v<T, Tx>>>
Tx(T const* t) : id_{t->id_}
{
}
ID const&
id() const
{
return id_;
}
bool
operator<(Tx const& o) const
{
return id_ < o.id_;
}
bool
operator==(Tx const& o) const
{
return id_ == o.id_;
}
private:
ID id_;
};
//!-------------------------------------------------------------------------
//! All sets of Tx are represented as a flat_set for performance.
using TxSetType = boost::container::flat_set<Tx>;
//! TxSet is a set of transactions to consider including in the ledger
class TxSet
{
public:
using ID = beast::uhash<>::result_type;
using Tx = csf::Tx;
static ID
calcID(TxSetType const& txs)
{
return beast::uhash<>{}(txs);
}
class MutableTxSet
{
friend class TxSet;
TxSetType txs_;
public:
MutableTxSet(TxSet const& s) : txs_{s.txs_}
{
}
bool
insert(Tx const& t)
{
return txs_.insert(t).second;
}
bool
erase(Tx::ID const& txId)
{
return txs_.erase(Tx{txId}) > 0;
}
};
TxSet() = default;
TxSet(TxSetType const& s) : txs_{s}, id_{calcID(txs_)}
{
}
TxSet(MutableTxSet&& m) // NOLINT(cppcoreguidelines-rvalue-reference-param-not-moved)
: txs_{m.txs_}, id_{calcID(txs_)}
{
}
bool
exists(Tx::ID const txId) const
{
auto it = txs_.find(Tx{txId});
return it != txs_.end();
}
Tx const*
find(Tx::ID const& txId) const
{
auto it = txs_.find(Tx{txId});
if (it != txs_.end())
return &(*it);
return nullptr;
}
TxSetType const&
txs() const
{
return txs_;
}
ID
id() const
{
return id_;
}
/** @return Map of Tx::ID that are missing. True means
it was in this set and not other. False means
it was in the other set and not this
*/
std::map<Tx::ID, bool>
compare(TxSet const& other) const
{
std::map<Tx::ID, bool> res;
auto populate_diffs = [&res](auto const& a, auto const& b, bool s) {
auto populator = [&](auto const& tx) { res[tx.id()] = s; };
std::set_difference(
a.begin(),
a.end(),
b.begin(),
b.end(),
boost::make_function_output_iterator(std::ref(populator)));
};
populate_diffs(txs_, other.txs_, true);
populate_diffs(other.txs_, txs_, false);
return res;
}
private:
//! The set contains the actual transactions
TxSetType txs_;
//! The unique ID of this tx set
ID id_{};
};
//------------------------------------------------------------------------------
// Helper functions for debug printing
inline std::ostream&
operator<<(std::ostream& o, Tx const& t)
{
return o << t.id();
}
template <class T>
inline std::ostream&
operator<<(std::ostream& o, boost::container::flat_set<T> const& ts)
{
o << "{ ";
bool do_comma = false;
for (auto const& t : ts)
{
if (do_comma)
o << ", ";
else
do_comma = true;
o << t;
}
o << " }";
return o;
}
inline std::string
to_string(TxSetType const& txs)
{
std::stringstream ss;
ss << txs;
return ss.str();
}
template <class Hasher>
inline void
hash_append(Hasher& h, Tx const& tx)
{
using beast::hash_append;
hash_append(h, tx.id());
}
} // namespace csf
} // namespace test
} // namespace xrpl