Files
rippled/include/xrpl/ledger/PaymentSandbox.h
2026-07-13 10:40:40 +00:00

254 lines
7.6 KiB
C++

#pragma once
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/OwnerCounts.h>
#include <xrpl/ledger/RawView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/detail/ApplyViewBase.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/MPTIssue.h>
#include <xrpl/protocol/STAmount.h>
#include <xrpl/protocol/UintTypes.h>
#include <xrpl/protocol/XRPAmount.h>
#include <cstdint>
#include <map>
#include <optional>
#include <tuple>
#include <utility>
namespace xrpl {
namespace detail {
// VFALCO TODO Inline this implementation
// into the PaymentSandbox class itself
class DeferredCredits
{
private:
using KeyIOU = std::tuple<AccountID, AccountID, Currency>;
struct ValueIOU
{
explicit ValueIOU() = default;
STAmount lowAcctDebits;
STAmount highAcctDebits;
STAmount lowAcctOrigBalance;
};
struct HolderValueMPT
{
HolderValueMPT() = default;
// Debit to issuer
std::uint64_t debit = 0;
std::uint64_t origBalance = 0;
};
struct IssuerValueMPT
{
IssuerValueMPT() = default;
std::map<AccountID, HolderValueMPT> holders;
// Credit to holder
std::uint64_t credit = 0;
// OutstandingAmount might overflow when MPTs are credited to a holder.
// Consider A1 paying 100MPT to A2 and A1 already having maximum MPTs.
// Since the payment engine executes a payment in revers, A2 is
// credited first and OutstandingAmount is going to be equal
// to MaximumAmount + 100MPT. In the next step A1 redeems 100MPT
// to the issuer and OutstandingAmount balances out.
std::int64_t origBalance = 0;
// Self debit on offer selling MPT. Since the payment engine executes
// a payment in reverse, a crediting/buying step may overflow
// OutstandingAmount. A sell MPT offer owned by a holder can redeem any
// amount up to the offer's amount and holder's available funds,
// balancing out OutstandingAmount. But if the offer's owner is issuer
// then it issues more MPT. In this case the available amount to issue
// is the initial issuer's available amount less all offer sell amounts
// by the issuer. This is self-debit, where the offer's owner,
// issuer in this case, debits to self.
std::uint64_t selfDebit = 0;
};
using AdjustmentMPT = IssuerValueMPT;
public:
struct AdjustmentIOU
{
AdjustmentIOU(STAmount d, STAmount c, STAmount b)
: debits(std::move(d)), credits(std::move(c)), origBalance(std::move(b))
{
}
STAmount debits;
STAmount credits;
STAmount origBalance;
};
// Get the adjustments for the balance between main and other.
// Returns the debits, credits and the original balance
[[nodiscard]] std::optional<AdjustmentIOU>
adjustmentsIOU(AccountID const& main, AccountID const& other, Currency const& currency) const;
[[nodiscard]] std::optional<AdjustmentMPT>
adjustmentsMPT(MPTID const& mptID) const;
void
creditIOU(
AccountID const& sender,
AccountID const& receiver,
STAmount const& amount,
STAmount const& preCreditSenderBalance);
void
creditMPT(
AccountID const& sender,
AccountID const& receiver,
STAmount const& amount,
std::uint64_t preCreditBalanceHolder,
std::int64_t preCreditBalanceIssuer);
void
issuerSelfDebitMPT(MPTIssue const& issue, std::uint64_t amount, std::int64_t origBalance);
void
ownerCount(AccountID const& id, OwnerCounts const& cur, OwnerCounts const& next);
// Get the adjusted owner count. Since DeferredCredits is meant to be used
// in payments, and payments only decrease owner counts, return the max
// remembered owner count.
[[nodiscard]] std::optional<OwnerCounts>
ownerCount(AccountID const& id) const;
void
apply(DeferredCredits& to);
private:
static KeyIOU
makeKeyIOU(AccountID const& a1, AccountID const& a2, Currency const& currency);
std::map<KeyIOU, ValueIOU> creditsIOU_;
std::map<MPTID, IssuerValueMPT> creditsMPT_;
std::map<AccountID, OwnerCounts> ownerCounts_;
};
} // namespace detail
//------------------------------------------------------------------------------
/**
* A wrapper which makes credits unavailable to balances.
*
* This is used for payments and pathfinding, so that consuming
* liquidity from a path never causes portions of that path or
* other paths to gain liquidity.
*
* The behavior of certain free functions in the ApplyView API
* will change via the balanceHook and creditHook overrides
* of PaymentSandbox.
*
* @note Presented as ApplyView to clients
*/
class PaymentSandbox final : public detail::ApplyViewBase
{
public:
PaymentSandbox() = delete;
PaymentSandbox(PaymentSandbox const&) = delete;
PaymentSandbox&
operator=(PaymentSandbox&&) = delete;
PaymentSandbox&
operator=(PaymentSandbox const&) = delete;
PaymentSandbox(PaymentSandbox&&) = default;
PaymentSandbox(ReadView const* base, ApplyFlags flags) : ApplyViewBase(base, flags)
{
}
PaymentSandbox(ApplyView const* base) : ApplyViewBase(base, base->flags())
{
}
/**
* Construct on top of existing PaymentSandbox.
*
* The changes are pushed to the parent when
* apply() is called.
*
* @param parent A non-null pointer to the parent.
*
* @note A pointer is used to prevent confusion
* with copy construction.
*/
// VFALCO If we are constructing on top of a PaymentSandbox,
// or a PaymentSandbox-derived class, we MUST go through
// one of these constructors or invariants will be broken.
/** @{ */
explicit PaymentSandbox(PaymentSandbox const* base)
: ApplyViewBase(base, base->flags()), ps_(base)
{
}
explicit PaymentSandbox(PaymentSandbox* base) : ApplyViewBase(base, base->flags()), ps_(base)
{
}
/** @} */
[[nodiscard]] STAmount
balanceHookIOU(AccountID const& account, AccountID const& issuer, STAmount const& amount)
const override;
[[nodiscard]] STAmount
balanceHookMPT(AccountID const& account, MPTIssue const& issue, std::int64_t amount)
const override;
[[nodiscard]] STAmount
balanceHookSelfIssueMPT(MPTIssue const& issue, std::int64_t amount) const override;
void
creditHookIOU(
AccountID const& from,
AccountID const& to,
STAmount const& amount,
STAmount const& preCreditBalance) override;
void
creditHookMPT(
AccountID const& from,
AccountID const& to,
STAmount const& amount,
std::uint64_t preCreditBalanceHolder,
std::int64_t preCreditBalanceIssuer) override;
void
issuerSelfDebitHookMPT(MPTIssue const& issue, std::uint64_t amount, std::int64_t origBalance)
override;
void
adjustOwnerCountHook(AccountID const& account, OwnerCounts const& cur, OwnerCounts const& next)
override;
[[nodiscard]] OwnerCounts
ownerCountHook(AccountID const& account, OwnerCounts const& count) const override;
/**
* Apply changes to base view.
*
* `to` must contain contents identical to the parent
* view passed upon construction, else undefined
* behavior will result.
*/
/** @{ */
void
apply(RawView& to);
void
apply(PaymentSandbox& to);
/** @} */
[[nodiscard]] XRPAmount
xrpDestroyed() const;
private:
detail::DeferredCredits tab_;
PaymentSandbox const* ps_ = nullptr;
};
} // namespace xrpl