Files
rippled/include/xrpl/tx/paths/OfferStream.h
2026-07-13 10:40:40 +00:00

160 lines
4.1 KiB
C++

#pragma once
#include <xrpl/basics/Log.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/chrono.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/protocol/Book.h>
#include <xrpl/protocol/Concepts.h>
#include <xrpl/tx/paths/BookTip.h>
#include <xrpl/tx/paths/Offer.h>
#include <boost/container/flat_set.hpp>
#include <cstdint>
#include <optional>
namespace xrpl {
template <StepAmount TIn, StepAmount TOut>
class TOfferStreamBase
{
public:
class StepCounter
{
private:
std::uint32_t const limit_;
std::uint32_t count_{0};
beast::Journal j_;
public:
StepCounter(std::uint32_t limit, beast::Journal j) : limit_(limit), j_(j)
{
}
bool
step()
{
if (count_ >= limit_)
{
JLOG(j_.debug()) << "Exceeded " << limit_ << " step limit.";
return false;
}
count_++;
return true;
}
[[nodiscard]] std::uint32_t
count() const
{
return count_;
}
};
protected:
beast::Journal const j_;
ApplyView& view_;
ApplyView& cancelView_;
Book book_;
bool validBook_;
NetClock::time_point const expire_;
BookTip tip_;
TOffer<TIn, TOut> offer_;
std::optional<TOut> ownerFunds_;
StepCounter& counter_;
void
erase(ApplyView& view);
virtual void
permRmOffer(uint256 const& offerIndex) = 0;
template <class TTakerPays, class TTakerGets>
requires ValidTaker<TTakerPays, TTakerGets>
[[nodiscard]] bool
shouldRmSmallIncreasedQOffer() const;
public:
TOfferStreamBase(
ApplyView& view,
ApplyView& cancelView,
Book const& book,
NetClock::time_point when,
StepCounter& counter,
beast::Journal journal);
virtual ~TOfferStreamBase() = default;
/**
* Returns the offer at the tip of the order book.
* Offers are always presented in decreasing quality.
* Only valid if step() returned `true`.
*/
[[nodiscard]] TOffer<TIn, TOut>&
tip() const
{
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
return const_cast<TOfferStreamBase*>(this)->offer_;
}
/**
* Advance to the next valid offer.
* This automatically removes:
* - Offers with missing ledger entries
* - Offers found unfunded
* - expired offers
* @return `true` if there is a valid offer.
*/
bool
step();
[[nodiscard]] TOut
ownerFunds() const
{
return *ownerFunds_; // NOLINT(bugprone-unchecked-optional-access) always set after step()
// is called
}
};
/**
* Presents and consumes the offers in an order book.
*
* The `view_' ` `ApplyView` accumulates changes to the ledger.
* The `cancelView_` is used to determine if an offer is found
* unfunded or became unfunded.
* The `permToRemove` collection identifies offers that should be
* removed even if the strand associated with this OfferStream
* is not applied.
*
* Certain invalid offers are added to the `permToRemove` collection:
* - Offers with missing ledger entries
* - Offers that expired
* - Offers found unfunded:
* An offer is found unfunded when the corresponding balance is zero
* and the caller has not modified the balance. This is accomplished
* by also looking up the balance in the cancel view.
*/
template <StepAmount TIn, StepAmount TOut>
class FlowOfferStream : public TOfferStreamBase<TIn, TOut>
{
private:
boost::container::flat_set<uint256> permToRemove_;
public:
using TOfferStreamBase<TIn, TOut>::TOfferStreamBase;
// The following interface allows offer crossing to permanently
// remove self crossed offers. The motivation is somewhat
// unintuitive. See the discussion in the comments for
// BookOfferCrossingStep::limitSelfCrossQuality().
void
permRmOffer(uint256 const& offerIndex) override;
[[nodiscard]] boost::container::flat_set<uint256> const&
permToRemove() const
{
return permToRemove_;
}
};
} // namespace xrpl