set up framework

This commit is contained in:
Mayukha Vadari
2026-07-09 21:13:54 -04:00
parent c7adb215ed
commit 628272a11e
6 changed files with 862 additions and 0 deletions

View File

@@ -279,6 +279,7 @@ words:
- sles
- soci
- socidb
- sponsee
- SRPMS
- sslws
- statsd
@@ -327,6 +328,7 @@ words:
- unserviced
- unshareable
- unshares
- unsponsored
- unsquelch
- unsquelched
- unsquelching

View File

@@ -0,0 +1,31 @@
#pragma once
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STArray.h> // IWYU pragma: keep
#include <xrpl/protocol/STLedgerEntry.h>
#include <cstddef>
#include <cstdint>
namespace xrpl {
constexpr std::uint32_t kMinOracleReserveCount = 1;
constexpr std::uint32_t kMaxOracleReserveCount = 2;
constexpr std::size_t kOracleReserveCountThreshold = 5;
template <typename T>
requires requires(T const& t) { t.size(); }
inline std::uint32_t
calculateOracleReserve(T const& priceDataSeries)
{
return priceDataSeries.size() > kOracleReserveCountThreshold ? kMaxOracleReserveCount
: kMinOracleReserveCount;
}
inline std::uint32_t
calculateOracleReserve(SLE::const_ref oracleSle)
{
return calculateOracleReserve(oracleSle->getFieldArray(sfPriceDataSeries));
}
} // namespace xrpl

View File

@@ -0,0 +1,250 @@
#pragma once
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/protocol/STLedgerEntry.h>
#include <concepts>
#include <memory>
#include <stdexcept>
#include <type_traits>
namespace xrpl {
// Concept to distinguish read-only vs writable view types
template <typename V>
concept WritableView = std::derived_from<V, ApplyView>;
/**
* View-parameterized base class for all ledger entry wrappers.
*
* SLEBase<ReadView> — read-only: holds shared_ptr<SLE const> + ReadView const&
* SLEBase<ApplyView> — writable: holds shared_ptr<SLE> + ApplyView& + Keylet,
* plus insert/update/erase operations
*
* Write-only members are gated by `requires` clauses, providing compile-time
* guarantees that read-only wrappers cannot mutate state.
*
* Derived classes should provide domain-specific accessors that hide
* implementation details of the underlying ledger entry format.
*/
template <typename ViewT>
class SLEBase
{
public:
static constexpr bool kIsWritable = WritableView<ViewT>;
// SLE pointer type: mutable for writable views, const for read-only
using sle_ptr_type = std::conditional_t<kIsWritable, std::shared_ptr<SLE>, SLE::const_pointer>;
// View reference type: ApplyView& for writable, ReadView const& for read-only
using view_ref_type = std::conditional_t<kIsWritable, ApplyView&, ReadView const&>;
virtual ~SLEBase() = default;
SLEBase(SLEBase const&) = default;
SLEBase(SLEBase&&) = default;
SLEBase&
operator=(SLEBase const&) = delete;
SLEBase&
operator=(SLEBase&&) = delete;
SLEBase() = delete;
// --- Common interface (always available) ---
/** Returns true if the ledger entry exists */
[[nodiscard]] bool
exists() const
{
return sle_ != nullptr;
}
/** Explicit conversion to bool for convenient existence checking */
explicit
operator bool() const
{
return exists();
}
/** Returns the underlying SLE for read access */
[[nodiscard]] SLE::const_pointer
sle() const
{
return sle_;
}
/** Returns the read view (always available; ApplyView inherits ReadView) */
[[nodiscard]] ReadView const&
readView() const
{
return view_;
}
/** Const dereference operators (always available) */
STLedgerEntry const*
operator->() const
{
XRPL_ASSERT(exists(), "xrpl::SLEBase::operator-> : exists");
return sle_.get();
}
STLedgerEntry const&
operator*() const
{
XRPL_ASSERT(exists(), "xrpl::SLEBase::operator* : exists");
return *sle_;
}
// --- Writable interface (compile-time gated) ---
/** Returns a mutable SLE for write operations */
[[nodiscard]] sle_ptr_type const&
mutableSle() const
requires kIsWritable
{
return sle_;
}
/** Returns true if this wrapper supports write operations */
[[nodiscard]] bool
canModify() const
requires kIsWritable
{
return sle_ != nullptr;
}
/** Returns the apply view for write operations */
[[nodiscard]] ApplyView&
applyView() const
requires kIsWritable
{
return view_;
}
/** Mutable dereference operators */
STLedgerEntry*
operator->()
requires kIsWritable
{
XRPL_ASSERT(canModify(), "xrpl::SLEBase::operator-> : can modify");
return sle_.get();
}
STLedgerEntry&
operator*()
requires kIsWritable
{
XRPL_ASSERT(canModify(), "xrpl::SLEBase::operator* : can modify");
return *sle_;
}
void
insert()
requires kIsWritable
{
XRPL_ASSERT(canModify(), "xrpl::SLEBase::insert : can modify");
view_.insert(sle_);
}
void
erase()
requires kIsWritable
{
XRPL_ASSERT(canModify(), "xrpl::SLEBase::erase : can modify");
view_.erase(sle_);
}
void
update()
requires kIsWritable
{
XRPL_ASSERT(canModify(), "xrpl::SLEBase::update : can modify");
view_.update(sle_);
}
void
newSLE()
requires kIsWritable
{
XRPL_ASSERT(!canModify(), "xrpl::SLEBase::newSLE : no existing SLE");
sle_ = std::make_shared<SLE>(key_);
}
[[nodiscard]] beast::Journal
journal() const
{
return j_;
}
protected:
/** Constructor for read-only context */
explicit SLEBase(
SLE::const_pointer sle,
ReadView const& view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
requires(!kIsWritable)
: view_(view), sle_(std::move(sle)), j_(j)
{
}
/** Constructor for read-only context (read from view by keylet) */
explicit SLEBase(
Keylet const& key,
ReadView const& view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
requires(!kIsWritable)
: view_(view), sle_(view.read(key)), j_(j)
{
}
/** Converting constructor: writable → read-only.
* Enables implicit conversion from SLEBase<ApplyView> to
* SLEBase<ReadView>, so functions taking ReadOnlySLE const& can
* accept WritableSLE.
*/
template <WritableView OtherViewT>
SLEBase(SLEBase<OtherViewT> const& other)
requires(!kIsWritable)
: view_(other.readView()), sle_(other.sle()), j_(other.journal())
{
}
/** Constructor for writable context (from existing SLE) */
explicit SLEBase(
std::shared_ptr<SLE> sle,
ApplyView& view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
requires kIsWritable
: view_(view)
, key_(sle ? Keylet(sle->getType(), sle->key()) : Keylet(ltANY, uint256{}))
, sle_(std::move(sle))
, j_(j)
{
}
/** Constructor for writable context (peek from view by keylet) */
explicit SLEBase(
Keylet const& key,
ApplyView& view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
requires kIsWritable
: view_(view), key_(key), sle_(view_.peek(key)), j_(j)
{
}
view_ref_type view_{};
// Keylet is only meaningful for writable views, but we conditionally
// include it to avoid wasting space in read-only wrappers.
struct Empty
{
};
[[no_unique_address]]
std::conditional_t<kIsWritable, Keylet, Empty> key_{};
sle_ptr_type sle_{};
beast::Journal j_;
};
} // namespace xrpl

View File

@@ -0,0 +1,450 @@
#pragma once
#include <xrpl/ledger/helpers/SLEBase.h>
#include <xrpl/protocol/Indexes.h>
#include <cstdint>
namespace xrpl {
/**
* Concrete, view-parameterized wrappers for each ledger entry type.
*
* Every wrapper derives from SLEBase<ViewT> and, for now, does nothing more
* than translate the entry's "keylet parts" (the arguments to its keylet::
* function) into a Keylet that the base class resolves against the view:
*
* AccountRoot<ApplyView> acct{id, view}; // peek (writable)
* AccountRoot<ReadView> acct{id, readView}; // read (read-only)
*
* Domain-specific accessors will be layered onto each wrapper over time.
*/
// Ordered to match include/xrpl/protocol/detail/ledger_entries.macro.
template <typename ViewT>
class NFTokenOffer : public SLEBase<ViewT>
{
public:
explicit NFTokenOffer(
AccountID const& owner,
std::uint32_t seq,
typename SLEBase<ViewT>::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: SLEBase<ViewT>(keylet::nftokenOffer(owner, seq), view, j)
{
}
};
template <typename ViewT>
class Check : public SLEBase<ViewT>
{
public:
explicit Check(
AccountID const& id,
std::uint32_t seq,
typename SLEBase<ViewT>::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: SLEBase<ViewT>(keylet::check(id, seq), view, j)
{
}
};
template <typename ViewT>
class DID : public SLEBase<ViewT>
{
public:
explicit DID(
AccountID const& account,
typename SLEBase<ViewT>::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: SLEBase<ViewT>(keylet::did(account), view, j)
{
}
};
template <typename ViewT>
class NegativeUNL : public SLEBase<ViewT>
{
public:
explicit NegativeUNL(
typename SLEBase<ViewT>::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: SLEBase<ViewT>(keylet::negativeUNL(), view, j)
{
}
};
template <typename ViewT>
class NFTokenPage : public SLEBase<ViewT>
{
public:
explicit NFTokenPage(
Keylet const& page,
uint256 const& token,
typename SLEBase<ViewT>::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: SLEBase<ViewT>(keylet::nftokenPage(page, token), view, j)
{
}
};
template <typename ViewT>
class SignerList : public SLEBase<ViewT>
{
public:
explicit SignerList(
AccountID const& account,
typename SLEBase<ViewT>::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: SLEBase<ViewT>(keylet::signerList(account), view, j)
{
}
};
template <typename ViewT>
class Ticket : public SLEBase<ViewT>
{
public:
explicit Ticket(
AccountID const& id,
std::uint32_t ticketSeq,
typename SLEBase<ViewT>::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: SLEBase<ViewT>(keylet::ticket(id, ticketSeq), view, j)
{
}
};
template <typename ViewT>
class AccountRoot : public SLEBase<ViewT>
{
public:
explicit AccountRoot(
AccountID const& id,
typename SLEBase<ViewT>::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: SLEBase<ViewT>(keylet::account(id), view, j)
{
}
};
template <typename ViewT>
class DirectoryNode : public SLEBase<ViewT>
{
public:
explicit DirectoryNode(
AccountID const& id,
typename SLEBase<ViewT>::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: SLEBase<ViewT>(keylet::ownerDir(id), view, j)
{
}
};
template <typename ViewT>
class Amendments : public SLEBase<ViewT>
{
public:
explicit Amendments(
typename SLEBase<ViewT>::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: SLEBase<ViewT>(keylet::amendments(), view, j)
{
}
};
template <typename ViewT>
class LedgerHashes : public SLEBase<ViewT>
{
public:
explicit LedgerHashes(
typename SLEBase<ViewT>::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: SLEBase<ViewT>(keylet::skip(), view, j)
{
}
};
template <typename ViewT>
class Bridge : public SLEBase<ViewT>
{
public:
explicit Bridge(
STXChainBridge const& bridge,
STXChainBridge::ChainType chainType,
typename SLEBase<ViewT>::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: SLEBase<ViewT>(keylet::bridge(bridge, chainType), view, j)
{
}
};
template <typename ViewT>
class Offer : public SLEBase<ViewT>
{
public:
explicit Offer(
AccountID const& id,
std::uint32_t seq,
typename SLEBase<ViewT>::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: SLEBase<ViewT>(keylet::offer(id, seq), view, j)
{
}
};
template <typename ViewT>
class DepositPreauth : public SLEBase<ViewT>
{
public:
explicit DepositPreauth(
AccountID const& owner,
AccountID const& preauthorized,
typename SLEBase<ViewT>::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: SLEBase<ViewT>(keylet::depositPreauth(owner, preauthorized), view, j)
{
}
};
template <typename ViewT>
class XChainOwnedClaimID : public SLEBase<ViewT>
{
public:
explicit XChainOwnedClaimID(
STXChainBridge const& bridge,
std::uint64_t seq,
typename SLEBase<ViewT>::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: SLEBase<ViewT>(keylet::xChainClaimID(bridge, seq), view, j)
{
}
};
template <typename ViewT>
class RippleState : public SLEBase<ViewT>
{
public:
explicit RippleState(
AccountID const& id0,
AccountID const& id1,
Currency const& currency,
typename SLEBase<ViewT>::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: SLEBase<ViewT>(keylet::trustLine(id0, id1, currency), view, j)
{
}
};
template <typename ViewT>
class FeeSettings : public SLEBase<ViewT>
{
public:
explicit FeeSettings(
typename SLEBase<ViewT>::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: SLEBase<ViewT>(keylet::feeSettings(), view, j)
{
}
};
template <typename ViewT>
class XChainOwnedCreateAccountClaimID : public SLEBase<ViewT>
{
public:
explicit XChainOwnedCreateAccountClaimID(
STXChainBridge const& bridge,
std::uint64_t seq,
typename SLEBase<ViewT>::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: SLEBase<ViewT>(keylet::xChainCreateAccountClaimID(bridge, seq), view, j)
{
}
};
template <typename ViewT>
class Escrow : public SLEBase<ViewT>
{
public:
explicit Escrow(
AccountID const& src,
std::uint32_t seq,
typename SLEBase<ViewT>::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: SLEBase<ViewT>(keylet::escrow(src, seq), view, j)
{
}
};
template <typename ViewT>
class PayChannel : public SLEBase<ViewT>
{
public:
explicit PayChannel(
AccountID const& src,
AccountID const& dst,
std::uint32_t seq,
typename SLEBase<ViewT>::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: SLEBase<ViewT>(keylet::payChannel(src, dst, seq), view, j)
{
}
};
template <typename ViewT>
class AMM : public SLEBase<ViewT>
{
public:
explicit AMM(
Asset const& issue1,
Asset const& issue2,
typename SLEBase<ViewT>::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: SLEBase<ViewT>(keylet::amm(issue1, issue2), view, j)
{
}
};
template <typename ViewT>
class MPTokenIssuance : public SLEBase<ViewT>
{
public:
explicit MPTokenIssuance(
std::uint32_t seq,
AccountID const& issuer,
typename SLEBase<ViewT>::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: SLEBase<ViewT>(keylet::mptokenIssuance(seq, issuer), view, j)
{
}
};
template <typename ViewT>
class MPToken : public SLEBase<ViewT>
{
public:
explicit MPToken(
MPTID const& issuanceID,
AccountID const& holder,
typename SLEBase<ViewT>::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: SLEBase<ViewT>(keylet::mptoken(issuanceID, holder), view, j)
{
}
};
template <typename ViewT>
class Oracle : public SLEBase<ViewT>
{
public:
explicit Oracle(
AccountID const& account,
std::uint32_t documentID,
typename SLEBase<ViewT>::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: SLEBase<ViewT>(keylet::oracle(account, documentID), view, j)
{
}
};
template <typename ViewT>
class Credential : public SLEBase<ViewT>
{
public:
explicit Credential(
AccountID const& subject,
AccountID const& issuer,
Slice const& credType,
typename SLEBase<ViewT>::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: SLEBase<ViewT>(keylet::credential(subject, issuer, credType), view, j)
{
}
};
template <typename ViewT>
class PermissionedDomain : public SLEBase<ViewT>
{
public:
explicit PermissionedDomain(
AccountID const& account,
std::uint32_t seq,
typename SLEBase<ViewT>::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: SLEBase<ViewT>(keylet::permissionedDomain(account, seq), view, j)
{
}
};
template <typename ViewT>
class Delegate : public SLEBase<ViewT>
{
public:
explicit Delegate(
AccountID const& account,
AccountID const& authorizedAccount,
typename SLEBase<ViewT>::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: SLEBase<ViewT>(keylet::delegate(account, authorizedAccount), view, j)
{
}
};
template <typename ViewT>
class Vault : public SLEBase<ViewT>
{
public:
explicit Vault(
AccountID const& owner,
std::uint32_t seq,
typename SLEBase<ViewT>::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: SLEBase<ViewT>(keylet::vault(owner, seq), view, j)
{
}
};
template <typename ViewT>
class LoanBroker : public SLEBase<ViewT>
{
public:
explicit LoanBroker(
AccountID const& owner,
std::uint32_t seq,
typename SLEBase<ViewT>::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: SLEBase<ViewT>(keylet::loanBroker(owner, seq), view, j)
{
}
};
template <typename ViewT>
class Loan : public SLEBase<ViewT>
{
public:
explicit Loan(
uint256 const& loanBrokerID,
std::uint32_t loanSeq,
typename SLEBase<ViewT>::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: SLEBase<ViewT>(keylet::loan(loanBrokerID, loanSeq), view, j)
{
}
};
template <typename ViewT>
class Sponsorship : public SLEBase<ViewT>
{
public:
explicit Sponsorship(
AccountID const& sponsor,
AccountID const& sponsee,
typename SLEBase<ViewT>::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: SLEBase<ViewT>(keylet::sponsorship(sponsor, sponsee), view, j)
{
}
};
} // namespace xrpl

View File

@@ -0,0 +1,26 @@
#pragma once
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/TxFlags.h>
#include <xrpl/protocol/TxFormats.h>
#include <cstdint>
#include <expected>
#include <optional>
#include <unordered_set>
namespace xrpl {
std::optional<AccountID>
getLedgerEntryOwner(ReadView const& view, SLE const& sle, AccountID const& account);
std::uint32_t
getLedgerEntryOwnerCount(SLE const& sle);
} // namespace xrpl

View File

@@ -0,0 +1,103 @@
#include <xrpl/ledger/helpers/SponsorHelpers.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/OracleHelpers.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/Rules.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STArray.h>
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/TER.h>
#include <cstdint>
#include <expected>
#include <optional>
namespace xrpl {
std::optional<AccountID>
getLedgerEntryOwner(ReadView const& view, SLE const& sle, AccountID const& account)
{
switch (sle.getType())
{
case ltCHECK:
case ltESCROW:
case ltPAYCHAN:
case ltMPTOKEN:
case ltDELEGATE:
case ltDEPOSIT_PREAUTH:
return sle.getAccountID(sfAccount);
case ltMPTOKEN_ISSUANCE:
return sle.getAccountID(sfIssuer);
case ltSIGNER_LIST: {
auto const signerList = view.read(keylet::signerList(account));
if (!signerList)
return std::nullopt;
if (signerList->key() == sle.key())
return account;
return std::nullopt;
}
case ltCREDENTIAL: {
if (sle.isFlag(lsfAccepted))
return sle.getAccountID(sfSubject);
return sle.getAccountID(sfIssuer);
}
case ltRIPPLE_STATE: {
if (sle.isFlag(lsfHighReserve))
{
auto const highAccount = sle.getFieldAmount(sfHighLimit).getIssuer();
if (highAccount == account)
return highAccount;
}
if (sle.isFlag(lsfLowReserve))
{
auto const lowAccount = sle.getFieldAmount(sfLowLimit).getIssuer();
if (lowAccount == account)
return lowAccount;
}
return std::nullopt;
}
default:
// LCOV_EXCL_START
UNREACHABLE("xrpl::getLedgerEntryOwner : object is not supported by sponsorship.");
return std::nullopt;
// LCOV_EXCL_STOP
};
}
std::uint32_t
getLedgerEntryOwnerCount(SLE const& sle)
{
switch (sle.getType())
{
case ltORACLE: {
return calculateOracleReserve(sle.getFieldArray(sfPriceDataSeries));
}
// Vaults require 2 owner counts (the vault and a pseudo-account)
case ltVAULT:
return 2;
case ltSIGNER_LIST: {
// Mirror SignerListSet's owner-count accounting so that create and
// delete agree. Modern lists (post-MultiSignReserve) carry the
// lsfOneOwnerCount flag and cost a single owner count. Legacy
// pre-MultiSignReserve lists cost 2 + signer_count owner counts
if (sle.isFlag(lsfOneOwnerCount))
return 1;
return 2 + static_cast<std::uint32_t>(sle.getFieldArray(sfSignerEntries).size());
}
case ltACCOUNT_ROOT:
UNREACHABLE("AccountRoots are not supported by object sponsorship.");
return 0;
default:
return 1;
}
}
} // namespace xrpl