Files
rippled/src/tests/libxrpl/csf/impl/ledgers.cpp
2026-07-23 21:38:21 +00:00

168 lines
4.1 KiB
C++

#include <csf/ledgers.h>
#include <xrpl/basics/chrono.h>
#include <xrpl/json/json_value.h>
#include <xrpl/ledger/LedgerTiming.h>
#include <csf/Tx.h>
#include <algorithm>
#include <chrono>
#include <cstddef>
#include <optional>
#include <set>
#include <vector>
namespace xrpl::test::csf {
Ledger::Instance const Ledger::kGenesis;
json::Value
Ledger::getJson() const
{
json::Value res(json::ValueType::Object);
res["id"] = static_cast<ID::value_type>(id());
res["seq"] = static_cast<Seq::value_type>(seq());
return res;
}
bool
Ledger::isAncestor(Ledger const& ancestor) const
{
if (ancestor.seq() < seq())
return operator[](ancestor.seq()) == ancestor.id();
return false;
}
Ledger::ID
Ledger::operator[](Seq s) const
{
if (s > seq())
return {};
if (s == seq())
return id();
return instance_->ancestors[static_cast<Seq::value_type>(s)];
}
Ledger::Seq
mismatch(Ledger const& a, Ledger const& b)
{
using Seq = Ledger::Seq;
// end is 1 past end of range
Seq start{0};
Seq const end = std::min(a.seq() + Seq{1}, b.seq() + Seq{1});
// Find mismatch in [start,end)
// Binary search
Seq count = end - start;
while (count > Seq{0})
{
Seq const step = count / Seq{2};
Seq curr = start + step;
if (a[curr] == b[curr])
{
// go to second half
start = ++curr;
count -= step + Seq{1};
}
else
{
count = step;
}
}
return start;
}
LedgerOracle::LedgerOracle()
{
instances_.insert(InstanceEntry{Ledger::kGenesis, nextID()});
}
Ledger::ID
LedgerOracle::nextID() const
{
return Ledger::ID{static_cast<Ledger::ID::value_type>(instances_.size())};
}
Ledger
LedgerOracle::accept(
Ledger const& parent,
TxSetType const& txs,
NetClock::duration closeTimeResolution,
NetClock::time_point const& consensusCloseTime)
{
using namespace std::chrono_literals;
Ledger::Instance next(*parent.instance_);
next.txs.insert(txs.begin(), txs.end());
next.seq = parent.seq() + Ledger::Seq{1};
next.closeTimeResolution = closeTimeResolution;
next.closeTimeAgree = consensusCloseTime != NetClock::time_point{};
if (next.closeTimeAgree)
{
next.closeTime = effCloseTime(consensusCloseTime, closeTimeResolution, parent.closeTime());
}
else
{
next.closeTime = parent.closeTime() + 1s;
}
next.parentCloseTime = parent.closeTime();
next.parentID = parent.id();
next.ancestors.push_back(parent.id());
auto it = instances_.left.find(next);
if (it == instances_.left.end())
{
using Entry = InstanceMap::left_value_type;
it = instances_.left.insert(Entry{next, nextID()}).first;
}
return Ledger(it->second, &(it->first));
}
std::optional<Ledger>
LedgerOracle::lookup(Ledger::ID const& id) const
{
auto const it = instances_.right.find(id);
if (it != instances_.right.end())
{
return Ledger(it->first, &(it->second));
}
return std::nullopt;
}
std::size_t
LedgerOracle::branches(std::set<Ledger> const& ledgers)
{
// Tips always maintains the Ledgers with largest sequence number
// along all known chains.
std::vector<Ledger> tips;
tips.reserve(ledgers.size());
for (Ledger const& ledger : ledgers)
{
// Three options,
// 1. ledger is on a new branch
// 2. ledger is on a branch that we have seen tip for
// 3. ledger is the new tip for a branch
bool found = false;
for (auto idx = 0; idx < tips.size() && !found; ++idx)
{
bool const idxEarlier = tips[idx].seq() < ledger.seq();
Ledger const& earlier = idxEarlier ? tips[idx] : ledger;
Ledger const& later = idxEarlier ? ledger : tips[idx];
if (later.isAncestor(earlier))
{
tips[idx] = later;
found = true;
}
}
if (!found)
tips.push_back(ledger);
}
// The size of tips is the number of branches
return tips.size();
}
} // namespace xrpl::test::csf