mirror of
https://github.com/XRPLF/rippled.git
synced 2026-10-26 05:27:18 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a318bfa110 | ||
|
|
d5c0f8f0a7 | ||
|
|
30066af4ac | ||
|
|
d4cb62e78d | ||
|
|
cb2ab92927 | ||
|
|
16e7f81110 | ||
|
|
42c93e333e | ||
|
|
b8b9da92ee | ||
|
|
9d11ba0ca7 | ||
|
|
fb9b10fc03 | ||
|
|
628272a11e |
No files matched your search
@@ -7,6 +7,7 @@
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h> // IWYU pragma: keep
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
@@ -47,11 +48,9 @@ namespace xrpl {
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
canApplyToBrokerCover(
|
||||
ReadView const& view,
|
||||
SLE::const_ref sleBroker,
|
||||
LoanBrokerEntry<ReadView> const& sleBroker,
|
||||
Asset const& vaultAsset,
|
||||
STAmount const& amount,
|
||||
beast::Journal j,
|
||||
std::string_view logPrefix);
|
||||
|
||||
// Lending protocol has dependencies, so capture them here.
|
||||
@@ -217,7 +216,7 @@ adjustImpreciseNumber(
|
||||
}
|
||||
|
||||
inline int
|
||||
getAssetsTotalScale(SLE::const_ref vaultSle)
|
||||
getAssetsTotalScale(VaultEntry<ReadView> const& vaultSle)
|
||||
{
|
||||
if (!vaultSle)
|
||||
return Number::kMinExponent - 1; // LCOV_EXCL_LINE
|
||||
@@ -228,7 +227,10 @@ getAssetsTotalScale(SLE::const_ref vaultSle)
|
||||
// DebtTotal is a broker-level aggregate maintained at vault scale, so the
|
||||
// rounding must also use vault scale — never an individual loan's scale.
|
||||
inline Number
|
||||
minimumBrokerCover(Number const& debtTotal, TenthBips32 coverRateMinimum, SLE::const_ref vaultSle)
|
||||
minimumBrokerCover(
|
||||
Number const& debtTotal,
|
||||
TenthBips32 coverRateMinimum,
|
||||
VaultEntry<ReadView> const& vaultSle)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
vaultSle && vaultSle->getType() == ltVAULT, "xrpl::minimumBrokerCover : valid Vault sle");
|
||||
@@ -266,7 +268,7 @@ constructLoanState(
|
||||
// Constructs a valid LoanState object from a Loan object, which always has
|
||||
// rounded values
|
||||
LoanState
|
||||
constructRoundedLoanState(SLE::const_ref loan);
|
||||
constructRoundedLoanState(LoanEntry<ReadView> const& loan);
|
||||
|
||||
Number
|
||||
computeManagementFee(
|
||||
@@ -550,11 +552,9 @@ enum class LoanPaymentType { Regular = 0, Late, Full, Overpayment };
|
||||
std::expected<LoanPaymentParts, TER>
|
||||
loanMakePayment(
|
||||
Asset const& asset,
|
||||
ApplyView& view,
|
||||
SLE::ref loan,
|
||||
SLE::const_ref brokerSle,
|
||||
LoanEntry<ApplyView>& loan,
|
||||
LoanBrokerEntry<ReadView> const& brokerSle,
|
||||
STAmount const& amount,
|
||||
LoanPaymentType const paymentType,
|
||||
beast::Journal j);
|
||||
LoanPaymentType const paymentType);
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -1,5 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/helpers/SLEBase.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STArray.h> // IWYU pragma: keep
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
@@ -28,4 +32,35 @@ calculateOracleReserve(SLE::const_ref oracleSle)
|
||||
return calculateOracleReserve(oracleSle->getFieldArray(sfPriceDataSeries));
|
||||
}
|
||||
|
||||
template <typename ViewT>
|
||||
class OracleEntry : public SLEBase<ViewT>
|
||||
{
|
||||
public:
|
||||
// Inherit base constructors: adopt an existing SLE, or resolve one from a
|
||||
// Keylet against the view.
|
||||
using SLEBase<ViewT>::SLEBase;
|
||||
|
||||
explicit OracleEntry(
|
||||
AccountID const& account,
|
||||
std::uint32_t documentID,
|
||||
SLEBase<ViewT>::view_ref_type view,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: SLEBase<ViewT>(keylet::oracle(account, documentID), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] SField const&
|
||||
ownerField() const override
|
||||
{
|
||||
return sfOwner;
|
||||
}
|
||||
|
||||
// An Oracle with more than five price-data pairs occupies two reserve slots.
|
||||
[[nodiscard]] std::uint32_t
|
||||
reserveCount() const override
|
||||
{
|
||||
return calculateOracleReserve(this->sle()->getFieldArray(sfPriceDataSeries));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/protocol/Rules.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
@@ -23,7 +24,11 @@ namespace xrpl {
|
||||
* fails; tefINTERNAL if the source account SLE cannot be found.
|
||||
*/
|
||||
TER
|
||||
closeChannel(SLE::ref slep, ApplyView& view, uint256 const& key, beast::Journal j);
|
||||
closeChannel(
|
||||
PayChannelEntry<ApplyView>& slep,
|
||||
ApplyView& view,
|
||||
uint256 const& key,
|
||||
beast::Journal j);
|
||||
|
||||
/**
|
||||
* Add two uint32_t values with saturation at UINT32_MAX.
|
||||
|
||||
@@ -0,0 +1,599 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h> // increaseOwnerCount, accountReserve
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h> // describeOwnerDir
|
||||
#include <xrpl/ledger/helpers/SponsorHelpers.h> // addSponsorToLedgerEntry, checkReserve
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h> // keylet::account, keylet::ownerDir
|
||||
#include <xrpl/protocol/Keylet.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
|
||||
#include <concepts>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
// Concept to distinguish read-only vs writable view types
|
||||
template <typename V>
|
||||
concept WritableView = std::derived_from<V, ApplyView>;
|
||||
|
||||
/**
|
||||
* Describes one directory a ledger entry is linked into, for create()/destroy().
|
||||
*
|
||||
* @param owner the account whose owner directory this is (the directory is
|
||||
* keylet::ownerDir(owner)).
|
||||
* @param node the field on the entry holding this directory's page index
|
||||
* (sfOwnerNode, sfDestinationNode, sfSubjectNode, ...).
|
||||
* @param countsToward whether linking here consumes `owner`'s OwnerCount /
|
||||
* reserve (true for the owning account, false for
|
||||
* auxiliary links such as a destination's tracking
|
||||
* directory).
|
||||
*/
|
||||
struct OwnerDirLink
|
||||
{
|
||||
AccountID owner;
|
||||
SField const* node{} {};
|
||||
bool countsToward{} {};
|
||||
};
|
||||
|
||||
/**
|
||||
* 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_);
|
||||
}
|
||||
|
||||
/**
|
||||
* The field holding the account that owns this entry (sfAccount, sfOwner,
|
||||
* sfIssuer, ...).
|
||||
*
|
||||
* Single-directory entry types override this to name their owning-account
|
||||
* field; the default ownerDirs() then links a single owner directory
|
||||
* keyed on it. Types that live in multiple directories override
|
||||
* ownerDirs() directly instead. The generic base has no owner.
|
||||
*/
|
||||
[[nodiscard]] virtual SField const&
|
||||
ownerField() const
|
||||
{
|
||||
UNREACHABLE("xrpl::SLEBase::ownerField : type does not define an owner field");
|
||||
return sfAccount; // unreachable; present only to satisfy the return type
|
||||
}
|
||||
|
||||
/**
|
||||
* The directories this entry is linked into, and where each stores its
|
||||
* page index.
|
||||
*
|
||||
* Default: a single owner directory for ownerField()'s account, recorded
|
||||
* in sfOwnerNode, counting toward that account's reserve. Types linked
|
||||
* into more than one directory (e.g. Check/PayChannel/Escrow's
|
||||
* destination directory, Credential's subject directory) override this to
|
||||
* list them; only links with `countsToward == true` consume an
|
||||
* OwnerCount/reserve slot.
|
||||
*/
|
||||
[[nodiscard]] virtual std::vector<OwnerDirLink>
|
||||
ownerDirs() const
|
||||
{
|
||||
return {{sle_->getAccountID(ownerField()), &sfOwnerNode, /*countsToward=*/true}};
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of OwnerCount/reserve slots a counted link consumes (default 1).
|
||||
* Types whose footprint scales with their contents (e.g. Oracle)
|
||||
* override.
|
||||
*/
|
||||
[[nodiscard]] virtual std::uint32_t
|
||||
reserveCount() const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Link this entry into each listed owner directory (keylet::ownerDir),
|
||||
* and record the assigned page in the link's node field.
|
||||
*
|
||||
* This is the shared directory-linking primitive used by create() and by
|
||||
* the bespoke create() overrides of entries that live in several owner
|
||||
* directories. The OwnerDirLink::countsToward flag is ignored here —
|
||||
* reserve and OwnerCount accounting is the caller's responsibility.
|
||||
* Returns tecDIR_FULL if any directory is full.
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
linkOwnerDirs(std::vector<OwnerDirLink> const& dirs)
|
||||
requires kIsWritable
|
||||
{
|
||||
for (auto const& d : dirs)
|
||||
{
|
||||
auto const page =
|
||||
view_.dirInsert(keylet::ownerDir(d.owner), sle_->key(), describeOwnerDir(d.owner));
|
||||
if (!page)
|
||||
return tecDIR_FULL; // LCOV_EXCL_LINE
|
||||
sle_->setFieldU64(*d.node, *page);
|
||||
}
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlink this entry from each listed owner directory, using the page
|
||||
* stored in each link's node field. Inverse of linkOwnerDirs(). Returns
|
||||
* tefBAD_LEDGER if any removal fails.
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
unlinkOwnerDirs(std::vector<OwnerDirLink> const& dirs)
|
||||
requires kIsWritable
|
||||
{
|
||||
for (auto const& d : dirs)
|
||||
{
|
||||
{
|
||||
if (!view_.dirRemove(
|
||||
keylet::ownerDir(d.owner),
|
||||
sle_->getFieldU64(*d.node),
|
||||
sle_->key(),
|
||||
/*keepRoot=*/false))
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
}
|
||||
}
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Link a freshly-populated entry into its owner directories and insert
|
||||
* it.
|
||||
*
|
||||
* Handles the create-time boilerplate shared by owned ledger entries:
|
||||
* 1. reserve check against `ownerReserveBalance` (the owner's pre-fee
|
||||
* XRP balance — pass the transactor's preFeeBalance_ when the entry
|
||||
* is owned by the transaction submitter). Pass std::nullopt to skip
|
||||
* the check entirely, e.g. for entries an internal caller installs
|
||||
* on a pseudo-account (VaultCreate),
|
||||
* 2. link into each ownerDirs() directory, recording the page in its
|
||||
* node field,
|
||||
* 3. bump the OwnerCount of each counted owner by reserveCount(),
|
||||
* 4. insert the entry into the view.
|
||||
*
|
||||
* The caller must have already called newSLE() and populated the entry's
|
||||
* domain fields (in particular the account fields ownerDirs() reads).
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
create(std::optional<XRPAmount> ownerReserveBalance)
|
||||
requires kIsWritable
|
||||
{
|
||||
XRPL_ASSERT(canModify(), "xrpl::SLEBase::create : can modify");
|
||||
auto const dirs = ownerDirs();
|
||||
Adjustment const adj{.ownerCountDelta = static_cast<std::int32_t>(reserveCount())};
|
||||
|
||||
// The reserve sponsor covers only the transaction submitter's own
|
||||
// objects, so it is resolved from the counted owner (yielding no sponsor
|
||||
// when the owner is a pseudo-account or otherwise not the submitter).
|
||||
// Null unless the wrapper was built with an ApplyViewContext (a create
|
||||
// path) and the transaction carries a reserve sponsor for the owner.
|
||||
SLE::pointer sponsorSle;
|
||||
|
||||
for (auto const& d : dirs)
|
||||
{
|
||||
if (!d.countsToward)
|
||||
continue;
|
||||
auto const ownerSle = view_.peek(keylet::account(d.owner));
|
||||
if (!ownerSle)
|
||||
return tecNO_ENTRY; // LCOV_EXCL_LINE
|
||||
if (tx_ != nullptr != nullptr)
|
||||
{
|
||||
auto const sponsorExp =
|
||||
getEffectiveTxReserveSponsor(ApplyViewContext{view_, *tx_}, ownerSle);
|
||||
if (!sponsorExp)
|
||||
return sponsorExp.error(); // LCOV_EXCL_LINE
|
||||
sponsorSle = *sponsorExp;
|
||||
}
|
||||
if (!ownerReserveBalance)
|
||||
continue;
|
||||
// Route the reserve check through checkReserve() so a reserve
|
||||
// sponsor is honored; otherwise fall back to the account's own
|
||||
// reserve.
|
||||
if (tx_ != nullptr != nullptr)
|
||||
{
|
||||
if (auto const ret = checkReserve(
|
||||
ApplyViewContext{view_, *tx_},
|
||||
ownerSle,
|
||||
*ownerReserveBalance,
|
||||
sponsorSle,
|
||||
adj,
|
||||
j_);
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
}
|
||||
else if (*ownerReserveBalance < accountReserve(view_, ownerSle, j_, adj))
|
||||
{
|
||||
{
|
||||
{
|
||||
{
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (auto const ter = linkOwnerDirs(dirs); !isTesSuccess(ter))
|
||||
return ter; // LCOV_EXCL_LINE
|
||||
|
||||
for (auto const& d : dirs)
|
||||
{
|
||||
{
|
||||
if (d.countsToward)
|
||||
{
|
||||
{
|
||||
increaseOwnerCount(
|
||||
view_,
|
||||
view_.peek(keylet::account(d.owner)),
|
||||
sponsorSle,
|
||||
reserveCount(),
|
||||
j_);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Stamp the reserve sponsor (if any) onto the new entry so that a later
|
||||
// delete refunds the sponsor rather than the owner. A no-op when
|
||||
// sponsorSle is null.
|
||||
if (tx_ != nullptr != nullptr)
|
||||
addSponsorToLedgerEntry(sle_, sponsorSle);
|
||||
|
||||
view_.insert(sle_);
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlink an owned entry from its directories and erase it.
|
||||
*
|
||||
* Inverse of create(): removes the entry from each ownerDirs() directory
|
||||
* (using the stored node fields), decrements each counted owner's
|
||||
* OwnerCount by reserveCount(), and erases the entry.
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
destroy()
|
||||
requires kIsWritable
|
||||
{
|
||||
XRPL_ASSERT(canModify(), "xrpl::SLEBase::destroy : can modify");
|
||||
auto const dirs = ownerDirs();
|
||||
|
||||
if (auto const ter = unlinkOwnerDirs(dirs); !isTesSuccess(ter))
|
||||
return ter; // LCOV_EXCL_LINE
|
||||
|
||||
// decreaseOwnerCountForObject derives the reserve sponsor (if any) from
|
||||
// the entry's sfSponsor field, refunding the sponsor rather than the
|
||||
// owner when the object was sponsored.
|
||||
for (auto const& d : dirs)
|
||||
{
|
||||
{
|
||||
if (d.countsToward)
|
||||
{
|
||||
{
|
||||
if (auto ownerSle = view_.peek(keylet::account(d.owner)))
|
||||
decreaseOwnerCountForObject(view_, ownerSle, sle_, reserveCount(), j_);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
view_.erase(sle_);
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
[[nodiscard]] beast::Journal
|
||||
journal() const
|
||||
{
|
||||
return j_;
|
||||
}
|
||||
|
||||
// --- Constructors that adopt/resolve an SLE (public so the ReadOnlySLE /
|
||||
// WritableSLE aliases and the per-type wrappers can be built directly
|
||||
// from an already-fetched SLE or a keylet). ---
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for writable context carrying the applying transaction
|
||||
* (from existing SLE). Providing the ApplyViewContext lets create()
|
||||
* perform reserve-sponsorship-aware accounting.
|
||||
*/
|
||||
explicit SLEBase(
|
||||
std::shared_ptr<SLE> sle,
|
||||
ApplyViewContext ctx,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
requires kIsWritable
|
||||
: view_(ctx.view)
|
||||
, key_(sle ? Keylet(sle->getType(), sle->key()) : Keylet(ltANY, uint256{}))
|
||||
, sle_(std::move(sle))
|
||||
, j_(j)
|
||||
, tx_(&ctx.tx)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for writable context carrying the applying transaction
|
||||
* (peek from view by keylet).
|
||||
*/
|
||||
explicit SLEBase(
|
||||
Keylet const& key,
|
||||
ApplyViewContext ctx,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
requires kIsWritable
|
||||
: view_(ctx.view), key_(key), sle_(view_.peek(key)), j_(j), tx_(&ctx.tx)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
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_;
|
||||
|
||||
// The applying transaction, when the wrapper was constructed from an
|
||||
// ApplyViewContext. Only meaningful for writable wrappers on a create path;
|
||||
// null otherwise. Enables reserve-sponsorship-aware create().
|
||||
STTx const* tx_ = nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generic (any-entry-type) SLE handles.
|
||||
*
|
||||
* Use these when the concrete ledger entry type is not known at a given site;
|
||||
* otherwise prefer the per-type wrappers in SLEWrappers.h.
|
||||
*
|
||||
* SLE::const_pointer / SLE::const_ref -> ReadOnlySLE
|
||||
* SLE::pointer / SLE::ref -> WritableSLE
|
||||
*/
|
||||
using ReadOnlySLE = SLEBase<ReadView>;
|
||||
using WritableSLE = SLEBase<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,9 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
|
||||
#include <optional>
|
||||
|
||||
@@ -21,7 +21,10 @@ namespace xrpl {
|
||||
* @return The number of shares, or nullopt on error.
|
||||
*/
|
||||
[[nodiscard]] std::optional<STAmount>
|
||||
assetsToSharesDeposit(SLE::const_ref vault, SLE::const_ref issuance, STAmount const& assets);
|
||||
assetsToSharesDeposit(
|
||||
VaultEntry<ReadView> const& vault,
|
||||
MPTokenIssuanceEntry<ReadView> const& issuance,
|
||||
STAmount const& assets);
|
||||
|
||||
/**
|
||||
* From the perspective of a vault, return the number of assets to take from
|
||||
@@ -35,7 +38,10 @@ assetsToSharesDeposit(SLE::const_ref vault, SLE::const_ref issuance, STAmount co
|
||||
* @return The number of assets, or nullopt on error.
|
||||
*/
|
||||
[[nodiscard]] std::optional<STAmount>
|
||||
sharesToAssetsDeposit(SLE::const_ref vault, SLE::const_ref issuance, STAmount const& shares);
|
||||
sharesToAssetsDeposit(
|
||||
VaultEntry<ReadView> const& vault,
|
||||
MPTokenIssuanceEntry<ReadView> const& issuance,
|
||||
STAmount const& shares);
|
||||
|
||||
/**
|
||||
* Controls whether to truncate shares instead of rounding.
|
||||
@@ -68,8 +74,8 @@ enum class WaiveUnrealizedLoss : bool { No = false, Yes = true };
|
||||
*/
|
||||
[[nodiscard]] std::optional<STAmount>
|
||||
assetsToSharesWithdraw(
|
||||
SLE::const_ref vault,
|
||||
SLE::const_ref issuance,
|
||||
VaultEntry<ReadView> const& vault,
|
||||
MPTokenIssuanceEntry<ReadView> const& issuance,
|
||||
STAmount const& assets,
|
||||
TruncateShares truncate = TruncateShares::No,
|
||||
WaiveUnrealizedLoss waive = WaiveUnrealizedLoss::No);
|
||||
@@ -89,8 +95,8 @@ assetsToSharesWithdraw(
|
||||
*/
|
||||
[[nodiscard]] std::optional<STAmount>
|
||||
sharesToAssetsWithdraw(
|
||||
SLE::const_ref vault,
|
||||
SLE::const_ref issuance,
|
||||
VaultEntry<ReadView> const& vault,
|
||||
MPTokenIssuanceEntry<ReadView> const& issuance,
|
||||
STAmount const& shares,
|
||||
WaiveUnrealizedLoss waive = WaiveUnrealizedLoss::No);
|
||||
|
||||
@@ -105,6 +111,9 @@ sharesToAssetsWithdraw(
|
||||
* both the share MPTID and the outstanding-amount total.
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
isSoleShareholder(ReadView const& view, AccountID const& account, SLE::const_ref issuance);
|
||||
isSoleShareholder(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
MPTokenIssuanceEntry<ReadView> const& issuance);
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
@@ -169,7 +171,7 @@ public:
|
||||
static std::pair<TER, bool>
|
||||
deleteAMMAccountIfEmpty(
|
||||
Sandbox& sb,
|
||||
SLE::pointer const ammSle,
|
||||
AMMEntry<ApplyView>& ammSle,
|
||||
STAmount const& lpTokenBalance,
|
||||
Asset const& asset1,
|
||||
Asset const& asset2,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
@@ -41,34 +42,28 @@ public:
|
||||
*/
|
||||
static TER
|
||||
defaultLoan(
|
||||
ApplyView& view,
|
||||
SLE::ref loanSle,
|
||||
SLE::ref brokerSle,
|
||||
SLE::ref vaultSle,
|
||||
Asset const& vaultAsset,
|
||||
beast::Journal j);
|
||||
LoanEntry<ApplyView>& loanSle,
|
||||
LoanBrokerEntry<ApplyView>& brokerSle,
|
||||
VaultEntry<ApplyView>& vaultSle,
|
||||
Asset const& vaultAsset);
|
||||
|
||||
/**
|
||||
* Helper function that might be needed by other transactors
|
||||
*/
|
||||
static TER
|
||||
impairLoan(
|
||||
ApplyView& view,
|
||||
SLE::ref loanSle,
|
||||
SLE::ref vaultSle,
|
||||
Asset const& vaultAsset,
|
||||
beast::Journal j);
|
||||
LoanEntry<ApplyView>& loanSle,
|
||||
VaultEntry<ApplyView>& vaultSle,
|
||||
Asset const& vaultAsset);
|
||||
|
||||
/**
|
||||
* Helper function that might be needed by other transactors
|
||||
*/
|
||||
[[nodiscard]] static TER
|
||||
unimpairLoan(
|
||||
ApplyView& view,
|
||||
SLE::ref loanSle,
|
||||
SLE::ref vaultSle,
|
||||
Asset const& vaultAsset,
|
||||
beast::Journal j);
|
||||
LoanEntry<ApplyView>& loanSle,
|
||||
VaultEntry<ApplyView>& vaultSle,
|
||||
Asset const& vaultAsset);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
@@ -21,10 +23,10 @@ private:
|
||||
pay(AccountID const& from, AccountID const& to, STAmount const& amount);
|
||||
|
||||
TER
|
||||
acceptOffer(SLE::ref offer);
|
||||
acceptOffer(NFTokenOfferEntry<ApplyView> const& offer);
|
||||
|
||||
TER
|
||||
bridgeOffers(SLE::ref buy, SLE::ref sell);
|
||||
bridgeOffers(NFTokenOfferEntry<ApplyView> const& buy, NFTokenOfferEntry<ApplyView> const& sell);
|
||||
|
||||
TER
|
||||
transferNFToken(AccountID const& buyer, AccountID const& seller, uint256 const& nfTokenID);
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
@@ -48,8 +50,8 @@ public:
|
||||
private:
|
||||
std::expected<std::pair<STAmount, STAmount>, TER>
|
||||
assetsToClawback(
|
||||
SLE::ref vault,
|
||||
SLE::const_ref sleShareIssuance,
|
||||
VaultEntry<ApplyView>& vault,
|
||||
MPTokenIssuanceEntry<ReadView> const& sleShareIssuance,
|
||||
AccountID const& holder,
|
||||
STAmount const& clawbackAmount);
|
||||
};
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
#include <xrpl/ledger/Sandbox.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEBase.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/AMMCore.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
@@ -555,7 +557,7 @@ ammLPHolds(
|
||||
auto const currency = ammLPTCurrency(asset1, asset2);
|
||||
STAmount amount;
|
||||
|
||||
auto const sle = view.read(keylet::trustLine(lpAccount, ammAccount, currency));
|
||||
RippleStateEntry<ReadView> const sle{keylet::trustLine(lpAccount, ammAccount, currency), view};
|
||||
if (!sle)
|
||||
{
|
||||
amount.clear(Issue{currency, ammAccount});
|
||||
@@ -632,7 +634,7 @@ ammAccountHolds(ReadView const& view, AccountID const& ammAccountID, Asset const
|
||||
// Get the actual AMM balance without factoring in the balance hook
|
||||
return asset.visit(
|
||||
[&](MPTIssue const& issue) {
|
||||
if (auto const sle = view.read(keylet::mptoken(issue, ammAccountID));
|
||||
if (MPTokenEntry<ReadView> const sle{keylet::mptoken(issue, ammAccountID), view};
|
||||
sle && !isFrozen(view, ammAccountID, issue))
|
||||
return STAmount{issue, (*sle)[sfMPTAmount]};
|
||||
return STAmount{asset};
|
||||
@@ -640,12 +642,12 @@ ammAccountHolds(ReadView const& view, AccountID const& ammAccountID, Asset const
|
||||
[&](Issue const& issue) {
|
||||
if (isXRP(issue))
|
||||
{
|
||||
if (auto const sle = view.read(keylet::account(ammAccountID)))
|
||||
if (AccountRootEntry<ReadView> const sle{keylet::account(ammAccountID), view})
|
||||
return (*sle)[sfBalance];
|
||||
}
|
||||
else if (
|
||||
auto const sle =
|
||||
view.read(keylet::trustLine(ammAccountID, issue.account, issue.currency));
|
||||
RippleStateEntry<ReadView> const sle{
|
||||
keylet::trustLine(ammAccountID, issue.account, issue.currency), view};
|
||||
sle && !isFrozen(view, ammAccountID, issue.currency, issue.account))
|
||||
{
|
||||
STAmount amount = (*sle)[sfBalance];
|
||||
@@ -746,7 +748,7 @@ deleteAMMMPTokens(Sandbox& sb, AccountID const& ammAccountID, beast::Journal j)
|
||||
TER
|
||||
deleteAMMAccount(Sandbox& sb, Asset const& asset, Asset const& asset2, beast::Journal j)
|
||||
{
|
||||
auto ammSle = sb.peek(keylet::amm(asset, asset2));
|
||||
AMMEntry<ApplyView> ammSle{keylet::amm(asset, asset2), sb};
|
||||
if (!ammSle)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
@@ -756,7 +758,7 @@ deleteAMMAccount(Sandbox& sb, Asset const& asset, Asset const& asset2, beast::Jo
|
||||
}
|
||||
|
||||
auto const ammAccountID = (*ammSle)[sfAccount];
|
||||
auto sleAMMRoot = sb.peek(keylet::account(ammAccountID));
|
||||
AccountRootEntry<ApplyView> sleAMMRoot{keylet::account(ammAccountID), sb};
|
||||
if (!sleAMMRoot)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
@@ -776,25 +778,12 @@ deleteAMMAccount(Sandbox& sb, Asset const& asset, Asset const& asset2, beast::Jo
|
||||
if (auto const ter = deleteAMMMPTokens(sb, ammAccountID, j); !isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
auto const ownerDirKeylet = keylet::ownerDir(ammAccountID);
|
||||
if (!sb.dirRemove(ownerDirKeylet, (*ammSle)[sfOwnerNode], ammSle->key(), false))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.error()) << "deleteAMMAccount: failed to remove dir link";
|
||||
return tecINTERNAL;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
if (sb.exists(ownerDirKeylet) && !sb.emptyDirDelete(ownerDirKeylet))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.error()) << "deleteAMMAccount: cannot delete root dir node of "
|
||||
<< toBase58(ammAccountID);
|
||||
return tecINTERNAL;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
// Unlink the AMM from its pseudo-account's directory (collapsing the empty
|
||||
// root) and erase it. See AMMEntry::destroy().
|
||||
if (auto const ter = ammSle.destroy(); !isTesSuccess(ter))
|
||||
return ter; // LCOV_EXCL_LINE
|
||||
|
||||
sb.erase(ammSle);
|
||||
sb.erase(sleAMMRoot);
|
||||
sleAMMRoot.erase();
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
@@ -885,12 +874,12 @@ isOnlyLiquidityProvider(ReadView const& view, Issue const& ammIssue, AccountID c
|
||||
// Iterate over AMM owner directory objects.
|
||||
while (limit-- >= 1)
|
||||
{
|
||||
auto const ownerDir = view.read(currentIndex);
|
||||
DirectoryNodeEntry<ReadView> const ownerDir{currentIndex, view};
|
||||
if (!ownerDir)
|
||||
return std::unexpected<TER>(tecINTERNAL); // LCOV_EXCL_LINE
|
||||
for (auto const& key : ownerDir->getFieldV256(sfIndexes))
|
||||
{
|
||||
auto const sle = view.read(keylet::child(key));
|
||||
ReadOnlySLE const sle{keylet::child(key), view};
|
||||
if (!sle)
|
||||
return std::unexpected<TER>(tecINTERNAL); // LCOV_EXCL_LINE
|
||||
auto const entryType = sle->getFieldU16(sfLedgerEntryType);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/OwnerCounts.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/ledger/helpers/SponsorHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
@@ -39,7 +40,7 @@ isGlobalFrozen(ReadView const& view, AccountID const& issuer)
|
||||
{
|
||||
if (isXRP(issuer))
|
||||
return false;
|
||||
if (auto const sle = view.read(keylet::account(issuer)))
|
||||
if (AccountRootEntry<ReadView> const sle{keylet::account(issuer), view})
|
||||
return sle->isFlag(lsfGlobalFreeze);
|
||||
return false;
|
||||
}
|
||||
@@ -269,17 +270,17 @@ ownerCount(SLE::const_ref sle, beast::Journal j, std::int32_t ownerCountAdj)
|
||||
XRPAmount
|
||||
xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, beast::Journal j)
|
||||
{
|
||||
auto const sle = view.read(keylet::account(id));
|
||||
if (sle == nullptr)
|
||||
AccountRootEntry<ReadView> const sle{keylet::account(id), view};
|
||||
if (!sle)
|
||||
return beast::kZero;
|
||||
|
||||
// Return balance minus reserve
|
||||
std::uint32_t const currentOwnerCount =
|
||||
confineOwnerCount(view.ownerCountHook(id, OwnerCounts(sle)).count(), ownerCountAdj);
|
||||
std::uint32_t const currentAccountCount = accountCountImpl(sle, 0, j);
|
||||
confineOwnerCount(view.ownerCountHook(id, OwnerCounts(sle.sle())).count(), ownerCountAdj);
|
||||
std::uint32_t const currentAccountCount = accountCountImpl(sle.sle(), 0, j);
|
||||
|
||||
// Pseudo-accounts have no reserve requirement
|
||||
auto const reserve = isPseudoAccount(sle)
|
||||
auto const reserve = isPseudoAccount(sle.sle())
|
||||
? XRPAmount{0}
|
||||
: view.fees().accountReserve(currentOwnerCount, currentAccountCount);
|
||||
|
||||
@@ -301,7 +302,7 @@ xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj,
|
||||
Rate
|
||||
transferRate(ReadView const& view, AccountID const& issuer)
|
||||
{
|
||||
auto const sle = view.read(keylet::account(issuer));
|
||||
AccountRootEntry<ReadView> const sle{keylet::account(issuer), view};
|
||||
|
||||
if (sle && sle->isFieldPresent(sfTransferRate))
|
||||
return Rate{sle->getFieldU32(sfTransferRate)};
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
@@ -53,13 +53,13 @@ removeExpired(ApplyView& view, STVector256 const& arr, beast::Journal const j)
|
||||
{
|
||||
// Credentials already checked in preclaim. Look only for expired here.
|
||||
auto const k = keylet::credential(h);
|
||||
auto const sleCred = view.peek(k);
|
||||
CredentialEntry<ApplyView> sleCred{k, view};
|
||||
|
||||
if (sleCred && checkExpired(*sleCred, closeTime))
|
||||
{
|
||||
JLOG(j.trace()) << "Credentials are expired. Cred: " << sleCred->getText();
|
||||
// delete expired credentials even if the transaction failed
|
||||
auto const err = deleteSLE(view, sleCred, j);
|
||||
auto const err = deleteSLE(view, sleCred.mutableSle(), j);
|
||||
if (view.rules().enabled(fixCleanup3_1_3) && !isTesSuccess(err))
|
||||
return std::unexpected(err);
|
||||
foundExpired = true;
|
||||
@@ -75,52 +75,26 @@ deleteSLE(ApplyView& view, SLE::ref sleCredential, beast::Journal j)
|
||||
if (!sleCredential)
|
||||
return tecNO_ENTRY;
|
||||
|
||||
auto delSLE = [&view, &sleCredential, j](
|
||||
AccountID const& account, SField const& node, bool isOwner) -> TER {
|
||||
auto const sleAccount = view.peek(keylet::account(account));
|
||||
if (!sleAccount)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.fatal()) << "Internal error: can't retrieve Owner account.";
|
||||
return tecINTERNAL;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
// Remove object from owner directory
|
||||
std::uint64_t const page = sleCredential->getFieldU64(node);
|
||||
if (!view.dirRemove(keylet::ownerDir(account), page, sleCredential->key(), false))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.fatal()) << "Unable to delete Credential from owner.";
|
||||
return tefBAD_LEDGER;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
if (isOwner)
|
||||
decreaseOwnerCountForObject(view, sleAccount, sleCredential, 1, j);
|
||||
|
||||
return tesSUCCESS;
|
||||
};
|
||||
|
||||
// Historically deleteSLE fetched both the issuer's and (for a third-party
|
||||
// credential) the subject's account and failed if either was missing, even
|
||||
// though only one of them is counted against a reserve. Preserve that
|
||||
// stricter contract: a corrupted view missing one of these accounts must
|
||||
// report tecINTERNAL rather than silently unlinking.
|
||||
auto const issuer = sleCredential->getAccountID(sfIssuer);
|
||||
auto const subject = sleCredential->getAccountID(sfSubject);
|
||||
bool const accepted = sleCredential->isFlag(lsfAccepted);
|
||||
|
||||
auto err = delSLE(issuer, sfIssuerNode, !accepted || (subject == issuer));
|
||||
if (!isTesSuccess(err))
|
||||
return err;
|
||||
|
||||
if (subject != issuer)
|
||||
if (!view.exists(keylet::account(issuer)) ||
|
||||
(subject != issuer && !view.exists(keylet::account(subject))))
|
||||
{
|
||||
err = delSLE(subject, sfSubjectNode, accepted);
|
||||
if (!isTesSuccess(err))
|
||||
return err;
|
||||
JLOG(j.fatal()) << "Internal error: can't retrieve Owner account.";
|
||||
return tecINTERNAL;
|
||||
}
|
||||
|
||||
// Remove object from ledger
|
||||
view.erase(sleCredential);
|
||||
|
||||
return tesSUCCESS;
|
||||
// Unlink the credential from the issuer's and subject's directories,
|
||||
// decrementing whichever account currently owns it (the issuer until the
|
||||
// subject accepts, the subject afterwards), and erase it. See
|
||||
// CredentialEntry::ownerDirs().
|
||||
CredentialEntry<ApplyView> cred{sleCredential, view, j};
|
||||
return cred.destroy();
|
||||
}
|
||||
|
||||
NotTEC
|
||||
@@ -160,7 +134,7 @@ valid(STTx const& tx, ReadView const& view, AccountID const& src, beast::Journal
|
||||
auto const& credIDs(tx.getFieldV256(sfCredentialIDs));
|
||||
for (auto const& h : credIDs)
|
||||
{
|
||||
auto const sleCred = view.read(keylet::credential(h));
|
||||
CredentialEntry<ReadView> const sleCred{keylet::credential(h), view};
|
||||
if (!sleCred)
|
||||
{
|
||||
JLOG(j.trace()) << "Credential doesn't exist. Cred: " << h;
|
||||
@@ -189,7 +163,7 @@ TER
|
||||
validDomain(ReadView const& view, uint256 domainID, AccountID const& subject)
|
||||
{
|
||||
// Note, permissioned domain objects can be deleted at any time
|
||||
auto const slePD = view.read(keylet::permissionedDomain(domainID));
|
||||
PermissionedDomainEntry<ReadView> const slePD{keylet::permissionedDomain(domainID), view};
|
||||
if (!slePD)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
@@ -200,7 +174,7 @@ validDomain(ReadView const& view, uint256 domainID, AccountID const& subject)
|
||||
auto const issuer = h.getAccountID(sfIssuer);
|
||||
auto const type = h.getFieldVL(sfCredentialType);
|
||||
auto const keyletCredential = keylet::credential(subject, issuer, makeSlice(type));
|
||||
auto const sleCredential = view.read(keyletCredential);
|
||||
CredentialEntry<ReadView> const sleCredential{keyletCredential, view};
|
||||
|
||||
// We cannot delete expired credentials, that would require ApplyView&
|
||||
// However we can check if credentials are expired. Expected transaction
|
||||
@@ -234,14 +208,14 @@ authorizedDepositPreauth(ReadView const& view, STVector256 const& credIDs, Accou
|
||||
lifeExtender.reserve(credIDs.size());
|
||||
for (auto const& h : credIDs)
|
||||
{
|
||||
auto sleCred = view.read(keylet::credential(h));
|
||||
CredentialEntry<ReadView> const sleCred{keylet::credential(h), view};
|
||||
if (!sleCred) // already checked in preclaim
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto [it, ins] = sorted.emplace((*sleCred)[sfIssuer], (*sleCred)[sfCredentialType]);
|
||||
if (!ins)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
lifeExtender.push_back(std::move(sleCred));
|
||||
lifeExtender.push_back(sleCred.sle());
|
||||
}
|
||||
|
||||
if (!view.exists(keylet::depositPreauth(dst, sorted)))
|
||||
@@ -312,7 +286,7 @@ checkArray(STArray const& credentials, unsigned maxSize, beast::Journal j)
|
||||
TER
|
||||
verifyValidDomain(ApplyView& view, AccountID const& account, uint256 domainID, beast::Journal j)
|
||||
{
|
||||
auto const slePD = view.read(keylet::permissionedDomain(domainID));
|
||||
PermissionedDomainEntry<ReadView> const slePD{keylet::permissionedDomain(domainID), view};
|
||||
if (!slePD)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
@@ -334,7 +308,7 @@ verifyValidDomain(ApplyView& view, AccountID const& account, uint256 domainID, b
|
||||
|
||||
for (auto const& h : credentials)
|
||||
{
|
||||
auto sleCredential = view.read(keylet::credential(h));
|
||||
CredentialEntry<ReadView> const sleCredential{keylet::credential(h), view};
|
||||
if (!sleCredential)
|
||||
continue; // expired, i.e. deleted in credentials::removeExpired
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
@@ -32,13 +33,14 @@ namespace xrpl {
|
||||
|
||||
[[nodiscard]] TER
|
||||
canApplyToBrokerCover(
|
||||
ReadView const& view,
|
||||
SLE::const_ref sleBroker,
|
||||
LoanBrokerEntry<ReadView> const& sleBroker,
|
||||
Asset const& vaultAsset,
|
||||
STAmount const& amount,
|
||||
beast::Journal j,
|
||||
std::string_view logPrefix)
|
||||
{
|
||||
ReadView const& view = sleBroker.readView();
|
||||
beast::Journal const j = sleBroker.journal();
|
||||
|
||||
XRPL_ASSERT(
|
||||
sleBroker && sleBroker->getType() == ltLOAN_BROKER,
|
||||
"xrpl::canApplyToBrokerCover : valid LoanBroker sle");
|
||||
@@ -1632,7 +1634,7 @@ constructLoanState(
|
||||
}
|
||||
|
||||
LoanState
|
||||
constructRoundedLoanState(SLE::const_ref loan)
|
||||
constructRoundedLoanState(LoanEntry<ReadView> const& loan)
|
||||
{
|
||||
return constructLoanState(
|
||||
loan->at(sfTotalValueOutstanding),
|
||||
@@ -1783,15 +1785,16 @@ computeLoanProperties(
|
||||
std::expected<LoanPaymentParts, TER>
|
||||
loanMakePayment(
|
||||
Asset const& asset,
|
||||
ApplyView& view,
|
||||
SLE::ref loan,
|
||||
SLE::const_ref brokerSle,
|
||||
LoanEntry<ApplyView>& loan,
|
||||
LoanBrokerEntry<ReadView> const& brokerSle,
|
||||
STAmount const& amount,
|
||||
LoanPaymentType const paymentType,
|
||||
beast::Journal j)
|
||||
LoanPaymentType const paymentType)
|
||||
{
|
||||
using namespace Lending;
|
||||
|
||||
ApplyView& view = loan.applyView();
|
||||
beast::Journal const j = loan.journal();
|
||||
|
||||
auto principalOutstandingProxy = loan->at(sfPrincipalOutstanding);
|
||||
auto paymentRemainingProxy = loan->at(sfPaymentRemaining);
|
||||
|
||||
@@ -1836,7 +1839,7 @@ loanMakePayment(
|
||||
|
||||
XRPL_ASSERT(*totalValueOutstandingProxy > 0, "xrpl::loanMakePayment : valid total value");
|
||||
|
||||
view.update(loan);
|
||||
loan.update();
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// A late payment not flagged as late overrides all other options.
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/CredentialHelpers.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEBase.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/ledger/helpers/SponsorHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
@@ -41,7 +43,8 @@ namespace xrpl {
|
||||
bool
|
||||
isGlobalFrozen(ReadView const& view, MPTIssue const& mptIssue)
|
||||
{
|
||||
if (auto const sle = view.read(keylet::mptokenIssuance(mptIssue.getMptID())))
|
||||
if (MPTokenIssuanceEntry<ReadView> const sle{
|
||||
keylet::mptokenIssuance(mptIssue.getMptID()), view})
|
||||
return sle->isFlag(lsfMPTLocked);
|
||||
return false;
|
||||
}
|
||||
@@ -49,7 +52,7 @@ isGlobalFrozen(ReadView const& view, MPTIssue const& mptIssue)
|
||||
bool
|
||||
isIndividualFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue)
|
||||
{
|
||||
if (auto const sle = view.read(keylet::mptoken(mptIssue.getMptID(), account)))
|
||||
if (MPTokenEntry<ReadView> const sle{keylet::mptoken(mptIssue.getMptID(), account), view})
|
||||
return sle->isFlag(lsfMPTLocked);
|
||||
return false;
|
||||
}
|
||||
@@ -92,7 +95,7 @@ transferRate(ReadView const& view, MPTID const& issuanceID)
|
||||
// fee is 0-50,000 (0-50%), rate is 1,000,000,000-2,000,000,000
|
||||
// For example, if transfer fee is 50% then 10,000 * 50,000 = 500,000
|
||||
// which represents 50% of 1,000,000,000
|
||||
if (auto const sle = view.read(keylet::mptokenIssuance(issuanceID));
|
||||
if (MPTokenIssuanceEntry<ReadView> const sle{keylet::mptokenIssuance(issuanceID), view};
|
||||
sle && sle->isFieldPresent(sfTransferFee))
|
||||
{
|
||||
auto const fee = sle->getFieldU16(sfTransferFee);
|
||||
@@ -107,7 +110,7 @@ transferRate(ReadView const& view, MPTID const& issuanceID)
|
||||
canAddHolding(ReadView const& view, MPTIssue const& mptIssue)
|
||||
{
|
||||
auto mptID = mptIssue.getMptID();
|
||||
auto issuance = view.read(keylet::mptokenIssuance(mptID));
|
||||
MPTokenIssuanceEntry<ReadView> const issuance{keylet::mptokenIssuance(mptID), view};
|
||||
if (!issuance)
|
||||
{
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
@@ -129,7 +132,7 @@ addEmptyHolding(
|
||||
beast::Journal journal)
|
||||
{
|
||||
auto const& mptID = mptIssue.getMptID();
|
||||
auto const mpt = ctx.view.peek(keylet::mptokenIssuance(mptID));
|
||||
MPTokenIssuanceEntry<ApplyView> const mpt{keylet::mptokenIssuance(mptID), ctx.view};
|
||||
if (!mpt)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
if (mpt->isFlag(lsfMPTLocked))
|
||||
@@ -152,7 +155,7 @@ authorizeMPToken(
|
||||
std::uint32_t flags,
|
||||
std::optional<AccountID> holderID)
|
||||
{
|
||||
auto const sleAcct = ctx.view.peek(keylet::account(account));
|
||||
AccountRootEntry<ApplyView> const sleAcct{keylet::account(account), ctx.view};
|
||||
if (!sleAcct)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -167,50 +170,28 @@ authorizeMPToken(
|
||||
if ((flags & tfMPTUnauthorize) != 0u)
|
||||
{
|
||||
auto const mptokenKey = keylet::mptoken(mptIssuanceID, account);
|
||||
auto const sleMpt = ctx.view.peek(mptokenKey);
|
||||
MPTokenEntry<ApplyView> sleMpt{mptokenKey, ctx.view, journal};
|
||||
if (!sleMpt || (*sleMpt)[sfMPTAmount] != 0 ||
|
||||
(ctx.view.rules().enabled(fixCleanup3_1_3) &&
|
||||
(*sleMpt)[~sfLockedAmount].valueOr(0) != 0))
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
if (!ctx.view.dirRemove(
|
||||
keylet::ownerDir(account), (*sleMpt)[sfOwnerNode], sleMpt->key(), false))
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
decreaseOwnerCountForObject(ctx.view, sleAcct, sleMpt, 1, journal);
|
||||
|
||||
ctx.view.erase(sleMpt);
|
||||
return tesSUCCESS;
|
||||
// Unlink from the holder's owner directory, decrement its
|
||||
// OwnerCount (refunding a reserve sponsor if present), and erase.
|
||||
// See MPTokenEntry.
|
||||
return sleMpt.destroy();
|
||||
}
|
||||
|
||||
// A potential holder wants to authorize/hold a mpt, the ledger must:
|
||||
// - add the new mptokenKey to the owner directory
|
||||
// - create the MPToken object for the holder
|
||||
|
||||
// A reserve sponsor only covers tx.Account's own objects.
|
||||
auto const sponsorExp = getEffectiveTxReserveSponsor(ctx, sleAcct);
|
||||
if (!sponsorExp)
|
||||
return sponsorExp.error(); // LCOV_EXCL_LINE
|
||||
auto const sponsorSle = *sponsorExp;
|
||||
|
||||
// The reserve that is required to create the MPToken. Note
|
||||
// that although the reserve increases with every item
|
||||
// an account owns, in the case of MPTokens we only
|
||||
// *enforce* a reserve if the user owns more than two
|
||||
// items. This is similar to the reserve requirements of trust lines.
|
||||
// The "free-tier" shortcut (ownerCount < 2) does not apply once a sponsor is on
|
||||
// the tx — the sponsor must always cover the reserve (via balance or prefunded
|
||||
// budget), so this check always runs for sponsored transactions.
|
||||
if (sponsorSle || ownerCount(sleAcct, journal) >= 2)
|
||||
{
|
||||
if (auto const ret = checkReserve(
|
||||
ctx, sleAcct, priorBalance, sponsorSle, {.ownerCountDelta = 1}, journal);
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
}
|
||||
// A potential holder wants to authorize/hold a mpt. The trust-line-style
|
||||
// free-tier reserve rule and reserve-sponsorship accounting are applied
|
||||
// by MPTokenEntry::create() below.
|
||||
|
||||
// Defensive check before we attempt to create MPToken for the issuer
|
||||
auto const mpt = ctx.view.read(keylet::mptokenIssuance(mptIssuanceID));
|
||||
MPTokenIssuanceEntry<ReadView> const mpt{keylet::mptokenIssuance(mptIssuanceID), ctx.view};
|
||||
if (!mpt || mpt->getAccountID(sfIssuer) == account)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
@@ -221,23 +202,22 @@ authorizeMPToken(
|
||||
}
|
||||
|
||||
auto const mptokenKey = keylet::mptoken(mptIssuanceID, account);
|
||||
auto mptoken = std::make_shared<SLE>(mptokenKey);
|
||||
if (auto ter = dirLink(ctx.view, account, mptoken))
|
||||
return ter; // LCOV_EXCL_LINE
|
||||
|
||||
// Build with the ApplyViewContext so create() can apply reserve
|
||||
// sponsorship (reserve check, OwnerCount, and sponsor stamp).
|
||||
MPTokenEntry<ApplyView> mptoken{mptokenKey, ctx, journal};
|
||||
mptoken.newSLE();
|
||||
(*mptoken)[sfAccount] = account;
|
||||
(*mptoken)[sfMPTokenIssuanceID] = mptIssuanceID;
|
||||
(*mptoken)[sfFlags] = 0;
|
||||
ctx.view.insert(mptoken);
|
||||
|
||||
// Update owner count.
|
||||
increaseOwnerCount(ctx.view, sleAcct, sponsorSle, 1, journal);
|
||||
addSponsorToLedgerEntry(mptoken, sponsorSle);
|
||||
|
||||
return tesSUCCESS;
|
||||
// Trust-line-style free-tier reserve check + link into the holder's
|
||||
// owner directory + bump the holder's OwnerCount + stamp any reserve
|
||||
// sponsor + insert. See MPTokenEntry::create().
|
||||
return mptoken.create(priorBalance);
|
||||
}
|
||||
|
||||
auto const sleMptIssuance = ctx.view.read(keylet::mptokenIssuance(mptIssuanceID));
|
||||
MPTokenIssuanceEntry<ReadView> const sleMptIssuance{
|
||||
keylet::mptokenIssuance(mptIssuanceID), ctx.view};
|
||||
if (!sleMptIssuance)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -247,7 +227,7 @@ authorizeMPToken(
|
||||
if (account != (*sleMptIssuance)[sfIssuer])
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const sleMpt = ctx.view.peek(keylet::mptoken(mptIssuanceID, *holderID));
|
||||
MPTokenEntry<ApplyView> sleMpt{keylet::mptoken(mptIssuanceID, *holderID), ctx.view};
|
||||
if (!sleMpt)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -270,7 +250,7 @@ authorizeMPToken(
|
||||
if (flagsIn != flagsOut)
|
||||
sleMpt->setFieldU32(sfFlags, flagsOut);
|
||||
|
||||
ctx.view.update(sleMpt);
|
||||
sleMpt.update();
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -286,7 +266,7 @@ removeEmptyHolding(
|
||||
// a token does exist, it will get deleted. If not, return success.
|
||||
bool const accountIsIssuer = accountID == mptIssue.getIssuer();
|
||||
auto const& mptID = mptIssue.getMptID();
|
||||
auto const mptoken = ctx.view.peek(keylet::mptoken(mptID, accountID));
|
||||
MPTokenEntry<ApplyView> mptoken{keylet::mptoken(mptID, accountID), ctx.view};
|
||||
if (!mptoken)
|
||||
return accountIsIssuer ? (TER)tesSUCCESS : (TER)tecOBJECT_NOT_FOUND;
|
||||
// Unlike a trust line, if the account is the issuer, and the token has a
|
||||
@@ -337,7 +317,7 @@ requireAuth(
|
||||
};
|
||||
|
||||
auto const mptID = keylet::mptokenIssuance(mptIssue.getMptID());
|
||||
auto const sleIssuance = view.read(mptID);
|
||||
MPTokenIssuanceEntry<ReadView> const sleIssuance{mptID, view};
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
@@ -362,13 +342,14 @@ requireAuth(
|
||||
}
|
||||
|
||||
// requireAuth is recursive if the issuer is a vault pseudo-account
|
||||
auto const sleIssuer = view.read(keylet::account(mptIssuer));
|
||||
AccountRootEntry<ReadView> const sleIssuer{keylet::account(mptIssuer), view};
|
||||
if (!sleIssuer)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
if (sleIssuer->isFieldPresent(sfVaultID))
|
||||
{
|
||||
auto const sleVault = view.read(keylet::vault(sleIssuer->getFieldH256(sfVaultID)));
|
||||
VaultEntry<ReadView> const sleVault{
|
||||
keylet::vault(sleIssuer->getFieldH256(sfVaultID)), view};
|
||||
if (!sleVault)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -384,7 +365,7 @@ requireAuth(
|
||||
}
|
||||
|
||||
auto const mptokenID = keylet::mptoken(mptID.key, account);
|
||||
auto const sleToken = view.read(mptokenID);
|
||||
MPTokenEntry<ReadView> const sleToken{mptokenID, view};
|
||||
|
||||
// if account has no MPToken, fail
|
||||
if (!sleToken && (authType == AuthType::StrongAuth || authType == AuthType::Legacy))
|
||||
@@ -432,7 +413,8 @@ enforceMPTokenAuthorization(
|
||||
XRPAmount const& priorBalance, // for MPToken authorization
|
||||
beast::Journal j)
|
||||
{
|
||||
auto const sleIssuance = ctx.view.read(keylet::mptokenIssuance(mptIssuanceID));
|
||||
MPTokenIssuanceEntry<ReadView> const sleIssuance{
|
||||
keylet::mptokenIssuance(mptIssuanceID), ctx.view};
|
||||
if (!sleIssuance)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -444,7 +426,7 @@ enforceMPTokenAuthorization(
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const keylet = keylet::mptoken(mptIssuanceID, account);
|
||||
auto const sleToken = ctx.view.read(keylet); // NOTE: might be null
|
||||
MPTokenEntry<ReadView> const sleToken{keylet, ctx.view}; // NOTE: might be null
|
||||
auto const maybeDomainID = sleIssuance->at(~sfDomainID);
|
||||
bool expired = false;
|
||||
bool const authorizedByDomain = [&]() -> bool {
|
||||
@@ -460,7 +442,7 @@ enforceMPTokenAuthorization(
|
||||
return false;
|
||||
}();
|
||||
|
||||
if (!authorizedByDomain && sleToken == nullptr)
|
||||
if (!authorizedByDomain && !sleToken)
|
||||
{
|
||||
// Could not find MPToken and won't create one, could be either of:
|
||||
//
|
||||
@@ -483,14 +465,14 @@ enforceMPTokenAuthorization(
|
||||
// We found an MPToken, but sfDomainID is not set, so this is a classic
|
||||
// MPToken which requires authorization by the token issuer.
|
||||
XRPL_ASSERT(
|
||||
sleToken != nullptr && !maybeDomainID.has_value(),
|
||||
sleToken && !maybeDomainID.has_value(),
|
||||
"xrpl::enforceMPTokenAuthorization : found MPToken");
|
||||
if (sleToken->isFlag(lsfMPTAuthorized))
|
||||
return tesSUCCESS;
|
||||
|
||||
return tecNO_AUTH;
|
||||
}
|
||||
if (authorizedByDomain && sleToken != nullptr)
|
||||
if (authorizedByDomain && sleToken)
|
||||
{
|
||||
// Found an MPToken, authorized by the domain. Ignore authorization flag
|
||||
// lsfMPTAuthorized because it is meaningless. Return tesSUCCESS
|
||||
@@ -504,7 +486,7 @@ enforceMPTokenAuthorization(
|
||||
// Could not find MPToken but there should be one because we are
|
||||
// authorized by domain. Proceed to create it, then return tesSUCCESS
|
||||
XRPL_ASSERT(
|
||||
maybeDomainID.has_value() && sleToken == nullptr,
|
||||
maybeDomainID.has_value() && !sleToken,
|
||||
"xrpl::enforceMPTokenAuthorization : new MPToken for domain");
|
||||
if (auto const err = authorizeMPToken(
|
||||
ctx,
|
||||
@@ -557,7 +539,7 @@ canTransfer(
|
||||
std::uint8_t depth)
|
||||
{
|
||||
auto const mptID = keylet::mptokenIssuance(mptIssue.getMptID());
|
||||
auto const sleIssuance = view.read(mptID);
|
||||
MPTokenIssuanceEntry<ReadView> const sleIssuance{mptID, view};
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
@@ -587,8 +569,8 @@ canTransfer(
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
auto const sleHolding =
|
||||
view.read(keylet::unchecked(sleIssuance->getFieldH256(sfReferenceHolding)));
|
||||
ReadOnlySLE const sleHolding{
|
||||
keylet::unchecked(sleIssuance->getFieldH256(sfReferenceHolding)), view};
|
||||
if (!sleHolding)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -610,7 +592,8 @@ canTrade(ReadView const& view, Asset const& asset, std::uint8_t depth)
|
||||
return asset.visit(
|
||||
[&](Issue const&) -> TER { return tesSUCCESS; },
|
||||
[&](MPTIssue const& mptIssue) -> TER {
|
||||
auto const sleIssuance = view.read(keylet::mptokenIssuance(mptIssue.getMptID()));
|
||||
MPTokenIssuanceEntry<ReadView> const sleIssuance{
|
||||
keylet::mptokenIssuance(mptIssue.getMptID()), view};
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
if (!sleIssuance->isFlag(lsfMPTCanTrade))
|
||||
@@ -632,8 +615,8 @@ canTrade(ReadView const& view, Asset const& asset, std::uint8_t depth)
|
||||
return tecINTERNAL;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
auto const sleHolding =
|
||||
view.read(keylet::unchecked(sleIssuance->getFieldH256(sfReferenceHolding)));
|
||||
ReadOnlySLE const sleHolding{
|
||||
keylet::unchecked(sleIssuance->getFieldH256(sfReferenceHolding)), view};
|
||||
if (!sleHolding)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -665,7 +648,7 @@ lockEscrowMPT(ApplyView& view, AccountID const& sender, STAmount const& amount,
|
||||
{
|
||||
auto const mptIssue = amount.get<MPTIssue>();
|
||||
auto const mptID = keylet::mptokenIssuance(mptIssue.getMptID());
|
||||
auto sleIssuance = view.peek(mptID);
|
||||
MPTokenIssuanceEntry<ApplyView> sleIssuance{mptID, view};
|
||||
if (!sleIssuance)
|
||||
{ // LCOV_EXCL_START
|
||||
JLOG(j.error()) << "lockEscrowMPT: MPT issuance not found for " << mptIssue.getMptID();
|
||||
@@ -682,7 +665,7 @@ lockEscrowMPT(ApplyView& view, AccountID const& sender, STAmount const& amount,
|
||||
// 2. Increase the MPT Holder EscrowedAmount
|
||||
{
|
||||
auto const mptokenID = keylet::mptoken(mptID.key, sender);
|
||||
auto sle = view.peek(mptokenID);
|
||||
MPTokenEntry<ApplyView> sle{mptokenID, view};
|
||||
if (!sle)
|
||||
{ // LCOV_EXCL_START
|
||||
JLOG(j.error()) << "lockEscrowMPT: MPToken not found for " << sender;
|
||||
@@ -721,7 +704,7 @@ lockEscrowMPT(ApplyView& view, AccountID const& sender, STAmount const& amount,
|
||||
sle->setFieldU64(sfLockedAmount, pay);
|
||||
}
|
||||
|
||||
view.update(sle);
|
||||
sle.update();
|
||||
}
|
||||
|
||||
// 1. Increase the Issuance EscrowedAmount
|
||||
@@ -748,7 +731,7 @@ lockEscrowMPT(ApplyView& view, AccountID const& sender, STAmount const& amount,
|
||||
sleIssuance->setFieldU64(sfLockedAmount, pay);
|
||||
}
|
||||
|
||||
view.update(sleIssuance);
|
||||
sleIssuance.update();
|
||||
}
|
||||
return tesSUCCESS;
|
||||
}
|
||||
@@ -770,7 +753,7 @@ unlockEscrowMPT(
|
||||
auto const& issuer = netAmount.getIssuer();
|
||||
auto const& mptIssue = netAmount.get<MPTIssue>();
|
||||
auto const mptID = keylet::mptokenIssuance(mptIssue.getMptID());
|
||||
auto sleIssuance = view.peek(mptID);
|
||||
MPTokenIssuanceEntry<ApplyView> sleIssuance{mptID, view};
|
||||
if (!sleIssuance)
|
||||
{ // LCOV_EXCL_START
|
||||
JLOG(j.error()) << "unlockEscrowMPT: MPT issuance not found for " << mptIssue.getMptID();
|
||||
@@ -806,14 +789,14 @@ unlockEscrowMPT(
|
||||
{
|
||||
sleIssuance->setFieldU64(sfLockedAmount, newLocked);
|
||||
}
|
||||
view.update(sleIssuance);
|
||||
sleIssuance.update();
|
||||
}
|
||||
|
||||
if (issuer != receiver)
|
||||
{
|
||||
// Increase the MPT Holder MPTAmount
|
||||
auto const mptokenID = keylet::mptoken(mptID.key, receiver);
|
||||
auto sle = view.peek(mptokenID);
|
||||
MPTokenEntry<ApplyView> sle{mptokenID, view};
|
||||
if (!sle)
|
||||
{ // LCOV_EXCL_START
|
||||
JLOG(j.error()) << "unlockEscrowMPT: MPToken not found for " << receiver;
|
||||
@@ -832,7 +815,7 @@ unlockEscrowMPT(
|
||||
} // LCOV_EXCL_STOP
|
||||
|
||||
(*sle)[sfMPTAmount] += delta;
|
||||
view.update(sle);
|
||||
sle.update();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -849,7 +832,7 @@ unlockEscrowMPT(
|
||||
} // LCOV_EXCL_STOP
|
||||
|
||||
sleIssuance->setFieldU64(sfOutstandingAmount, outstanding - redeem);
|
||||
view.update(sleIssuance);
|
||||
sleIssuance.update();
|
||||
}
|
||||
|
||||
if (issuer == sender)
|
||||
@@ -860,7 +843,7 @@ unlockEscrowMPT(
|
||||
} // LCOV_EXCL_STOP
|
||||
// Decrease the MPT Holder EscrowedAmount
|
||||
auto const mptokenID = keylet::mptoken(mptID.key, sender);
|
||||
auto sle = view.peek(mptokenID);
|
||||
MPTokenEntry<ApplyView> sle{mptokenID, view};
|
||||
if (!sle)
|
||||
{ // LCOV_EXCL_START
|
||||
JLOG(j.error()) << "unlockEscrowMPT: MPToken not found for " << sender;
|
||||
@@ -893,7 +876,7 @@ unlockEscrowMPT(
|
||||
{
|
||||
sle->setFieldU64(sfLockedAmount, newLocked);
|
||||
}
|
||||
view.update(sle);
|
||||
sle.update();
|
||||
|
||||
// Note: The gross amount is the amount that was locked, the net
|
||||
// amount is the amount that is being unlocked. The difference is the fee
|
||||
@@ -912,7 +895,7 @@ unlockEscrowMPT(
|
||||
} // LCOV_EXCL_STOP
|
||||
|
||||
sleIssuance->setFieldU64(sfOutstandingAmount, outstanding - diff);
|
||||
view.update(sleIssuance);
|
||||
sleIssuance.update();
|
||||
}
|
||||
return tesSUCCESS;
|
||||
}
|
||||
@@ -933,15 +916,16 @@ createMPToken(
|
||||
if (!ownerNode)
|
||||
return tecDIR_FULL; // LCOV_EXCL_LINE
|
||||
|
||||
auto mptoken = std::make_shared<SLE>(mptokenKey);
|
||||
MPTokenEntry<ApplyView> mptoken{mptokenKey, view};
|
||||
mptoken.newSLE();
|
||||
(*mptoken)[sfAccount] = account;
|
||||
(*mptoken)[sfMPTokenIssuanceID] = mptIssuanceID;
|
||||
(*mptoken)[sfFlags] = flags;
|
||||
(*mptoken)[sfOwnerNode] = *ownerNode;
|
||||
|
||||
addSponsorToLedgerEntry(mptoken, sponsorSle);
|
||||
addSponsorToLedgerEntry(mptoken.mutableSle(), sponsorSle);
|
||||
|
||||
view.insert(mptoken);
|
||||
mptoken.insert();
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
@@ -966,13 +950,12 @@ checkCreateMPT(
|
||||
{
|
||||
return err;
|
||||
}
|
||||
auto const sleAcct = view.peek(keylet::account(holder));
|
||||
AccountRootEntry<ApplyView> const sleAcct{keylet::account(holder), view};
|
||||
if (!sleAcct)
|
||||
{
|
||||
return tecINTERNAL;
|
||||
}
|
||||
|
||||
increaseOwnerCount(view, sleAcct, sponsorSle, 1, j);
|
||||
increaseOwnerCount(view, sleAcct.mutableSle(), sponsorSle, 1, j);
|
||||
}
|
||||
return tesSUCCESS;
|
||||
}
|
||||
@@ -994,7 +977,7 @@ availableMPTAmount(SLE const& sleIssuance)
|
||||
std::int64_t
|
||||
availableMPTAmount(ReadView const& view, MPTID const& mptID)
|
||||
{
|
||||
auto const sle = view.read(keylet::mptokenIssuance(mptID));
|
||||
MPTokenIssuanceEntry<ReadView> const sle{keylet::mptokenIssuance(mptID), view};
|
||||
if (!sle)
|
||||
Throw<std::runtime_error>(transHuman(tecINTERNAL));
|
||||
return availableMPTAmount(*sle);
|
||||
@@ -1018,7 +1001,7 @@ issuerFundsToSelfIssue(ReadView const& view, MPTIssue const& issue)
|
||||
{
|
||||
STAmount amount{issue};
|
||||
|
||||
auto const sle = view.read(keylet::mptokenIssuance(issue));
|
||||
MPTokenIssuanceEntry<ReadView> const sle{keylet::mptokenIssuance(issue), view};
|
||||
if (!sle)
|
||||
return amount;
|
||||
auto const available = availableMPTAmount(*sle);
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
@@ -182,7 +182,8 @@ getPageForToken(
|
||||
? narr[kDirMaxTokensPerPage - 1].getFieldH256(sfNFTokenID).next()
|
||||
: carr[0].getFieldH256(sfNFTokenID);
|
||||
|
||||
auto np = std::make_shared<SLE>(keylet::nftokenPage(base, tokenIDForNewPage));
|
||||
NFTokenPageEntry<ApplyView> np{base, tokenIDForNewPage, view};
|
||||
np.newSLE();
|
||||
XRPL_ASSERT(np->key() > base.key, "xrpl::nft::getPageForToken : valid NFT page index");
|
||||
np->setFieldArray(sfNFTokens, narr);
|
||||
np->setFieldH256(sfNextPageMin, cp->key());
|
||||
@@ -191,14 +192,14 @@ getPageForToken(
|
||||
{
|
||||
np->setFieldH256(sfPreviousPageMin, *ppm);
|
||||
|
||||
if (auto p3 = view.peek(Keylet(ltNFTOKEN_PAGE, *ppm)))
|
||||
if (NFTokenPageEntry<ApplyView> p3{Keylet(ltNFTOKEN_PAGE, *ppm), view})
|
||||
{
|
||||
p3->setFieldH256(sfNextPageMin, np->key());
|
||||
view.update(p3);
|
||||
p3.update();
|
||||
}
|
||||
}
|
||||
|
||||
view.insert(np);
|
||||
np.insert();
|
||||
|
||||
cp->setFieldArray(sfNFTokens, carr);
|
||||
cp->setFieldH256(sfPreviousPageMin, np->key());
|
||||
@@ -206,7 +207,7 @@ getPageForToken(
|
||||
|
||||
createCallback(view, owner);
|
||||
|
||||
return (first.key < np->key()) ? np : cp;
|
||||
return (first.key < np->key()) ? np.mutableSle() : cp;
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -230,7 +231,7 @@ changeTokenURI(
|
||||
uint256 const& nftokenID,
|
||||
std::optional<xrpl::Slice> const& uri)
|
||||
{
|
||||
SLE::pointer const page = locatePage(view, owner, nftokenID);
|
||||
NFTokenPageEntry<ApplyView> page{locatePage(view, owner, nftokenID), view};
|
||||
|
||||
// If the page couldn't be found, the given NFT isn't owned by this account
|
||||
if (!page)
|
||||
@@ -254,7 +255,7 @@ changeTokenURI(
|
||||
nftIter->makeFieldAbsent(sfURI);
|
||||
}
|
||||
|
||||
view.update(page);
|
||||
page.update();
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -269,10 +270,16 @@ insertToken(ApplyView& view, AccountID owner, STObject&& nft)
|
||||
// First, we need to locate the page the NFT belongs to, creating it
|
||||
// if necessary. This operation may fail if it is impossible to insert
|
||||
// the NFT.
|
||||
SLE::pointer const page =
|
||||
getPageForToken(view, owner, nft[sfNFTokenID], [](ApplyView& view, AccountID const& owner) {
|
||||
increaseOwnerCount(view, owner, {}, 1, beast::Journal{beast::Journal::getNullSink()});
|
||||
});
|
||||
NFTokenPageEntry<ApplyView> page{
|
||||
getPageForToken(
|
||||
view,
|
||||
owner,
|
||||
nft[sfNFTokenID],
|
||||
[](ApplyView& view, AccountID const& owner) {
|
||||
increaseOwnerCount(
|
||||
view, owner, {}, 1, beast::Journal{beast::Journal::getNullSink()});
|
||||
}),
|
||||
view};
|
||||
|
||||
if (!page)
|
||||
return tecNO_SUITABLE_NFTOKEN_PAGE;
|
||||
@@ -288,7 +295,7 @@ insertToken(ApplyView& view, AccountID owner, STObject&& nft)
|
||||
page->setFieldArray(sfNFTokens, arr);
|
||||
}
|
||||
|
||||
view.update(page);
|
||||
page.update();
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
@@ -332,13 +339,13 @@ mergePages(ApplyView& view, SLE::ref p1, SLE::ref p2)
|
||||
|
||||
if (auto const ppm = (*p1)[~sfPreviousPageMin])
|
||||
{
|
||||
auto p0 = view.peek(Keylet(ltNFTOKEN_PAGE, *ppm));
|
||||
NFTokenPageEntry<ApplyView> p0{Keylet(ltNFTOKEN_PAGE, *ppm), view};
|
||||
|
||||
if (!p0)
|
||||
Throw<std::runtime_error>("mergePages: p0 can't be located!");
|
||||
|
||||
p0->setFieldH256(sfNextPageMin, p2->key());
|
||||
view.update(p0);
|
||||
p0.update();
|
||||
|
||||
p2->setFieldH256(sfPreviousPageMin, *ppm);
|
||||
}
|
||||
@@ -355,13 +362,13 @@ mergePages(ApplyView& view, SLE::ref p1, SLE::ref p2)
|
||||
TER
|
||||
removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID)
|
||||
{
|
||||
SLE::pointer const page = locatePage(view, owner, nftokenID);
|
||||
NFTokenPageEntry<ApplyView> const page{locatePage(view, owner, nftokenID), view};
|
||||
|
||||
// If the page couldn't be found, the given NFT isn't owned by this account
|
||||
if (!page)
|
||||
return tecNO_ENTRY;
|
||||
|
||||
return removeToken(view, owner, nftokenID, page);
|
||||
return removeToken(view, owner, nftokenID, page.mutableSle());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -402,8 +409,8 @@ removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID, S
|
||||
return page2;
|
||||
};
|
||||
|
||||
auto const prev = loadPage(curr, sfPreviousPageMin);
|
||||
auto const next = loadPage(curr, sfNextPageMin);
|
||||
NFTokenPageEntry<ApplyView> prev{loadPage(curr, sfPreviousPageMin), view};
|
||||
NFTokenPageEntry<ApplyView> next{loadPage(curr, sfNextPageMin), view};
|
||||
|
||||
if (!arr.empty())
|
||||
{
|
||||
@@ -415,10 +422,10 @@ removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID, S
|
||||
|
||||
std::uint32_t cnt = 0;
|
||||
|
||||
if (prev && mergePages(view, prev, curr))
|
||||
if (prev && mergePages(view, prev.mutableSle(), curr))
|
||||
++cnt;
|
||||
|
||||
if (next && mergePages(view, curr, next))
|
||||
if (next && mergePages(view, curr, next.mutableSle()))
|
||||
++cnt;
|
||||
|
||||
if (cnt != 0)
|
||||
@@ -449,9 +456,9 @@ removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID, S
|
||||
curr->at(sfPreviousPageMin) = *prevLink;
|
||||
|
||||
// Also fix up the NextPageMin link in the new Previous.
|
||||
auto const newPrev = loadPage(curr, sfPreviousPageMin);
|
||||
NFTokenPageEntry<ApplyView> newPrev{loadPage(curr, sfPreviousPageMin), view};
|
||||
newPrev->at(sfNextPageMin) = curr->key();
|
||||
view.update(newPrev);
|
||||
newPrev.update();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -461,7 +468,7 @@ removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID, S
|
||||
decreaseOwnerCount(view, owner, {}, 1, beast::Journal{beast::Journal::getNullSink()});
|
||||
|
||||
view.update(curr);
|
||||
view.erase(prev);
|
||||
prev.erase();
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -476,7 +483,7 @@ removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID, S
|
||||
prev->makeFieldAbsent(sfNextPageMin);
|
||||
}
|
||||
|
||||
view.update(prev);
|
||||
prev.update();
|
||||
}
|
||||
|
||||
if (next)
|
||||
@@ -491,7 +498,7 @@ removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID, S
|
||||
next->makeFieldAbsent(sfPreviousPageMin);
|
||||
}
|
||||
|
||||
view.update(next);
|
||||
next.update();
|
||||
}
|
||||
|
||||
view.erase(curr);
|
||||
@@ -521,7 +528,7 @@ removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID, S
|
||||
std::optional<STObject>
|
||||
findToken(ReadView const& view, AccountID const& owner, uint256 const& nftokenID)
|
||||
{
|
||||
SLE::const_pointer const page = locatePage(view, owner, nftokenID);
|
||||
NFTokenPageEntry<ReadView> const page{locatePage(view, owner, nftokenID), view};
|
||||
|
||||
// If the page couldn't be found, the given NFT isn't owned by this account
|
||||
if (!page)
|
||||
@@ -569,7 +576,7 @@ removeTokenOffersWithLimit(ApplyView& view, Keylet const& directory, std::size_t
|
||||
|
||||
do
|
||||
{
|
||||
auto const page = view.peek(keylet::page(directory, *pageIndex));
|
||||
DirectoryNodeEntry<ApplyView> const page{keylet::page(directory, *pageIndex), view};
|
||||
if (!page)
|
||||
break;
|
||||
|
||||
@@ -587,9 +594,10 @@ removeTokenOffersWithLimit(ApplyView& view, Keylet const& directory, std::size_t
|
||||
// deleting during iteration.
|
||||
for (int i = offerIndexes.size() - 1; i >= 0; --i)
|
||||
{
|
||||
if (auto const offer = view.peek(keylet::nftokenOffer(offerIndexes[i])))
|
||||
if (NFTokenOfferEntry<ApplyView> const offer{
|
||||
keylet::nftokenOffer(offerIndexes[i]), view})
|
||||
{
|
||||
if (deleteTokenOffer(view, offer))
|
||||
if (deleteTokenOffer(view, offer.mutableSle()))
|
||||
{
|
||||
++deletedOffersCount;
|
||||
}
|
||||
@@ -614,25 +622,11 @@ deleteTokenOffer(ApplyView& view, SLE::ref offer)
|
||||
if (offer->getType() != ltNFTOKEN_OFFER)
|
||||
return false;
|
||||
|
||||
auto const owner = (*offer)[sfOwner];
|
||||
|
||||
if (!view.dirRemove(keylet::ownerDir(owner), (*offer)[sfOwnerNode], offer->key(), false))
|
||||
return false;
|
||||
|
||||
auto const nftokenID = (*offer)[sfNFTokenID];
|
||||
|
||||
if (!view.dirRemove(
|
||||
offer->isFlag(lsfSellNFToken) ? keylet::nftSells(nftokenID)
|
||||
: keylet::nftBuys(nftokenID),
|
||||
(*offer)[sfNFTokenOfferNode],
|
||||
offer->key(),
|
||||
false))
|
||||
return false;
|
||||
|
||||
decreaseOwnerCount(view, owner, {}, 1, beast::Journal{beast::Journal::getNullSink()});
|
||||
|
||||
view.erase(offer);
|
||||
return true;
|
||||
// Unlink from the owner directory and the token's buy/sell offer directory,
|
||||
// decrement the owner's OwnerCount, and erase. See
|
||||
// NFTokenOfferEntry::destroy().
|
||||
NFTokenOfferEntry<ApplyView> offerEntry{offer, view};
|
||||
return isTesSuccess(offerEntry.destroy());
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -729,7 +723,7 @@ repairNFTokenDirectoryLinks(ApplyView& view, AccountID const& owner)
|
||||
nextPage->at(sfPreviousPageMin) = *prevLink;
|
||||
|
||||
// Also fix up the NextPageMin link in the new Previous.
|
||||
auto const newPrev = view.peek(Keylet(ltNFTOKEN_PAGE, *prevLink));
|
||||
NFTokenPageEntry<ApplyView> newPrev{Keylet(ltNFTOKEN_PAGE, *prevLink), view};
|
||||
if (!newPrev)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
@@ -739,7 +733,7 @@ repairNFTokenDirectoryLinks(ApplyView& view, AccountID const& owner)
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
newPrev->at(sfNextPageMin) = nextPage->key();
|
||||
view.update(newPrev);
|
||||
newPrev.update();
|
||||
}
|
||||
view.erase(page);
|
||||
view.insert(nextPage);
|
||||
@@ -844,7 +838,7 @@ tokenOfferCreatePreclaim(
|
||||
|
||||
if (nftIssuer != acctID && ((nftFlags & nft::kFlagTransferable) == 0))
|
||||
{
|
||||
auto const root = view.read(keylet::account(nftIssuer));
|
||||
AccountRootEntry<ReadView> const root{keylet::account(nftIssuer), view};
|
||||
XRPL_ASSERT(root, "xrpl::nft::tokenOfferCreatePreclaim : non-null account");
|
||||
|
||||
if (auto minter = (*root)[~sfNFTokenMinter]; minter != acctID)
|
||||
@@ -869,7 +863,7 @@ tokenOfferCreatePreclaim(
|
||||
{
|
||||
// If a destination is specified, the destination must already be in
|
||||
// the ledger.
|
||||
auto const sleDst = view.read(keylet::account(*dest));
|
||||
AccountRootEntry<ReadView> const sleDst{keylet::account(*dest), view};
|
||||
|
||||
if (!sleDst)
|
||||
return tecNO_DST;
|
||||
@@ -881,7 +875,7 @@ tokenOfferCreatePreclaim(
|
||||
|
||||
if (owner)
|
||||
{
|
||||
auto const sleOwner = view.read(keylet::account(*owner));
|
||||
AccountRootEntry<ReadView> const sleOwner{keylet::account(*owner), view};
|
||||
|
||||
// defensively check
|
||||
// it should not be possible to specify owner that doesn't exist
|
||||
@@ -920,63 +914,27 @@ tokenOfferCreateApply(
|
||||
beast::Journal j,
|
||||
std::uint32_t txFlags)
|
||||
{
|
||||
Keylet const acctKeylet = keylet::account(acctID);
|
||||
if (auto const acct = view.read(acctKeylet);
|
||||
priorBalance < accountReserve(view, acct, j, {.ownerCountDelta = 1}))
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
|
||||
auto const offerID = keylet::nftokenOffer(acctID, seqProxy.value());
|
||||
|
||||
// Create the offer:
|
||||
{
|
||||
// Token offers are always added to the owner's owner directory:
|
||||
auto const ownerNode =
|
||||
view.dirInsert(keylet::ownerDir(acctID), offerID, describeOwnerDir(acctID));
|
||||
bool const isSellOffer = (txFlags & tfSellNFToken) != 0u;
|
||||
|
||||
if (!ownerNode)
|
||||
return tecDIR_FULL; // LCOV_EXCL_LINE
|
||||
NFTokenOfferEntry<ApplyView> offer{offerID, view, j};
|
||||
offer.newSLE();
|
||||
(*offer)[sfOwner] = acctID;
|
||||
(*offer)[sfNFTokenID] = nftokenID;
|
||||
(*offer)[sfAmount] = amount;
|
||||
(*offer)[sfFlags] = isSellOffer ? lsfSellNFToken : 0u;
|
||||
|
||||
bool const isSellOffer = (txFlags & tfSellNFToken) != 0u;
|
||||
if (expiration)
|
||||
(*offer)[sfExpiration] = *expiration;
|
||||
|
||||
// Token offers are also added to the token's buy or sell offer
|
||||
// directory
|
||||
auto const offerNode = view.dirInsert(
|
||||
isSellOffer ? keylet::nftSells(nftokenID) : keylet::nftBuys(nftokenID),
|
||||
offerID,
|
||||
[&nftokenID, isSellOffer](SLE::ref sle) {
|
||||
(*sle)[sfFlags] = isSellOffer ? lsfNFTokenSellOffers : lsfNFTokenBuyOffers;
|
||||
(*sle)[sfNFTokenID] = nftokenID;
|
||||
});
|
||||
if (dest)
|
||||
(*offer)[sfDestination] = *dest;
|
||||
|
||||
if (!offerNode)
|
||||
return tecDIR_FULL; // LCOV_EXCL_LINE
|
||||
|
||||
std::uint32_t sleFlags = 0;
|
||||
|
||||
if (isSellOffer)
|
||||
sleFlags |= lsfSellNFToken;
|
||||
|
||||
auto offer = std::make_shared<SLE>(offerID);
|
||||
(*offer)[sfOwner] = acctID;
|
||||
(*offer)[sfNFTokenID] = nftokenID;
|
||||
(*offer)[sfAmount] = amount;
|
||||
(*offer)[sfFlags] = sleFlags;
|
||||
(*offer)[sfOwnerNode] = *ownerNode;
|
||||
(*offer)[sfNFTokenOfferNode] = *offerNode;
|
||||
|
||||
if (expiration)
|
||||
(*offer)[sfExpiration] = *expiration;
|
||||
|
||||
if (dest)
|
||||
(*offer)[sfDestination] = *dest;
|
||||
|
||||
view.insert(offer);
|
||||
}
|
||||
|
||||
// Update owner count.
|
||||
increaseOwnerCount(view, acctID, {}, 1, j);
|
||||
|
||||
return tesSUCCESS;
|
||||
// Reserve check + link into the owner directory and the token's buy/sell
|
||||
// offer directory + bump the owner's OwnerCount + insert. See
|
||||
// NFTokenOfferEntry::create().
|
||||
return offer.create(priorBalance);
|
||||
}
|
||||
|
||||
TER
|
||||
@@ -991,7 +949,7 @@ checkTrustlineAuthorized(
|
||||
|
||||
if (view.rules().enabled(fixEnforceNFTokenTrustlineV2))
|
||||
{
|
||||
auto const issuerAccount = view.read(keylet::account(issue.account));
|
||||
AccountRootEntry<ReadView> const issuerAccount{keylet::account(issue.account), view};
|
||||
if (!issuerAccount)
|
||||
{
|
||||
JLOG(j.debug()) << "xrpl::nft::checkTrustlineAuthorized: can't "
|
||||
@@ -1011,7 +969,7 @@ checkTrustlineAuthorized(
|
||||
|
||||
if (issuerAccount->isFlag(lsfRequireAuth))
|
||||
{
|
||||
auto const trustLine = view.read(keylet::trustLine(id, issue.account, issue.currency));
|
||||
RippleStateEntry<ReadView> const trustLine{id, issue.account, issue.currency, view};
|
||||
|
||||
if (!trustLine)
|
||||
{
|
||||
@@ -1043,7 +1001,7 @@ checkTrustlineDeepFrozen(
|
||||
|
||||
if (view.rules().enabled(featureDeepFreeze))
|
||||
{
|
||||
auto const issuerAccount = view.read(keylet::account(issue.account));
|
||||
AccountRootEntry<ReadView> const issuerAccount{keylet::account(issue.account), view};
|
||||
if (!issuerAccount)
|
||||
{
|
||||
JLOG(j.debug()) << "xrpl::nft::checkTrustlineDeepFrozen: can't "
|
||||
@@ -1061,7 +1019,7 @@ checkTrustlineDeepFrozen(
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
auto const trustLine = view.read(keylet::trustLine(id, issue.account, issue.currency));
|
||||
RippleStateEntry<ReadView> const trustLine{id, issue.account, issue.currency, view};
|
||||
|
||||
if (!trustLine)
|
||||
{
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
#include <xrpl/ledger/helpers/OfferHelpers.h>
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h> // IWYU pragma: keep
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STArray.h> // IWYU pragma: keep
|
||||
#include <xrpl/protocol/STArray.h> // IWYU pragma: keep
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
|
||||
@@ -19,47 +15,13 @@ offerDelete(ApplyView& view, SLE::ref sle, beast::Journal j)
|
||||
{
|
||||
if (!sle)
|
||||
return tesSUCCESS;
|
||||
auto offerIndex = sle->key();
|
||||
auto owner = sle->getAccountID(sfAccount);
|
||||
|
||||
// Detect legacy directories.
|
||||
uint256 const uDirectory = sle->getFieldH256(sfBookDirectory);
|
||||
|
||||
if (!view.dirRemove(keylet::ownerDir(owner), sle->getFieldU64(sfOwnerNode), offerIndex, false))
|
||||
{
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
if (!view.dirRemove(keylet::page(uDirectory), sle->getFieldU64(sfBookNode), offerIndex, false))
|
||||
{
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
if (sle->isFieldPresent(sfAdditionalBooks))
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
sle->isFlag(lsfHybrid) && sle->isFieldPresent(sfDomainID),
|
||||
"xrpl::offerDelete : should be a hybrid domain offer");
|
||||
|
||||
auto const& additionalBookDirs = sle->getFieldArray(sfAdditionalBooks);
|
||||
|
||||
for (auto const& bookDir : additionalBookDirs)
|
||||
{
|
||||
auto const& dirIndex = bookDir.getFieldH256(sfBookDirectory);
|
||||
auto const& dirNode = bookDir.getFieldU64(sfBookNode);
|
||||
|
||||
if (!view.dirRemove(keylet::page(dirIndex), dirNode, offerIndex, false))
|
||||
{
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
decreaseOwnerCountForObject(view, owner, sle, 1, j);
|
||||
|
||||
view.erase(sle);
|
||||
|
||||
return tesSUCCESS;
|
||||
// Unlink the offer from its owner directory and every order-book page it
|
||||
// sits in (including a hybrid offer's additional books), decrement the
|
||||
// owner's OwnerCount (refunding a reserve sponsor when present), and erase
|
||||
// it. See OfferEntry::destroy().
|
||||
OfferEntry<ApplyView> offer{sle, view, j};
|
||||
return offer.destroy();
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -1,15 +1,13 @@
|
||||
#include <xrpl/ledger/helpers/PaymentChannelHelpers.h>
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
@@ -22,48 +20,28 @@
|
||||
namespace xrpl {
|
||||
|
||||
TER
|
||||
closeChannel(SLE::ref slep, ApplyView& view, uint256 const& key, beast::Journal j)
|
||||
closeChannel(
|
||||
PayChannelEntry<ApplyView>& slep,
|
||||
ApplyView& view,
|
||||
uint256 const& key,
|
||||
beast::Journal j)
|
||||
{
|
||||
AccountID const src = (*slep)[sfAccount];
|
||||
// Remove PayChan from owner directory
|
||||
{
|
||||
auto const page = (*slep)[sfOwnerNode];
|
||||
if (!view.dirRemove(keylet::ownerDir(src), page, key, true))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.fatal()) << "Could not remove paychan from src owner directory";
|
||||
return tefBAD_LEDGER;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
}
|
||||
|
||||
// Remove PayChan from recipient's owner directory, if present.
|
||||
if (auto const page = (*slep)[~sfDestinationNode])
|
||||
{
|
||||
auto const dst = (*slep)[sfDestination];
|
||||
if (!view.dirRemove(keylet::ownerDir(dst), *page, key, true))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.fatal()) << "Could not remove paychan from dst owner directory";
|
||||
return tefBAD_LEDGER;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
}
|
||||
|
||||
// Transfer amount back to owner, decrement owner count
|
||||
auto const sle = view.peek(keylet::account(src));
|
||||
// Transfer any remaining balance back to the owner.
|
||||
AccountRootEntry<ApplyView> sle{src, view};
|
||||
if (!sle)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
XRPL_ASSERT(
|
||||
(*slep)[sfAmount] >= (*slep)[sfBalance], "xrpl::closeChannel : minimum channel amount");
|
||||
(*sle)[sfBalance] = (*sle)[sfBalance] + (*slep)[sfAmount] - (*slep)[sfBalance];
|
||||
decreaseOwnerCountForObject(view, sle, slep, 1, j);
|
||||
view.update(sle);
|
||||
sle.update();
|
||||
|
||||
// Remove PayChan from ledger
|
||||
view.erase(slep);
|
||||
return tesSUCCESS;
|
||||
// Unlink the channel from the owner and destination directories, decrement
|
||||
// the owner's OwnerCount (refunding any reserve sponsor), and erase it. See
|
||||
// PayChannelEntry::ownerDirs().
|
||||
return slep.destroy();
|
||||
}
|
||||
|
||||
uint32_t
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/CredentialHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
@@ -31,7 +32,7 @@ accountInDomain(ReadView const& view, AccountID const& account, Domain const& do
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
auto const sleDomain = view.read(keylet::permissionedDomain(domainID));
|
||||
PermissionedDomainEntry<ReadView> const sleDomain{keylet::permissionedDomain(domainID), view};
|
||||
if (!sleDomain)
|
||||
return false;
|
||||
|
||||
@@ -42,8 +43,8 @@ accountInDomain(ReadView const& view, AccountID const& account, Domain const& do
|
||||
auto const& credentials = sleDomain->getFieldArray(sfAcceptedCredentials);
|
||||
|
||||
bool const inDomain = std::ranges::any_of(credentials, [&](auto const& credential) {
|
||||
auto const sleCred = view.read(
|
||||
keylet::credential(account, credential[sfIssuer], credential[sfCredentialType]));
|
||||
CredentialEntry<ReadView> const sleCred{
|
||||
keylet::credential(account, credential[sfIssuer], credential[sfCredentialType]), view};
|
||||
if (!sleCred || !sleCred->isFlag(lsfAccepted))
|
||||
return false;
|
||||
|
||||
@@ -60,7 +61,7 @@ offerInDomain(
|
||||
Domain const& domainID,
|
||||
beast::Journal j)
|
||||
{
|
||||
auto const sleOffer = view.read(keylet::offer(offerID));
|
||||
OfferEntry<ReadView> const sleOffer{keylet::offer(offerID), view};
|
||||
|
||||
// The following are defensive checks that should never happen, since this
|
||||
// function is used to check against the order book offers, which should not
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h> // IWYU pragma: keep
|
||||
@@ -17,7 +18,10 @@
|
||||
namespace xrpl {
|
||||
|
||||
[[nodiscard]] std::optional<STAmount>
|
||||
assetsToSharesDeposit(SLE::const_ref vault, SLE::const_ref issuance, STAmount const& assets)
|
||||
assetsToSharesDeposit(
|
||||
VaultEntry<ReadView> const& vault,
|
||||
MPTokenIssuanceEntry<ReadView> const& issuance,
|
||||
STAmount const& assets)
|
||||
{
|
||||
XRPL_ASSERT(!assets.negative(), "xrpl::assetsToSharesDeposit : non-negative assets");
|
||||
XRPL_ASSERT(
|
||||
@@ -41,7 +45,10 @@ assetsToSharesDeposit(SLE::const_ref vault, SLE::const_ref issuance, STAmount co
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<STAmount>
|
||||
sharesToAssetsDeposit(SLE::const_ref vault, SLE::const_ref issuance, STAmount const& shares)
|
||||
sharesToAssetsDeposit(
|
||||
VaultEntry<ReadView> const& vault,
|
||||
MPTokenIssuanceEntry<ReadView> const& issuance,
|
||||
STAmount const& shares)
|
||||
{
|
||||
XRPL_ASSERT(!shares.negative(), "xrpl::sharesToAssetsDeposit : non-negative shares");
|
||||
XRPL_ASSERT(
|
||||
@@ -65,8 +72,8 @@ sharesToAssetsDeposit(SLE::const_ref vault, SLE::const_ref issuance, STAmount co
|
||||
|
||||
[[nodiscard]] std::optional<STAmount>
|
||||
assetsToSharesWithdraw(
|
||||
SLE::const_ref vault,
|
||||
SLE::const_ref issuance,
|
||||
VaultEntry<ReadView> const& vault,
|
||||
MPTokenIssuanceEntry<ReadView> const& issuance,
|
||||
STAmount const& assets,
|
||||
TruncateShares truncate,
|
||||
WaiveUnrealizedLoss waive)
|
||||
@@ -94,8 +101,8 @@ assetsToSharesWithdraw(
|
||||
|
||||
[[nodiscard]] std::optional<STAmount>
|
||||
sharesToAssetsWithdraw(
|
||||
SLE::const_ref vault,
|
||||
SLE::const_ref issuance,
|
||||
VaultEntry<ReadView> const& vault,
|
||||
MPTokenIssuanceEntry<ReadView> const& issuance,
|
||||
STAmount const& shares,
|
||||
WaiveUnrealizedLoss waive)
|
||||
{
|
||||
@@ -118,7 +125,10 @@ sharesToAssetsWithdraw(
|
||||
}
|
||||
|
||||
[[nodiscard]] bool
|
||||
isSoleShareholder(ReadView const& view, AccountID const& account, SLE::const_ref issuance)
|
||||
isSoleShareholder(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
MPTokenIssuanceEntry<ReadView> const& issuance)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
issuance && issuance->getType() == ltMPTOKEN_ISSUANCE,
|
||||
@@ -130,7 +140,7 @@ isSoleShareholder(ReadView const& view, AccountID const& account, SLE::const_ref
|
||||
|
||||
auto const shareMPTID =
|
||||
makeMptID(issuance->getFieldU32(sfSequence), issuance->getAccountID(sfIssuer));
|
||||
auto const sleToken = view.read(keylet::mptoken(shareMPTID, account));
|
||||
MPTokenEntry<ReadView> const sleToken{keylet::mptoken(shareMPTID, account), view};
|
||||
if (!sleToken)
|
||||
return false; // LCOV_EXCL_LINE
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
#include <xrpl/ledger/helpers/NFTokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/OfferHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEBase.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
@@ -224,7 +226,7 @@ AccountDelete::preclaim(PreclaimContext const& ctx)
|
||||
AccountID const account{ctx.tx[sfAccount]};
|
||||
AccountID const dst{ctx.tx[sfDestination]};
|
||||
|
||||
auto sleDst = ctx.view.read(keylet::account(dst));
|
||||
AccountRootEntry<ReadView> const sleDst{keylet::account(dst), ctx.view};
|
||||
|
||||
if (!sleDst)
|
||||
return tecNO_DST;
|
||||
@@ -248,7 +250,7 @@ AccountDelete::preclaim(PreclaimContext const& ctx)
|
||||
}
|
||||
}
|
||||
|
||||
auto sleAccount = ctx.view.read(keylet::account(account));
|
||||
AccountRootEntry<ReadView> const sleAccount{keylet::account(account), ctx.view};
|
||||
XRPL_ASSERT(sleAccount, "xrpl::AccountDelete::preclaim : non-null account");
|
||||
if (!sleAccount)
|
||||
return terNO_ACCOUNT;
|
||||
@@ -262,8 +264,9 @@ AccountDelete::preclaim(PreclaimContext const& ctx)
|
||||
Keylet const first = keylet::nftokenPageMin(account);
|
||||
Keylet const last = keylet::nftokenPageMax(account);
|
||||
|
||||
auto const cp = ctx.view.read(
|
||||
Keylet(ltNFTOKEN_PAGE, ctx.view.succ(first.key, last.key.next()).value_or(last.key)));
|
||||
NFTokenPageEntry<ReadView> const cp{
|
||||
Keylet(ltNFTOKEN_PAGE, ctx.view.succ(first.key, last.key.next()).value_or(last.key)),
|
||||
ctx.view};
|
||||
if (cp)
|
||||
return tecHAS_OBLIGATIONS;
|
||||
|
||||
@@ -322,7 +325,7 @@ AccountDelete::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
// Make sure any directory node types that we find are the kind
|
||||
// we can delete.
|
||||
auto sleItem = ctx.view.read(keylet::child(dirEntry));
|
||||
ReadOnlySLE const sleItem{keylet::child(dirEntry), ctx.view};
|
||||
if (!sleItem)
|
||||
{
|
||||
// Directory node has an invalid index. Bail out.
|
||||
@@ -351,11 +354,11 @@ AccountDelete::preclaim(PreclaimContext const& ctx)
|
||||
TER
|
||||
AccountDelete::doApply()
|
||||
{
|
||||
auto src = view().peek(keylet::account(accountID_));
|
||||
AccountRootEntry<ApplyView> src{keylet::account(accountID_), view()};
|
||||
XRPL_ASSERT(src, "xrpl::AccountDelete::doApply : non-null source account");
|
||||
|
||||
auto const dstID = ctx_.tx[sfDestination];
|
||||
auto dst = view().peek(keylet::account(dstID));
|
||||
AccountRootEntry<ApplyView> dst{keylet::account(dstID), view()};
|
||||
XRPL_ASSERT(dst, "xrpl::AccountDelete::doApply : non-null destination account");
|
||||
|
||||
if (!src || !dst)
|
||||
@@ -363,8 +366,8 @@ AccountDelete::doApply()
|
||||
|
||||
if (ctx_.tx.isFieldPresent(sfCredentialIDs))
|
||||
{
|
||||
if (auto err =
|
||||
verifyDepositPreauth(ctx_.tx, ctx_.view(), accountID_, dstID, dst, ctx_.journal);
|
||||
if (auto err = verifyDepositPreauth(
|
||||
ctx_.tx, ctx_.view(), accountID_, dstID, dst.sle(), ctx_.journal);
|
||||
!isTesSuccess(err))
|
||||
return err;
|
||||
}
|
||||
@@ -446,8 +449,8 @@ AccountDelete::doApply()
|
||||
if (remainingBalance > XRPAmount(0) && dst->isFlag(lsfPasswordSpent))
|
||||
dst->clearFlag(lsfPasswordSpent);
|
||||
|
||||
view().update(dst);
|
||||
view().erase(src);
|
||||
dst.update();
|
||||
src.erase();
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
@@ -170,7 +171,7 @@ AccountSet::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
auto const id = ctx.tx[sfAccount];
|
||||
|
||||
auto const sle = ctx.view.read(keylet::account(id));
|
||||
AccountRootEntry<ReadView> const sle{keylet::account(id), ctx.view};
|
||||
if (!sle)
|
||||
return terNO_ACCOUNT;
|
||||
|
||||
@@ -224,7 +225,7 @@ AccountSet::preclaim(PreclaimContext const& ctx)
|
||||
TER
|
||||
AccountSet::doApply()
|
||||
{
|
||||
auto const sle = view().peek(keylet::account(accountID_));
|
||||
AccountRootEntry<ApplyView> sle{keylet::account(accountID_), view()};
|
||||
if (!sle)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -312,7 +313,8 @@ AccountSet::doApply()
|
||||
return tecNEED_MASTER_KEY;
|
||||
}
|
||||
|
||||
if ((!sle->isFieldPresent(sfRegularKey)) && (!view().peek(keylet::signerList(accountID_))))
|
||||
if ((!sle->isFieldPresent(sfRegularKey)) &&
|
||||
(!SignerListEntry<ApplyView>{keylet::signerList(accountID_), view()}))
|
||||
{
|
||||
// Account has no regular key or multi-signer signer list.
|
||||
return tecNO_ALTERNATIVE_KEY;
|
||||
@@ -582,7 +584,7 @@ AccountSet::doApply()
|
||||
if (uFlagsIn != uFlagsOut)
|
||||
sle->setFieldU32(sfFlags, uFlagsOut);
|
||||
|
||||
ctx_.view().update(sle);
|
||||
sle.update();
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
#include <xrpl/basics/Slice.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/PublicKey.h>
|
||||
@@ -25,7 +27,7 @@ SetRegularKey::calculateBaseFee(ReadView const& view, STTx const& tx)
|
||||
{
|
||||
if (calcAccountID(PublicKey(makeSlice(spk))) == id)
|
||||
{
|
||||
auto const sle = view.read(keylet::account(id));
|
||||
AccountRootEntry<ReadView> const sle{keylet::account(id), view};
|
||||
|
||||
if (sle && !sle->isFlag(lsfPasswordSpent))
|
||||
{
|
||||
@@ -53,7 +55,7 @@ SetRegularKey::preflight(PreflightContext const& ctx)
|
||||
TER
|
||||
SetRegularKey::doApply()
|
||||
{
|
||||
auto const sle = view().peek(keylet::account(accountID_));
|
||||
AccountRootEntry<ApplyView> sle{keylet::account(accountID_), view()};
|
||||
if (!sle)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -67,13 +69,14 @@ SetRegularKey::doApply()
|
||||
else
|
||||
{
|
||||
// Account has disabled master key and no multi-signer signer list.
|
||||
if (sle->isFlag(lsfDisableMaster) && !view().peek(keylet::signerList(accountID_)))
|
||||
if (sle->isFlag(lsfDisableMaster) &&
|
||||
!SignerListEntry<ApplyView>{keylet::signerList(accountID_), view()})
|
||||
return tecNO_ALTERNATIVE_KEY;
|
||||
|
||||
sle->makeFieldAbsent(sfRegularKey);
|
||||
}
|
||||
|
||||
ctx_.view().update(sle);
|
||||
sle.update();
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include <xrpl/ledger/RawView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
@@ -719,23 +719,15 @@ finalizeClaimHelper(
|
||||
}
|
||||
}
|
||||
|
||||
if (auto const sleClaimID = outerSb.peek(claimIDKeylet))
|
||||
if (XChainOwnedClaimIDEntry<ApplyView> sleClaimID{claimIDKeylet, outerSb, j})
|
||||
{
|
||||
auto const cidOwner = (*sleClaimID)[sfAccount];
|
||||
// Unlink the claim id from its owner's directory, decrement the owner's
|
||||
// OwnerCount, and erase it. See XChainOwnedClaimIDEntry.
|
||||
if (auto const ter = sleClaimID.destroy(); !isTesSuccess(ter))
|
||||
{
|
||||
// Remove the claim id
|
||||
auto const sleOwner = outerSb.peek(keylet::account(cidOwner));
|
||||
auto const page = (*sleClaimID)[sfOwnerNode];
|
||||
if (!outerSb.dirRemove(keylet::ownerDir(cidOwner), page, sleClaimID->key(), true))
|
||||
{
|
||||
JLOG(j.fatal()) << "Unable to delete xchain seq number from owner.";
|
||||
result.rmSleTer = tefBAD_LEDGER;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Remove the claim id from the ledger
|
||||
decreaseOwnerCountForObject(outerSb, sleOwner, sleClaimID, 1, j);
|
||||
outerSb.erase(sleClaimID);
|
||||
JLOG(j.fatal()) << "Unable to delete xchain seq number from owner.";
|
||||
result.rmSleTer = ter;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -805,24 +797,27 @@ readOrpeekBridge(F&& getter, STXChainBridge const& bridgeSpec)
|
||||
return tryGet(STXChainBridge::ChainType::Issuing);
|
||||
}
|
||||
|
||||
SLE::pointer
|
||||
BridgeEntry<ApplyView>
|
||||
peekBridge(ApplyView& v, STXChainBridge const& bridgeSpec)
|
||||
{
|
||||
return readOrpeekBridge<SLE>(
|
||||
[&v](STXChainBridge const& b, STXChainBridge::ChainType ct) -> SLE::pointer {
|
||||
return v.peek(keylet::bridge(b, ct));
|
||||
},
|
||||
bridgeSpec);
|
||||
return BridgeEntry<ApplyView>{
|
||||
readOrpeekBridge<SLE>(
|
||||
[&v](STXChainBridge const& b, STXChainBridge::ChainType ct) -> std::shared_ptr<SLE> {
|
||||
return v.peek(keylet::bridge(b, ct));
|
||||
},
|
||||
bridgeSpec),
|
||||
v};
|
||||
}
|
||||
|
||||
SLE::const_pointer
|
||||
BridgeEntry<ReadView>
|
||||
readBridge(ReadView const& v, STXChainBridge const& bridgeSpec)
|
||||
{
|
||||
return readOrpeekBridge<SLE const>(
|
||||
[&v](STXChainBridge const& b, STXChainBridge::ChainType ct) -> SLE::const_pointer {
|
||||
return v.read(keylet::bridge(b, ct));
|
||||
},
|
||||
bridgeSpec);
|
||||
return BridgeEntry<ReadView>{
|
||||
readOrpeekBridge<SLE const>(
|
||||
[&v](STXChainBridge const& b, STXChainBridge::ChainType ct)
|
||||
-> std::shared_ptr<SLE const> { return v.read(keylet::bridge(b, ct)); },
|
||||
bridgeSpec),
|
||||
v};
|
||||
}
|
||||
|
||||
// Precondition: all the claims in the range are consistent. They must sign for
|
||||
@@ -1127,27 +1122,18 @@ applyCreateAccountAttestations(
|
||||
}
|
||||
else if (createCID)
|
||||
{
|
||||
auto const createdSleClaimID = std::make_shared<SLE>(claimIDKeylet);
|
||||
XChainOwnedCreateAccountClaimIDEntry<ApplyView> createdSleClaimID{claimIDKeylet, psb, j};
|
||||
createdSleClaimID.newSLE();
|
||||
(*createdSleClaimID)[sfAccount] = doorAccount;
|
||||
(*createdSleClaimID)[sfXChainBridge] = bridgeSpec;
|
||||
(*createdSleClaimID)[sfXChainAccountCreateCount] = attBegin->createCount;
|
||||
createdSleClaimID->setFieldArray(sfXChainCreateAccountAttestations, curAtts.toSTArray());
|
||||
|
||||
// Add to owner directory of the door account
|
||||
auto const page = psb.dirInsert(
|
||||
keylet::ownerDir(doorAccount), claimIDKeylet, describeOwnerDir(doorAccount));
|
||||
if (!page)
|
||||
return tecDIR_FULL; // LCOV_EXCL_LINE
|
||||
(*createdSleClaimID)[sfOwnerNode] = *page;
|
||||
|
||||
auto const sleDoor = psb.peek(doorK);
|
||||
if (!sleDoor)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
// Reserve was already checked
|
||||
increaseOwnerCount(psb, sleDoor, {}, 1, j);
|
||||
psb.insert(createdSleClaimID);
|
||||
psb.update(sleDoor);
|
||||
// Link into the door account's owner directory, bump its OwnerCount, and
|
||||
// insert. The reserve was already checked against the door account
|
||||
// above, so skip create()'s check. See XChainOwnedCreateAccountClaimIDEntry.
|
||||
if (auto const ter = createdSleClaimID.create(std::nullopt); !isTesSuccess(ter))
|
||||
return ter;
|
||||
}
|
||||
|
||||
psb.apply(rawView);
|
||||
@@ -1472,7 +1458,8 @@ XChainCreateBridge::doApply()
|
||||
STXChainBridge::srcChain(account == bridgeSpec.lockingChainDoor());
|
||||
|
||||
Keylet const bridgeKeylet = keylet::bridge(bridgeSpec, chainType);
|
||||
auto const sleBridge = std::make_shared<SLE>(bridgeKeylet);
|
||||
BridgeEntry<ApplyView> sleBridge{bridgeKeylet, ctx_.view(), ctx_.journal};
|
||||
sleBridge.newSLE();
|
||||
|
||||
(*sleBridge)[sfAccount] = account;
|
||||
(*sleBridge)[sfSignatureReward] = reward;
|
||||
@@ -1483,21 +1470,9 @@ XChainCreateBridge::doApply()
|
||||
(*sleBridge)[sfXChainAccountCreateCount] = 0;
|
||||
(*sleBridge)[sfXChainAccountClaimCount] = 0;
|
||||
|
||||
// Add to owner directory
|
||||
{
|
||||
auto const page = ctx_.view().dirInsert(
|
||||
keylet::ownerDir(account), bridgeKeylet, describeOwnerDir(account));
|
||||
if (!page)
|
||||
return tecDIR_FULL; // LCOV_EXCL_LINE
|
||||
(*sleBridge)[sfOwnerNode] = *page;
|
||||
}
|
||||
|
||||
increaseOwnerCount(ctx_.view(), sleAcct, {}, 1, ctx_.journal);
|
||||
|
||||
ctx_.view().insert(sleBridge);
|
||||
ctx_.view().update(sleAcct);
|
||||
|
||||
return tesSUCCESS;
|
||||
// Reserve check (submitter's pre-fee balance) + link into the owner
|
||||
// directory + bump the owner's OwnerCount + insert. See BridgeEntry.
|
||||
return sleBridge.create(preFeeBalance_);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -2015,7 +1990,7 @@ XChainCreateClaimID::doApply()
|
||||
if (!sleAcct)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const sleBridge = peekBridge(ctx_.view(), bridgeSpec);
|
||||
auto sleBridge = peekBridge(ctx_.view(), bridgeSpec);
|
||||
if (!sleBridge)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -2029,14 +2004,14 @@ XChainCreateClaimID::doApply()
|
||||
(*sleBridge)[sfXChainClaimID] = claimID;
|
||||
|
||||
Keylet const claimIDKeylet = keylet::xChainClaimID(bridgeSpec, claimID);
|
||||
if (ctx_.view().exists(claimIDKeylet))
|
||||
XChainOwnedClaimIDEntry<ApplyView> sleClaimID{claimIDKeylet, ctx_.view(), ctx_.journal};
|
||||
if (sleClaimID)
|
||||
{
|
||||
// already checked out!?!
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
auto const sleClaimID = std::make_shared<SLE>(claimIDKeylet);
|
||||
|
||||
sleClaimID.newSLE();
|
||||
(*sleClaimID)[sfAccount] = account;
|
||||
(*sleClaimID)[sfXChainBridge] = bridgeSpec;
|
||||
(*sleClaimID)[sfXChainClaimID] = claimID;
|
||||
@@ -2044,21 +2019,13 @@ XChainCreateClaimID::doApply()
|
||||
(*sleClaimID)[sfSignatureReward] = reward;
|
||||
sleClaimID->setFieldArray(sfXChainClaimAttestations, STArray{sfXChainClaimAttestations});
|
||||
|
||||
// Add to owner directory
|
||||
{
|
||||
auto const page = ctx_.view().dirInsert(
|
||||
keylet::ownerDir(account), claimIDKeylet, describeOwnerDir(account));
|
||||
if (!page)
|
||||
return tecDIR_FULL; // LCOV_EXCL_LINE
|
||||
(*sleClaimID)[sfOwnerNode] = *page;
|
||||
}
|
||||
|
||||
increaseOwnerCount(ctx_.view(), sleAcct, {}, 1, ctx_.journal);
|
||||
|
||||
ctx_.view().insert(sleClaimID);
|
||||
ctx_.view().update(sleBridge);
|
||||
ctx_.view().update(sleAcct);
|
||||
// Reserve check (submitter's pre-fee balance) + link into the owner
|
||||
// directory + bump the owner's OwnerCount + insert. See
|
||||
// XChainOwnedClaimIDEntry.
|
||||
if (auto const ter = sleClaimID.create(preFeeBalance_); !isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
sleBridge.update();
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -2199,7 +2166,7 @@ XChainCreateAccountCommit::doApply()
|
||||
if (!sle)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const sleBridge = peekBridge(psb, bridge);
|
||||
auto sleBridge = peekBridge(psb, bridge);
|
||||
if (!sleBridge)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -2227,7 +2194,7 @@ XChainCreateAccountCommit::doApply()
|
||||
return thTer;
|
||||
|
||||
(*sleBridge)[sfXChainAccountCreateCount] = (*sleBridge)[sfXChainAccountCreateCount] + 1;
|
||||
psb.update(sleBridge);
|
||||
sleBridge.update();
|
||||
|
||||
psb.apply(ctx_.rawView());
|
||||
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
@@ -13,7 +14,6 @@
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
#include <cstdint>
|
||||
namespace xrpl {
|
||||
|
||||
NotTEC
|
||||
@@ -25,7 +25,7 @@ CheckCancel::preflight(PreflightContext const& ctx)
|
||||
TER
|
||||
CheckCancel::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
auto const sleCheck = ctx.view.read(keylet::check(ctx.tx[sfCheckID]));
|
||||
CheckEntry<ReadView> const sleCheck{keylet::check(ctx.tx[sfCheckID]), ctx.view};
|
||||
if (!sleCheck)
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Check does not exist.";
|
||||
@@ -54,7 +54,7 @@ CheckCancel::preclaim(PreclaimContext const& ctx)
|
||||
TER
|
||||
CheckCancel::doApply()
|
||||
{
|
||||
auto const sleCheck = view().peek(keylet::check(ctx_.tx[sfCheckID]));
|
||||
CheckEntry<ApplyView> sleCheck{keylet::check(ctx_.tx[sfCheckID]), view()};
|
||||
if (!sleCheck)
|
||||
{
|
||||
// Error should have been caught in preclaim.
|
||||
@@ -62,40 +62,10 @@ CheckCancel::doApply()
|
||||
return tecNO_ENTRY;
|
||||
}
|
||||
|
||||
AccountID const srcId{sleCheck->getAccountID(sfAccount)};
|
||||
AccountID const dstId{sleCheck->getAccountID(sfDestination)};
|
||||
auto viewJ = ctx_.registry.get().getJournal("View");
|
||||
|
||||
// If the check is not written to self (and it shouldn't be), remove the
|
||||
// check from the destination account root.
|
||||
if (srcId != dstId)
|
||||
{
|
||||
std::uint64_t const page{(*sleCheck)[sfDestinationNode]};
|
||||
if (!view().dirRemove(keylet::ownerDir(dstId), page, sleCheck->key(), true))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j_.fatal()) << "Unable to delete check from destination.";
|
||||
return tefBAD_LEDGER;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
}
|
||||
{
|
||||
std::uint64_t const page{(*sleCheck)[sfOwnerNode]};
|
||||
if (!view().dirRemove(keylet::ownerDir(srcId), page, sleCheck->key(), true))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j_.fatal()) << "Unable to delete check from owner.";
|
||||
return tefBAD_LEDGER;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
}
|
||||
|
||||
// If we succeeded, update the check owner's reserve.
|
||||
decreaseOwnerCountForObject(view(), srcId, sleCheck, 1, viewJ);
|
||||
|
||||
// Remove check from ledger.
|
||||
view().erase(sleCheck);
|
||||
return tesSUCCESS;
|
||||
// Unlink the check from the source (and destination) directories, decrement
|
||||
// the source's OwnerCount (refunding any reserve sponsor), and erase it. See
|
||||
// CheckEntry::ownerDirs().
|
||||
return sleCheck.destroy();
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/ledger/helpers/SponsorHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
@@ -82,7 +83,7 @@ CheckCash::preflight(PreflightContext const& ctx)
|
||||
TER
|
||||
CheckCash::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
auto const sleCheck = ctx.view.read(keylet::check(ctx.tx[sfCheckID]));
|
||||
CheckEntry<ReadView> const sleCheck{keylet::check(ctx.tx[sfCheckID]), ctx.view};
|
||||
if (!sleCheck)
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Check does not exist.";
|
||||
@@ -107,8 +108,8 @@ CheckCash::preclaim(PreclaimContext const& ctx)
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
{
|
||||
auto const sleSrc = ctx.view.read(keylet::account(srcId));
|
||||
auto const sleDst = ctx.view.read(keylet::account(dstId));
|
||||
AccountRootEntry<ReadView> const sleSrc{keylet::account(srcId), ctx.view};
|
||||
AccountRootEntry<ReadView> const sleDst{keylet::account(dstId), ctx.view};
|
||||
if (!sleSrc || !sleDst)
|
||||
{
|
||||
// If the check exists this should never occur.
|
||||
@@ -193,10 +194,10 @@ CheckCash::preclaim(PreclaimContext const& ctx)
|
||||
return value.asset().visit(
|
||||
[&](Issue const& issue) -> TER {
|
||||
Currency const currency{issue.currency};
|
||||
auto const sleTrustLine =
|
||||
ctx.view.read(keylet::trustLine(dstId, issuerId, currency));
|
||||
RippleStateEntry<ReadView> const sleTrustLine{
|
||||
keylet::trustLine(dstId, issuerId, currency), ctx.view};
|
||||
|
||||
auto const sleIssuer = ctx.view.read(keylet::account(issuerId));
|
||||
AccountRootEntry<ReadView> const sleIssuer{keylet::account(issuerId), ctx.view};
|
||||
if (!sleIssuer)
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Can't receive IOUs from "
|
||||
@@ -248,7 +249,7 @@ CheckCash::preclaim(PreclaimContext const& ctx)
|
||||
return tesSUCCESS;
|
||||
},
|
||||
[&](MPTIssue const& issue) -> TER {
|
||||
auto const sleIssuer = ctx.view.read(keylet::account(issuerId));
|
||||
AccountRootEntry<ReadView> const sleIssuer{keylet::account(issuerId), ctx.view};
|
||||
if (!sleIssuer)
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Can't receive MPTs from "
|
||||
@@ -576,32 +577,12 @@ CheckCash::doApply()
|
||||
}
|
||||
}
|
||||
|
||||
// Check was cashed. If not a self send (and it shouldn't be), remove
|
||||
// check link from destination directory.
|
||||
if (srcId != accountID_ &&
|
||||
!psb.dirRemove(
|
||||
keylet::ownerDir(accountID_), sleCheck->at(sfDestinationNode), sleCheck->key(), true))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j_.fatal()) << "Unable to delete check from destination.";
|
||||
return tefBAD_LEDGER;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
// Remove check from check owner's directory.
|
||||
if (!psb.dirRemove(keylet::ownerDir(srcId), sleCheck->at(sfOwnerNode), sleCheck->key(), true))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j_.fatal()) << "Unable to delete check from owner.";
|
||||
return tefBAD_LEDGER;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
// If we succeeded, update the check owner's reserve.
|
||||
decreaseOwnerCountForObject(psb, srcId, sleCheck, 1, viewJ);
|
||||
|
||||
// Remove check from ledger.
|
||||
psb.erase(sleCheck);
|
||||
// Check was cashed. Unlink it from the owner (and destination) directories,
|
||||
// decrement the source's OwnerCount (refunding any reserve sponsor), and
|
||||
// erase it. See CheckEntry::ownerDirs().
|
||||
CheckEntry<ApplyView> checkEntry{sleCheck, psb};
|
||||
if (auto const ter = checkEntry.destroy(); !isTesSuccess(ter))
|
||||
return ter; // LCOV_EXCL_LINE
|
||||
|
||||
psb.apply(ctx_.rawView());
|
||||
return tesSUCCESS;
|
||||
|
||||
@@ -3,11 +3,12 @@
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SponsorHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
@@ -26,7 +27,6 @@
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
namespace xrpl {
|
||||
@@ -80,7 +80,7 @@ CheckCreate::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
AccountID const dstId{ctx.tx[sfDestination]};
|
||||
AccountID const srcId{ctx.tx[sfAccount]};
|
||||
auto const sleDst = ctx.view.read(keylet::account(dstId));
|
||||
AccountRootEntry<ReadView> const sleDst{keylet::account(dstId), ctx.view};
|
||||
if (!sleDst)
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Destination account does not exist.";
|
||||
@@ -95,7 +95,7 @@ CheckCreate::preclaim(PreclaimContext const& ctx)
|
||||
// because all writes to pseudo-account discriminator fields **are**
|
||||
// amendment gated, hence the behaviour of this check will always match the
|
||||
// currently active amendments.
|
||||
if (isPseudoAccount(sleDst))
|
||||
if (isPseudoAccount(sleDst.sle()))
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
if (sleDst->isFlag(lsfRequireDestTag) && !ctx.tx.isFieldPresent(sfDestinationTag))
|
||||
@@ -127,8 +127,8 @@ CheckCreate::preclaim(PreclaimContext const& ctx)
|
||||
if (issuerId != srcId)
|
||||
{
|
||||
// Check if the issuer froze the line
|
||||
auto const sleTrust =
|
||||
ctx.view.read(keylet::trustLine(srcId, issuerId, issue.currency));
|
||||
RippleStateEntry<ReadView> const sleTrust{
|
||||
keylet::trustLine(srcId, issuerId, issue.currency), ctx.view};
|
||||
if (sleTrust &&
|
||||
sleTrust->isFlag((issuerId > srcId) ? lsfHighFreeze : lsfLowFreeze))
|
||||
{
|
||||
@@ -139,8 +139,8 @@ CheckCreate::preclaim(PreclaimContext const& ctx)
|
||||
if (issuerId != dstId)
|
||||
{
|
||||
// Check if dst froze the line.
|
||||
auto const sleTrust =
|
||||
ctx.view.read(keylet::trustLine(issuerId, dstId, issue.currency));
|
||||
RippleStateEntry<ReadView> const sleTrust{
|
||||
keylet::trustLine(issuerId, dstId, issue.currency), ctx.view};
|
||||
if (sleTrust &&
|
||||
sleTrust->isFlag((dstId > issuerId) ? lsfHighFreeze : lsfLowFreeze))
|
||||
{
|
||||
@@ -188,22 +188,17 @@ CheckCreate::preclaim(PreclaimContext const& ctx)
|
||||
TER
|
||||
CheckCreate::doApply()
|
||||
{
|
||||
auto const sle = view().peek(keylet::account(accountID_));
|
||||
AccountRootEntry<ApplyView> const sle{keylet::account(accountID_), view()};
|
||||
if (!sle)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
// A check counts against the reserve of the issuing account, but we
|
||||
// check the starting balance because we want to allow dipping into the
|
||||
// reserve to pay fees.
|
||||
if (auto const ret = checkReserve(
|
||||
ctx_.getApplyViewContext(), sle, preFeeBalance_, {.ownerCountDelta = 1}, ctx_.journal);
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
// Note that we use the value from the sequence or ticket as the
|
||||
// Check sequence. For more explanation see comments in SeqProxy.h.
|
||||
std::uint32_t const seq = ctx_.tx.getSeqValue();
|
||||
Keylet const checkKeylet = keylet::check(accountID_, seq);
|
||||
auto sleCheck = std::make_shared<SLE>(checkKeylet);
|
||||
// Build with the ApplyViewContext so create() honors reserve sponsorship.
|
||||
CheckEntry<ApplyView> sleCheck{checkKeylet, ctx_.getApplyViewContext()};
|
||||
sleCheck.newSLE();
|
||||
|
||||
sleCheck->setAccountID(sfAccount, accountID_);
|
||||
AccountID const dstAccountId = ctx_.tx[sfDestination];
|
||||
@@ -219,42 +214,11 @@ CheckCreate::doApply()
|
||||
if (auto const expiry = ctx_.tx[~sfExpiration])
|
||||
sleCheck->setFieldU32(sfExpiration, *expiry);
|
||||
|
||||
view().insert(sleCheck);
|
||||
|
||||
auto viewJ = ctx_.registry.get().getJournal("View");
|
||||
// If it's not a self-send (and it shouldn't be), add Check to the
|
||||
// destination's owner directory.
|
||||
if (dstAccountId != accountID_)
|
||||
{
|
||||
auto const page = view().dirInsert(
|
||||
keylet::ownerDir(dstAccountId), checkKeylet, describeOwnerDir(dstAccountId));
|
||||
|
||||
JLOG(j_.trace()) << "Adding Check to destination directory " << to_string(checkKeylet.key)
|
||||
<< ": " << (page ? "success" : "failure");
|
||||
|
||||
if (!page)
|
||||
return tecDIR_FULL; // LCOV_EXCL_LINE
|
||||
|
||||
sleCheck->setFieldU64(sfDestinationNode, *page);
|
||||
}
|
||||
|
||||
{
|
||||
auto const page = view().dirInsert(
|
||||
keylet::ownerDir(accountID_), checkKeylet, describeOwnerDir(accountID_));
|
||||
|
||||
JLOG(j_.trace()) << "Adding Check to owner directory " << to_string(checkKeylet.key) << ": "
|
||||
<< (page ? "success" : "failure");
|
||||
|
||||
if (!page)
|
||||
return tecDIR_FULL; // LCOV_EXCL_LINE
|
||||
|
||||
sleCheck->setFieldU64(sfOwnerNode, *page);
|
||||
}
|
||||
// If we succeeded, the new entry counts against the creator's reserve.
|
||||
|
||||
increaseOwnerCount(ctx_.getApplyViewContext(), sle, 1, viewJ);
|
||||
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), sleCheck);
|
||||
return tesSUCCESS;
|
||||
// Reserve check (source's pre-fee balance, honoring any reserve sponsor) +
|
||||
// link into the source owner directory and the destination tracking
|
||||
// directory + bump the source's OwnerCount + stamp the reserve sponsor +
|
||||
// insert. See CheckEntry::ownerDirs() and SLEBase::create().
|
||||
return sleCheck.create(preFeeBalance_);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -4,10 +4,8 @@
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/CredentialHelpers.h> // IWYU pragma: keep
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SponsorHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Keylet.h>
|
||||
@@ -23,7 +21,6 @@
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
@@ -106,7 +103,9 @@ CredentialCreate::doApply()
|
||||
auto const credType(ctx_.tx[sfCredentialType]);
|
||||
Keylet const credentialKey = keylet::credential(subject, accountID_, credType);
|
||||
|
||||
auto const sleCred = std::make_shared<SLE>(credentialKey);
|
||||
// Build with the ApplyViewContext so create() honors reserve sponsorship.
|
||||
CredentialEntry<ApplyView> sleCred{credentialKey, ctx_.getApplyViewContext()};
|
||||
sleCred.newSLE();
|
||||
if (!sleCred)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -126,15 +125,6 @@ CredentialCreate::doApply()
|
||||
sleCred->setFieldU32(sfExpiration, *optExp);
|
||||
}
|
||||
|
||||
auto const sleIssuer = view().peek(keylet::account(accountID_));
|
||||
if (!sleIssuer)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
if (auto const ret = checkReserve(
|
||||
ctx_.getApplyViewContext(), sleIssuer, preFeeBalance_, {.ownerCountDelta = 1}, j_);
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
|
||||
sleCred->setAccountID(sfSubject, subject);
|
||||
sleCred->setAccountID(sfIssuer, accountID_);
|
||||
sleCred->setFieldVL(sfCredentialType, credType);
|
||||
@@ -142,39 +132,15 @@ CredentialCreate::doApply()
|
||||
if (ctx_.tx.isFieldPresent(sfURI))
|
||||
sleCred->setFieldVL(sfURI, ctx_.tx.getFieldVL(sfURI));
|
||||
|
||||
{
|
||||
auto const page = view().dirInsert(
|
||||
keylet::ownerDir(accountID_), credentialKey, describeOwnerDir(accountID_));
|
||||
JLOG(j_.trace()) << "Adding Credential to owner directory " << to_string(credentialKey.key)
|
||||
<< ": " << (page ? "success" : "failure");
|
||||
if (!page)
|
||||
return tecDIR_FULL;
|
||||
sleCred->setFieldU64(sfIssuerNode, *page);
|
||||
|
||||
increaseOwnerCount(ctx_.getApplyViewContext(), sleIssuer, 1, j_);
|
||||
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), sleCred);
|
||||
}
|
||||
|
||||
// A self-issued credential is accepted immediately.
|
||||
if (subject == accountID_)
|
||||
{
|
||||
sleCred->setFieldU32(sfFlags, lsfAccepted);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Added to both dirs, owned only by issuer. CredentialAccept will transfer ownership to
|
||||
// subject. CredentialDelete will remove from both dirs and decrement 1 ownerCount.
|
||||
auto const page =
|
||||
view().dirInsert(keylet::ownerDir(subject), credentialKey, describeOwnerDir(subject));
|
||||
JLOG(j_.trace()) << "Adding Credential to subject directory "
|
||||
<< to_string(credentialKey.key) << ": " << (page ? "success" : "failure");
|
||||
if (!page)
|
||||
return tecDIR_FULL;
|
||||
sleCred->setFieldU64(sfSubjectNode, *page);
|
||||
}
|
||||
|
||||
view().insert(sleCred);
|
||||
|
||||
return tesSUCCESS;
|
||||
// Reserve check (issuer's pre-fee balance) + link into the issuer's owner
|
||||
// directory (counted) and, for a third-party subject, the subject's tracking
|
||||
// directory + bump the issuer's OwnerCount + insert. Ownership transfers to
|
||||
// the subject on CredentialAccept. See CredentialEntry::ownerDirs().
|
||||
return sleCred.create(preFeeBalance_);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/helpers/CredentialHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
@@ -80,7 +81,7 @@ CredentialDelete::doApply()
|
||||
auto const issuer = ctx_.tx[~sfIssuer].value_or(accountID_);
|
||||
|
||||
auto const credType(ctx_.tx[sfCredentialType]);
|
||||
auto const sleCred = view().peek(keylet::credential(subject, issuer, credType));
|
||||
CredentialEntry<ApplyView> sleCred{keylet::credential(subject, issuer, credType), view()};
|
||||
if (!sleCred)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -91,7 +92,7 @@ CredentialDelete::doApply()
|
||||
return tecNO_PERMISSION;
|
||||
}
|
||||
|
||||
return deleteSLE(view(), sleCred, j_);
|
||||
return deleteSLE(view(), sleCred.mutableSle(), j_);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
#include <xrpl/tx/transactors/delegate/DelegateSet.h>
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SponsorHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
@@ -72,25 +71,26 @@ DelegateSet::preclaim(PreclaimContext const& ctx)
|
||||
TER
|
||||
DelegateSet::doApply()
|
||||
{
|
||||
auto const sleOwner = ctx_.view().peek(keylet::account(accountID_));
|
||||
AccountRootEntry<ApplyView> const sleOwner{keylet::account(accountID_), ctx_.view()};
|
||||
if (!sleOwner)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const& authAccount = ctx_.tx[sfAuthorize];
|
||||
auto const delegateKey = keylet::delegate(accountID_, authAccount);
|
||||
|
||||
auto sle = ctx_.view().peek(delegateKey);
|
||||
// Build with the ApplyViewContext so create() honors reserve sponsorship.
|
||||
DelegateEntry<ApplyView> sle{delegateKey, ctx_.getApplyViewContext()};
|
||||
if (sle)
|
||||
{
|
||||
auto const& permissions = ctx_.tx.getFieldArray(sfPermissions);
|
||||
if (permissions.empty())
|
||||
{
|
||||
// if permissions array is empty, delete the ledger object.
|
||||
return deleteDelegate(view(), sle, j_);
|
||||
return deleteDelegate(view(), sle.mutableSle(), j_);
|
||||
}
|
||||
|
||||
sle->setFieldArray(sfPermissions, permissions);
|
||||
ctx_.view().update(sle);
|
||||
sle.update();
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -98,45 +98,16 @@ DelegateSet::doApply()
|
||||
if (permissions.empty())
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
if (auto const ret = checkReserve(
|
||||
ctx_.getApplyViewContext(),
|
||||
sleOwner,
|
||||
preFeeBalance_,
|
||||
{.ownerCountDelta = 1},
|
||||
ctx_.journal);
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
|
||||
sle = std::make_shared<SLE>(delegateKey);
|
||||
sle.newSLE();
|
||||
sle->setAccountID(sfAccount, accountID_);
|
||||
sle->setAccountID(sfAuthorize, authAccount);
|
||||
|
||||
sle->setFieldArray(sfPermissions, permissions);
|
||||
|
||||
// Add to delegating account's owner directory
|
||||
auto const page = ctx_.view().dirInsert(
|
||||
keylet::ownerDir(accountID_), delegateKey, describeOwnerDir(accountID_));
|
||||
|
||||
if (!page)
|
||||
return tecDIR_FULL; // LCOV_EXCL_LINE
|
||||
|
||||
(*sle)[sfOwnerNode] = *page;
|
||||
|
||||
// Add to authorized account's owner directory so AccountDelete can find
|
||||
// and clean up inbound delegations when the authorized account is deleted.
|
||||
auto const destPage = ctx_.view().dirInsert(
|
||||
keylet::ownerDir(authAccount), delegateKey, describeOwnerDir(authAccount));
|
||||
|
||||
if (!destPage)
|
||||
return tecDIR_FULL; // LCOV_EXCL_LINE
|
||||
|
||||
(*sle)[sfDestinationNode] = *destPage;
|
||||
|
||||
ctx_.view().insert(sle);
|
||||
increaseOwnerCount(ctx_.getApplyViewContext(), sleOwner, 1, ctx_.journal);
|
||||
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), sle);
|
||||
|
||||
return tesSUCCESS;
|
||||
// Reserve check (delegator's pre-fee balance, honoring any reserve sponsor)
|
||||
// + link into the delegator's owner directory and the authorized account's
|
||||
// directory + bump the delegator's OwnerCount + stamp the reserve sponsor +
|
||||
// insert. See DelegateEntry::ownerDirs() and SLEBase::create().
|
||||
return sle.create(preFeeBalance_);
|
||||
}
|
||||
|
||||
TER
|
||||
@@ -145,40 +116,12 @@ DelegateSet::deleteDelegate(ApplyView& view, SLE::ref sle, beast::Journal j)
|
||||
if (!sle)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const delegator = (*sle)[sfAccount];
|
||||
auto const delegatee = (*sle)[sfAuthorize];
|
||||
|
||||
// Remove from delegating account's owner directory
|
||||
if (!view.dirRemove(keylet::ownerDir(delegator), (*sle)[sfOwnerNode], sle->key(), false))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.fatal()) << "Unable to delete Delegate from owner.";
|
||||
return tefBAD_LEDGER;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
// Remove from authorized account's owner directory, if present
|
||||
if (auto const optPage = (*sle)[~sfDestinationNode])
|
||||
{
|
||||
if (!view.dirRemove(keylet::ownerDir(delegatee), *optPage, sle->key(), false))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.fatal()) << "Unable to delete Delegate from authorized account.";
|
||||
return tefBAD_LEDGER;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
}
|
||||
|
||||
// Only the delegating account's owner count was incremented on creation
|
||||
auto const sleOwner = view.peek(keylet::account(delegator));
|
||||
if (!sleOwner)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
decreaseOwnerCountForObject(view, sleOwner, sle, 1, j);
|
||||
|
||||
view.erase(sle);
|
||||
|
||||
return tesSUCCESS;
|
||||
// Unlink from the delegator's owner directory and the authorized account's
|
||||
// directory, decrement the delegator's OwnerCount (refunding a reserve
|
||||
// sponsor if present), and erase. Only the delegator's count was bumped on
|
||||
// creation. See DelegateEntry::ownerDirs().
|
||||
DelegateEntry<ApplyView> delegate{sle, view, j};
|
||||
return delegate.destroy();
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/Sandbox.h>
|
||||
#include <xrpl/ledger/helpers/AMMHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/AmountConversions.h>
|
||||
@@ -268,8 +270,9 @@ AMMClawback::applyGuts(Sandbox& sb)
|
||||
}
|
||||
}
|
||||
|
||||
AMMEntry<ApplyView> ammEntry{ammSle, sb};
|
||||
auto const res =
|
||||
AMMWithdraw::deleteAMMAccountIfEmpty(sb, ammSle, newLPTokenBalance, asset, asset2, j_);
|
||||
AMMWithdraw::deleteAMMAccountIfEmpty(sb, ammEntry, newLPTokenBalance, asset, asset2, j_);
|
||||
if (!res.second)
|
||||
return res.first; // LCOV_EXCL_LINE
|
||||
|
||||
|
||||
@@ -3,13 +3,14 @@
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/OrderBookDB.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/Sandbox.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/AMMHelpers.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/AMMCore.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
@@ -293,22 +294,24 @@ applyCreate(ApplyContext& ctx, Sandbox& sb, AccountID const& account, beast::Jou
|
||||
auto const lpTokens = ammLPTokens(amount, amount2, lptIss);
|
||||
|
||||
// Create ltAMM
|
||||
auto ammSle = std::make_shared<SLE>(ammKeylet);
|
||||
AMMEntry<ApplyView> ammSle{ammKeylet, sb};
|
||||
ammSle.newSLE();
|
||||
ammSle->setAccountID(sfAccount, accountId);
|
||||
ammSle->setFieldAmount(sfLPTokenBalance, lpTokens);
|
||||
auto const& [asset1, asset2] = std::minmax(amount.asset(), amount2.asset());
|
||||
ammSle->setFieldIssue(sfAsset, STIssue{sfAsset, asset1});
|
||||
ammSle->setFieldIssue(sfAsset2, STIssue{sfAsset2, asset2});
|
||||
// AMM creator gets the auction slot and the voting slot.
|
||||
initializeFeeAuctionVote(ctx.view(), ammSle, account, lptIss, ctx.tx[sfTradingFee]);
|
||||
SLE::pointer ammSlePtr = ammSle.mutableSle();
|
||||
initializeFeeAuctionVote(ctx.view(), ammSlePtr, account, lptIss, ctx.tx[sfTradingFee]);
|
||||
|
||||
// Add owner directory to link the root account and AMM object.
|
||||
if (auto ter = dirLink(sb, accountId, ammSle); ter)
|
||||
// Link the AMM into its pseudo-account's directory and insert it (no reserve
|
||||
// or OwnerCount for a pseudo-account). See AMMEntry.
|
||||
if (auto const ter = ammSle.create(std::nullopt); !isTesSuccess(ter))
|
||||
{
|
||||
JLOG(j.debug()) << "AMM Instance: failed to insert owner dir";
|
||||
return {ter, false};
|
||||
}
|
||||
sb.insert(ammSle);
|
||||
|
||||
// Send LPT to LP.
|
||||
auto res = accountSend(sb, accountId, account, lpTokens, ctx.journal);
|
||||
@@ -360,8 +363,8 @@ applyCreate(ApplyContext& ctx, Sandbox& sb, AccountID const& account, beast::Jou
|
||||
// Set AMM flag on AMM trustline
|
||||
if (!isXRP(amount))
|
||||
{
|
||||
SLE::pointer const sleRippleState =
|
||||
sb.peek(keylet::trustLine(accountId, issue));
|
||||
RippleStateEntry<ApplyView> sleRippleState{
|
||||
keylet::trustLine(accountId, issue), sb};
|
||||
if (!sleRippleState)
|
||||
{
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
@@ -369,7 +372,7 @@ applyCreate(ApplyContext& ctx, Sandbox& sb, AccountID const& account, beast::Jou
|
||||
|
||||
auto const flags = sleRippleState->getFlags();
|
||||
sleRippleState->setFieldU32(sfFlags, flags | lsfAMMNode);
|
||||
sb.update(sleRippleState);
|
||||
sleRippleState.update();
|
||||
}
|
||||
return tesSUCCESS;
|
||||
});
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/AMMCore.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
@@ -447,8 +448,9 @@ AMMWithdraw::applyGuts(Sandbox& sb)
|
||||
}
|
||||
}
|
||||
|
||||
AMMEntry<ApplyView> ammEntry{ammSle, sb};
|
||||
auto const res = deleteAMMAccountIfEmpty(
|
||||
sb, ammSle, newLPTokenBalance, ctx_.tx[sfAsset], ctx_.tx[sfAsset2], j_);
|
||||
sb, ammEntry, newLPTokenBalance, ctx_.tx[sfAsset], ctx_.tx[sfAsset2], j_);
|
||||
// LCOV_EXCL_START
|
||||
if (!res.second)
|
||||
return {res.first, false};
|
||||
@@ -799,7 +801,7 @@ AMMWithdraw::equalWithdrawTokens(
|
||||
std::pair<TER, bool>
|
||||
AMMWithdraw::deleteAMMAccountIfEmpty(
|
||||
Sandbox& sb,
|
||||
SLE::pointer const ammSle,
|
||||
AMMEntry<ApplyView>& ammSle,
|
||||
STAmount const& lpTokenBalance,
|
||||
Asset const& asset1,
|
||||
Asset const& asset2,
|
||||
@@ -819,7 +821,7 @@ AMMWithdraw::deleteAMMAccountIfEmpty(
|
||||
if (updateBalance)
|
||||
{
|
||||
ammSle->setFieldAmount(sfLPTokenBalance, lpTokenBalance);
|
||||
sb.update(ammSle);
|
||||
ammSle.update();
|
||||
}
|
||||
|
||||
return {ter, true};
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
#include <xrpl/tx/transactors/did/DIDDelete.h>
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEBase.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Keylet.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
@@ -27,34 +26,18 @@ DIDDelete::preflight(PreflightContext const& ctx)
|
||||
TER
|
||||
DIDDelete::deleteSLE(ApplyContext& ctx, Keylet sleKeylet, AccountID const owner)
|
||||
{
|
||||
auto const sle = ctx.view().peek(sleKeylet);
|
||||
WritableSLE const sle{sleKeylet, ctx.view()};
|
||||
if (!sle)
|
||||
return tecNO_ENTRY;
|
||||
|
||||
return DIDDelete::deleteSLE(ctx.view(), sle, owner, ctx.journal);
|
||||
return DIDDelete::deleteSLE(ctx.view(), sle.mutableSle(), owner, ctx.journal);
|
||||
}
|
||||
|
||||
TER
|
||||
DIDDelete::deleteSLE(ApplyView& view, SLE::pointer sle, AccountID const owner, beast::Journal j)
|
||||
{
|
||||
// Remove object from owner directory
|
||||
if (!view.dirRemove(keylet::ownerDir(owner), (*sle)[sfOwnerNode], sle->key(), true))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.fatal()) << "Unable to delete DID from owner.";
|
||||
return tefBAD_LEDGER;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
auto const sleOwner = view.peek(keylet::account(owner));
|
||||
if (!sleOwner)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
decreaseOwnerCountForObject(view, sleOwner, sle, 1, j);
|
||||
|
||||
// Remove object from ledger
|
||||
view.erase(sle);
|
||||
return tesSUCCESS;
|
||||
DIDEntry<ApplyView> did{sle, view, j};
|
||||
return did.destroy();
|
||||
}
|
||||
|
||||
TER
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
#include <xrpl/tx/transactors/did/DIDSet.h>
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Keylet.h>
|
||||
@@ -19,7 +18,6 @@
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
@@ -62,46 +60,12 @@ DIDSet::preflight(PreflightContext const& ctx)
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
static TER
|
||||
addSLE(ApplyContext& ctx, SLE::ref sle, AccountID const& owner)
|
||||
{
|
||||
auto const sleAccount = ctx.view().peek(keylet::account(owner));
|
||||
if (!sleAccount)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
// Check reserve availability for new object creation
|
||||
{
|
||||
auto const balance = STAmount((*sleAccount)[sfBalance]).xrp();
|
||||
auto const reserve =
|
||||
accountReserve(ctx.view(), sleAccount, ctx.journal, {.ownerCountDelta = 1});
|
||||
|
||||
if (balance < reserve)
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
}
|
||||
|
||||
// Add ledger object to ledger
|
||||
ctx.view().insert(sle);
|
||||
|
||||
// Add ledger object to owner's page
|
||||
{
|
||||
auto page =
|
||||
ctx.view().dirInsert(keylet::ownerDir(owner), sle->key(), describeOwnerDir(owner));
|
||||
if (!page)
|
||||
return tecDIR_FULL; // LCOV_EXCL_LINE
|
||||
(*sle)[sfOwnerNode] = *page;
|
||||
}
|
||||
increaseOwnerCount(ctx.view(), sleAccount, {}, 1, ctx.journal);
|
||||
ctx.view().update(sleAccount);
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
DIDSet::doApply()
|
||||
{
|
||||
// Edit ledger object if it already exists
|
||||
Keylet const didKeylet = keylet::did(accountID_);
|
||||
if (auto const sleDID = ctx_.view().peek(didKeylet))
|
||||
DIDEntry<ApplyView> sleDID{accountID_, ctx_.view()};
|
||||
if (sleDID.exists())
|
||||
{
|
||||
auto update = [&](auto const& sField) {
|
||||
if (auto const field = ctx_.tx[~sField])
|
||||
@@ -125,12 +89,12 @@ DIDSet::doApply()
|
||||
{
|
||||
return tecEMPTY_DID;
|
||||
}
|
||||
ctx_.view().update(sleDID);
|
||||
sleDID.update();
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
// Create new ledger object otherwise
|
||||
auto const sleDID = std::make_shared<SLE>(didKeylet);
|
||||
sleDID.newSLE();
|
||||
(*sleDID)[sfAccount] = accountID_;
|
||||
|
||||
auto set = [&](auto const& sField) {
|
||||
@@ -147,7 +111,7 @@ DIDSet::doApply()
|
||||
return tecEMPTY_DID;
|
||||
}
|
||||
|
||||
return addSLE(ctx_, sleDID, accountID_);
|
||||
return sleDID.create(preFeeBalance_);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/EscrowHelpers.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Concepts.h>
|
||||
@@ -73,7 +75,7 @@ escrowCancelPreclaimHelper<MPTIssue>(
|
||||
|
||||
// If the mpt does not exist, return tecOBJECT_NOT_FOUND
|
||||
auto const issuanceKey = keylet::mptokenIssuance(amount.get<MPTIssue>().getMptID());
|
||||
auto const sleIssuance = ctx.view.read(issuanceKey);
|
||||
MPTokenIssuanceEntry<ReadView> const sleIssuance{issuanceKey, ctx.view};
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
@@ -93,7 +95,7 @@ EscrowCancel::preclaim(PreclaimContext const& ctx)
|
||||
if (ctx.view.rules().enabled(featureTokenEscrow))
|
||||
{
|
||||
auto const k = keylet::escrow(ctx.tx[sfOwner], ctx.tx[sfOfferSequence]);
|
||||
auto const slep = ctx.view.read(k);
|
||||
EscrowEntry<ReadView> const slep{k, ctx.view};
|
||||
if (!slep)
|
||||
return tecNO_TARGET;
|
||||
|
||||
@@ -118,7 +120,7 @@ TER
|
||||
EscrowCancel::doApply()
|
||||
{
|
||||
auto const k = keylet::escrow(ctx_.tx[sfOwner], ctx_.tx[sfOfferSequence]);
|
||||
auto const slep = ctx_.view().peek(k);
|
||||
EscrowEntry<ApplyView> slep{k, ctx_.view()};
|
||||
if (!slep)
|
||||
{
|
||||
if (ctx_.view().rules().enabled(featureTokenEscrow))
|
||||
@@ -139,6 +141,9 @@ EscrowCancel::doApply()
|
||||
|
||||
AccountID const account = (*slep)[sfAccount];
|
||||
|
||||
// Escrow deletion is inlined here (rather than EscrowEntry::destroy()) so the
|
||||
// directory removals stay ordered before token delivery, matching develop
|
||||
// for consensus.
|
||||
// Remove escrow from owner directory
|
||||
{
|
||||
auto const page = (*slep)[sfOwnerNode];
|
||||
@@ -163,7 +168,7 @@ EscrowCancel::doApply()
|
||||
}
|
||||
}
|
||||
|
||||
auto const sle = ctx_.view().peek(keylet::account(account));
|
||||
AccountRootEntry<ApplyView> sle{keylet::account(account), ctx_.view()};
|
||||
STAmount const amount = slep->getFieldAmount(sfAmount);
|
||||
|
||||
// Transfer amount back to the owner
|
||||
@@ -183,7 +188,8 @@ EscrowCancel::doApply()
|
||||
return escrowUnlockApplyHelper<T>(
|
||||
ctx_.getApplyViewContext(),
|
||||
kParityRate,
|
||||
ctx_.view().rules().enabled(fixCleanup3_2_0) ? sle : slep,
|
||||
ctx_.view().rules().enabled(fixCleanup3_2_0) ? sle.mutableSle()
|
||||
: slep.mutableSle(),
|
||||
preFeeBalance_,
|
||||
amount,
|
||||
issuer,
|
||||
@@ -196,24 +202,25 @@ EscrowCancel::doApply()
|
||||
!isTesSuccess(ret))
|
||||
return ret; // LCOV_EXCL_LINE
|
||||
|
||||
// Remove escrow from issuers owner directory, if present.
|
||||
// Remove escrow from issuer's owner directory, if present.
|
||||
if (auto const optPage = (*slep)[~sfIssuerNode]; optPage)
|
||||
{
|
||||
if (!ctx_.view().dirRemove(keylet::ownerDir(issuer), *optPage, k.key, true))
|
||||
if (!ctx_.view().dirRemove(keylet::ownerDir(amount.getIssuer()), *optPage, k.key, true))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
|
||||
JLOG(j_.fatal()) << "Unable to delete Escrow from issuer.";
|
||||
return tefBAD_LEDGER;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
decreaseOwnerCountForObject(ctx_.view(), sle, slep, 1, ctx_.journal);
|
||||
|
||||
// Remove escrow from ledger
|
||||
ctx_.view().erase(slep);
|
||||
// Decrement the owner's OwnerCount (refunding any reserve sponsor); this
|
||||
// also persists the balance credit above.
|
||||
decreaseOwnerCountForObject(ctx_.view(), sle.mutableSle(), slep.mutableSle(), 1, ctx_.journal);
|
||||
|
||||
// Remove escrow from ledger.
|
||||
slep.erase();
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,11 +6,12 @@
|
||||
#include <xrpl/conditions/Condition.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/ledger/helpers/SponsorHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
@@ -33,7 +34,6 @@
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
#include <xrpl/tx/applySteps.h>
|
||||
|
||||
#include <memory>
|
||||
#include <system_error>
|
||||
#include <variant>
|
||||
|
||||
@@ -202,14 +202,15 @@ escrowCreatePreclaimHelper<Issue>(
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
// If the lsfAllowTrustLineLocking is not enabled, return tecNO_PERMISSION
|
||||
auto const sleIssuer = ctx.view.read(keylet::account(issuer));
|
||||
AccountRootEntry<ReadView> const sleIssuer{keylet::account(issuer), ctx.view};
|
||||
if (!sleIssuer)
|
||||
return tecNO_ISSUER;
|
||||
if (!sleIssuer->isFlag(lsfAllowTrustLineLocking))
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
// If the account does not have a trustline to the issuer, return tecNO_LINE
|
||||
auto const sleRippleState = ctx.view.read(keylet::trustLine(account, issuer, issue.currency));
|
||||
RippleStateEntry<ReadView> const sleRippleState{
|
||||
keylet::trustLine(account, issuer, issue.currency), ctx.view};
|
||||
if (!sleRippleState)
|
||||
return tecNO_LINE;
|
||||
|
||||
@@ -273,7 +274,7 @@ escrowCreatePreclaimHelper<MPTIssue>(
|
||||
|
||||
// If the mpt does not exist, return tecOBJECT_NOT_FOUND
|
||||
auto const issuanceKey = keylet::mptokenIssuance(amount.get<MPTIssue>().getMptID());
|
||||
auto const sleIssuance = ctx.view.read(issuanceKey);
|
||||
MPTokenIssuanceEntry<ReadView> const sleIssuance{issuanceKey, ctx.view};
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
@@ -342,7 +343,7 @@ EscrowCreate::preclaim(PreclaimContext const& ctx)
|
||||
AccountID const account{ctx.tx[sfAccount]};
|
||||
AccountID const dest{ctx.tx[sfDestination]};
|
||||
|
||||
auto const sled = ctx.view.read(keylet::account(dest));
|
||||
AccountRootEntry<ReadView> const sled{keylet::account(dest), ctx.view};
|
||||
if (!sled)
|
||||
return tecNO_DST;
|
||||
|
||||
@@ -350,7 +351,7 @@ EscrowCreate::preclaim(PreclaimContext const& ctx)
|
||||
// because all writes to pseudo-account discriminator fields **are**
|
||||
// amendment gated, hence the behaviour of this check will always match the
|
||||
// currently active amendments.
|
||||
if (isPseudoAccount(sled))
|
||||
if (isPseudoAccount(sled.sle()))
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
if (!isXRP(amount))
|
||||
@@ -428,7 +429,7 @@ EscrowCreate::doApply()
|
||||
if (ctx_.tx[~sfFinishAfter] && after(closeTime, ctx_.tx[sfFinishAfter]))
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
auto const sle = ctx_.view().peek(keylet::account(accountID_));
|
||||
AccountRootEntry<ApplyView> sle{keylet::account(accountID_), ctx_.view()};
|
||||
if (!sle)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -441,8 +442,8 @@ EscrowCreate::doApply()
|
||||
// validates the sponsor's reserve + remaining credit. When
|
||||
// unsponsored this hits the source branch and validates the
|
||||
// source's pre-lock balance against base + (currentOC+1)*increment.
|
||||
if (auto const ret =
|
||||
checkReserve(ctx_.getApplyViewContext(), sle, balance, {.ownerCountDelta = 1}, j_);
|
||||
if (auto const ret = checkReserve(
|
||||
ctx_.getApplyViewContext(), sle.sle(), balance, {.ownerCountDelta = 1}, j_);
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
|
||||
@@ -460,14 +461,17 @@ EscrowCreate::doApply()
|
||||
// source only owes reserve for its current owners.
|
||||
// - unsponsored: 1 — source owes reserve including the new increment.
|
||||
auto const sourceReserve = accountReserve(
|
||||
ctx_.view(), sle, j_, {.ownerCountDelta = getTxReserveSponsorID(ctx_.tx) ? 0 : 1});
|
||||
ctx_.view(),
|
||||
sle.sle(),
|
||||
j_,
|
||||
{.ownerCountDelta = getTxReserveSponsorID(ctx_.tx) ? 0 : 1});
|
||||
if (balance - STAmount(amount).xrp() < sourceReserve)
|
||||
return tecUNFUNDED;
|
||||
}
|
||||
|
||||
// Check destination account
|
||||
{
|
||||
auto const sled = ctx_.view().read(keylet::account(ctx_.tx[sfDestination]));
|
||||
AccountRootEntry<ReadView> const sled{keylet::account(ctx_.tx[sfDestination]), ctx_.view()};
|
||||
if (!sled)
|
||||
return tecNO_DST; // LCOV_EXCL_LINE
|
||||
if (sled->isFlag(lsfRequireDestTag) && !ctx_.tx[~sfDestinationTag])
|
||||
@@ -477,7 +481,9 @@ EscrowCreate::doApply()
|
||||
// Create escrow in ledger. Note that we use the value from the
|
||||
// sequence or ticket. For more explanation see comments in SeqProxy.h.
|
||||
Keylet const escrowKeylet = keylet::escrow(accountID_, ctx_.tx.getSeqValue());
|
||||
auto const slep = std::make_shared<SLE>(escrowKeylet);
|
||||
// Build with the ApplyViewContext so create() honors reserve sponsorship.
|
||||
EscrowEntry<ApplyView> slep{escrowKeylet, ctx_.getApplyViewContext()};
|
||||
slep.newSLE();
|
||||
(*slep)[sfAmount] = amount;
|
||||
(*slep)[sfAccount] = accountID_;
|
||||
(*slep)[~sfCondition] = ctx_.tx[~sfCondition];
|
||||
@@ -499,45 +505,12 @@ EscrowCreate::doApply()
|
||||
(*slep)[sfTransferRate] = xferRate.value;
|
||||
}
|
||||
|
||||
ctx_.view().insert(slep);
|
||||
|
||||
// Add escrow to sender's owner directory
|
||||
{
|
||||
auto page = ctx_.view().dirInsert(
|
||||
keylet::ownerDir(accountID_), escrowKeylet, describeOwnerDir(accountID_));
|
||||
if (!page)
|
||||
return tecDIR_FULL; // LCOV_EXCL_LINE
|
||||
(*slep)[sfOwnerNode] = *page;
|
||||
}
|
||||
|
||||
// If it's not a self-send, add escrow to recipient's owner directory.
|
||||
AccountID const dest = ctx_.tx[sfDestination];
|
||||
if (dest != accountID_)
|
||||
{
|
||||
auto page =
|
||||
ctx_.view().dirInsert(keylet::ownerDir(dest), escrowKeylet, describeOwnerDir(dest));
|
||||
if (!page)
|
||||
return tecDIR_FULL; // LCOV_EXCL_LINE
|
||||
(*slep)[sfDestinationNode] = *page;
|
||||
}
|
||||
|
||||
// IOU escrow objects are added to the issuer's owner directory to help
|
||||
// track the total locked balance. For MPT, this isn't necessary because the
|
||||
// locked balance is already stored directly in the MPTokenIssuance object.
|
||||
// Deduct/lock the escrowed amount from the sender.
|
||||
AccountID const issuer = amount.getIssuer();
|
||||
if (!isXRP(amount) && issuer != accountID_ && issuer != dest && !amount.holds<MPTIssue>())
|
||||
{
|
||||
auto page =
|
||||
ctx_.view().dirInsert(keylet::ownerDir(issuer), escrowKeylet, describeOwnerDir(issuer));
|
||||
if (!page)
|
||||
return tecDIR_FULL; // LCOV_EXCL_LINE
|
||||
(*slep)[sfIssuerNode] = *page;
|
||||
}
|
||||
|
||||
// Deduct owner's balance
|
||||
if (isXRP(amount))
|
||||
{
|
||||
(*sle)[sfBalance] = (*sle)[sfBalance] - amount;
|
||||
sle.update();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -552,11 +525,11 @@ EscrowCreate::doApply()
|
||||
}
|
||||
}
|
||||
|
||||
// increment owner count
|
||||
increaseOwnerCount(ctx_.getApplyViewContext(), sle, 1, ctx_.journal);
|
||||
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), slep);
|
||||
ctx_.view().update(sle);
|
||||
return tesSUCCESS;
|
||||
// Link the escrow into the sender's owner directory (counts toward reserve),
|
||||
// plus the destination and (for IOU) issuer tracking directories where
|
||||
// applicable, bump the sender's OwnerCount, stamp any reserve sponsor, and
|
||||
// insert. See EscrowEntry::ownerDirs() and SLEBase::create().
|
||||
return slep.create(preFeeBalance_);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <xrpl/ledger/helpers/EscrowHelpers.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Concepts.h>
|
||||
@@ -173,7 +174,7 @@ escrowFinishPreclaimHelper<MPTIssue>(
|
||||
|
||||
// If the mpt does not exist, return tecOBJECT_NOT_FOUND
|
||||
auto const issuanceKey = keylet::mptokenIssuance(amount.get<MPTIssue>().getMptID());
|
||||
auto const sleIssuance = ctx.view.read(issuanceKey);
|
||||
MPTokenIssuanceEntry<ReadView> const sleIssuance{issuanceKey, ctx.view};
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
@@ -204,7 +205,7 @@ EscrowFinish::preclaim(PreclaimContext const& ctx)
|
||||
if (ctx.view.rules().enabled(featureTokenEscrow))
|
||||
{
|
||||
auto const k = keylet::escrow(ctx.tx[sfOwner], ctx.tx[sfOfferSequence]);
|
||||
auto const slep = ctx.view.read(k);
|
||||
EscrowEntry<ReadView> const slep{k, ctx.view};
|
||||
if (!slep)
|
||||
return tecNO_TARGET;
|
||||
|
||||
@@ -229,7 +230,7 @@ TER
|
||||
EscrowFinish::doApply()
|
||||
{
|
||||
auto const k = keylet::escrow(ctx_.tx[sfOwner], ctx_.tx[sfOfferSequence]);
|
||||
auto const slep = ctx_.view().peek(k);
|
||||
EscrowEntry<ApplyView> slep{k, ctx_.view()};
|
||||
if (!slep)
|
||||
{
|
||||
if (ctx_.view().rules().enabled(featureTokenEscrow))
|
||||
@@ -305,17 +306,21 @@ EscrowFinish::doApply()
|
||||
|
||||
// NOTE: Escrow payments cannot be used to fund accounts.
|
||||
AccountID const destID = (*slep)[sfDestination];
|
||||
auto const sled = ctx_.view().peek(keylet::account(destID));
|
||||
AccountRootEntry<ApplyView> sled{keylet::account(destID), ctx_.view()};
|
||||
if (!sled)
|
||||
return tecNO_DST;
|
||||
|
||||
if (auto err =
|
||||
verifyDepositPreauth(ctx_.tx, ctx_.view(), accountID_, destID, sled, ctx_.journal);
|
||||
if (auto err = verifyDepositPreauth(
|
||||
ctx_.tx, ctx_.view(), accountID_, destID, sled.sle(), ctx_.journal);
|
||||
!isTesSuccess(err))
|
||||
return err;
|
||||
|
||||
AccountID const account = (*slep)[sfAccount];
|
||||
|
||||
// Escrow deletion is not expressed via EscrowEntry::destroy() here because
|
||||
// the reserve release is consensus-ordered relative to token delivery (see
|
||||
// below), which the wrapper cannot express.
|
||||
//
|
||||
// Remove escrow from owner directory
|
||||
{
|
||||
auto const page = (*slep)[sfOwnerNode];
|
||||
@@ -348,7 +353,7 @@ EscrowFinish::doApply()
|
||||
// arithmetic for self-escrows and would break consensus if not gated.
|
||||
bool const sponsorEnabled = ctx_.view().rules().enabled(featureSponsor);
|
||||
if (sponsorEnabled)
|
||||
decreaseOwnerCountForObject(ctx_.view(), account, slep, 1, ctx_.journal);
|
||||
decreaseOwnerCountForObject(ctx_.view(), account, slep.mutableSle(), 1, ctx_.journal);
|
||||
|
||||
STAmount const amount = slep->getFieldAmount(sfAmount);
|
||||
// Transfer amount to destination
|
||||
@@ -371,7 +376,7 @@ EscrowFinish::doApply()
|
||||
return escrowUnlockApplyHelper<T>(
|
||||
ctx_.getApplyViewContext(),
|
||||
lockedRate,
|
||||
sled,
|
||||
sled.mutableSle(),
|
||||
preFeeBalance_,
|
||||
amount,
|
||||
issuer,
|
||||
@@ -384,27 +389,27 @@ EscrowFinish::doApply()
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
|
||||
// Remove escrow from issuers owner directory, if present.
|
||||
// Remove escrow from issuer's owner directory, if present.
|
||||
if (auto const optPage = (*slep)[~sfIssuerNode]; optPage)
|
||||
{
|
||||
if (!ctx_.view().dirRemove(keylet::ownerDir(issuer), *optPage, k.key, true))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
|
||||
JLOG(j_.fatal()) << "Unable to delete Escrow from issuer.";
|
||||
return tefBAD_LEDGER;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx_.view().update(sled);
|
||||
sled.update();
|
||||
|
||||
// Adjust source owner count (legacy position, pre-Sponsor)
|
||||
// Adjust source owner count (legacy position, pre-Sponsor).
|
||||
if (!sponsorEnabled)
|
||||
decreaseOwnerCountForObject(ctx_.view(), account, slep, 1, ctx_.journal);
|
||||
decreaseOwnerCountForObject(ctx_.view(), account, slep.mutableSle(), 1, ctx_.journal);
|
||||
|
||||
// Remove escrow from ledger
|
||||
ctx_.view().erase(slep);
|
||||
slep.erase();
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/LendingHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
@@ -161,7 +162,7 @@ determineClawAmount(
|
||||
SLE const& sleBroker,
|
||||
Asset const& vaultAsset,
|
||||
std::optional<STAmount> const& amount,
|
||||
SLE::const_ref vaultSle,
|
||||
VaultEntry<ReadView> const& vaultSle,
|
||||
Rules const& rules)
|
||||
{
|
||||
auto const maxClawAmount = [&]() {
|
||||
@@ -293,8 +294,8 @@ LoanBrokerCoverClawback::preclaim(PreclaimContext const& ctx)
|
||||
}
|
||||
}
|
||||
|
||||
auto const findClawAmount =
|
||||
determineClawAmount(*sleBroker, vaultAsset, amount, vault, ctx.view.rules());
|
||||
auto const findClawAmount = determineClawAmount(
|
||||
*sleBroker, vaultAsset, amount, VaultEntry<ReadView>{vault, ctx.view}, ctx.view.rules());
|
||||
if (!findClawAmount)
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "LoanBroker cover is already at minimum.";
|
||||
@@ -303,7 +304,10 @@ LoanBrokerCoverClawback::preclaim(PreclaimContext const& ctx)
|
||||
STAmount const& clawAmount = *findClawAmount;
|
||||
|
||||
if (auto const ret = canApplyToBrokerCover(
|
||||
ctx.view, sleBroker, vaultAsset, clawAmount, ctx.j, "LoanBrokerCoverClawback"))
|
||||
LoanBrokerEntry<ReadView>{sleBroker, ctx.view, ctx.j},
|
||||
vaultAsset,
|
||||
clawAmount,
|
||||
"LoanBrokerCoverClawback"))
|
||||
return ret;
|
||||
|
||||
// Explicitly check the balance of the trust line / MPT to make sure the
|
||||
@@ -356,8 +360,8 @@ LoanBrokerCoverClawback::doApply()
|
||||
|
||||
auto const vaultAsset = vault->at(sfAsset);
|
||||
|
||||
auto const findClawAmount =
|
||||
determineClawAmount(*sleBroker, vaultAsset, amount, vault, view().rules());
|
||||
auto const findClawAmount = determineClawAmount(
|
||||
*sleBroker, vaultAsset, amount, VaultEntry<ReadView>{vault, view()}, view().rules());
|
||||
if (!findClawAmount)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
STAmount const& clawAmount = *findClawAmount;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/LendingHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
@@ -96,7 +97,10 @@ LoanBrokerCoverWithdraw::preclaim(PreclaimContext const& ctx)
|
||||
|
||||
// Helper handles both IOU and MPT correctly without explicit branching.
|
||||
if (auto const ret = canApplyToBrokerCover(
|
||||
ctx.view, sleBroker, vaultAsset, amount, ctx.j, "LoanBrokerCoverWithdraw"))
|
||||
LoanBrokerEntry<ReadView>{sleBroker, ctx.view, ctx.j},
|
||||
vaultAsset,
|
||||
amount,
|
||||
"LoanBrokerCoverWithdraw"))
|
||||
return ret;
|
||||
|
||||
// The broker's pseudo-account is the source of funds.
|
||||
@@ -152,7 +156,9 @@ LoanBrokerCoverWithdraw::preclaim(PreclaimContext const& ctx)
|
||||
if (fix320Enabled)
|
||||
{
|
||||
return minimumBrokerCover(
|
||||
currentDebtTotal, TenthBips32{sleBroker->at(sfCoverRateMinimum)}, vault);
|
||||
currentDebtTotal,
|
||||
TenthBips32{sleBroker->at(sfCoverRateMinimum)},
|
||||
VaultEntry<ReadView>{vault, ctx.view});
|
||||
}
|
||||
|
||||
// Always round the minimum required up.
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/helpers/LendingHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
@@ -78,7 +79,7 @@ LoanBrokerDelete::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
// Any remaining debt should have been wiped out by the last Loan
|
||||
// Delete. This check is purely defensive.
|
||||
auto const scale = getAssetsTotalScale(vault);
|
||||
auto const scale = getAssetsTotalScale(VaultEntry<ReadView>{vault, ctx.view});
|
||||
|
||||
auto const rounded =
|
||||
roundToAsset(asset, debtTotal, scale, Number::RoundingMode::TowardsZero);
|
||||
@@ -129,29 +130,17 @@ LoanBrokerDelete::doApply()
|
||||
auto const brokerID = tx[sfLoanBrokerID];
|
||||
|
||||
// Delete the loan broker
|
||||
auto broker = view().peek(keylet::loanBroker(brokerID));
|
||||
LoanBrokerEntry<ApplyView> broker{keylet::loanBroker(brokerID), view(), j_};
|
||||
if (!broker)
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
auto const vaultID = broker->at(sfVaultID);
|
||||
auto const sleVault = view().read(keylet::vault(vaultID));
|
||||
if (!sleVault)
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
auto const vaultPseudoID = sleVault->at(sfAccount);
|
||||
auto const vaultAsset = sleVault->at(sfAsset);
|
||||
|
||||
auto const brokerPseudoID = broker->at(sfAccount);
|
||||
|
||||
if (!view().dirRemove(
|
||||
keylet::ownerDir(accountID_), broker->at(sfOwnerNode), broker->key(), false))
|
||||
{
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
}
|
||||
if (!view().dirRemove(
|
||||
keylet::ownerDir(vaultPseudoID), broker->at(sfVaultNode), broker->key(), false))
|
||||
{
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
{
|
||||
auto const coverAvailable = STAmount{vaultAsset, broker->at(sfCoverAvailable)};
|
||||
if (auto const ter = accountSend(
|
||||
@@ -186,17 +175,11 @@ LoanBrokerDelete::doApply()
|
||||
|
||||
view().erase(brokerPseudoSLE);
|
||||
|
||||
{
|
||||
auto owner = view().peek(keylet::account(accountID_));
|
||||
if (!owner)
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
|
||||
// Decreases the owner count by two: one for the LoanBroker object, and
|
||||
// one for the pseudo-account.
|
||||
decreaseOwnerCountForObject(view(), owner, broker, 2, j_);
|
||||
}
|
||||
|
||||
view().erase(broker);
|
||||
// Unlink the broker from its owner's directory and the vault pseudo-account's
|
||||
// directory, decrement the owner's OwnerCount by 2 (broker + pseudo-account,
|
||||
// refunding any reserve sponsor), and erase it. See LoanBrokerEntry.
|
||||
if (auto const ter = broker.destroy(); !isTesSuccess(ter))
|
||||
return ter; // LCOV_EXCL_LINE
|
||||
|
||||
associateAsset(*broker, vaultAsset);
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/LendingHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
@@ -216,7 +216,6 @@ LoanBrokerSet::doApply()
|
||||
return tefBAD_LEDGER;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
auto const vaultPseudoID = sleVault->at(sfAccount);
|
||||
auto const vaultAsset = sleVault->at(sfAsset);
|
||||
auto const sequence = tx.getSeqValue();
|
||||
|
||||
@@ -229,18 +228,13 @@ LoanBrokerSet::doApply()
|
||||
return tefBAD_LEDGER;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
auto broker = std::make_shared<SLE>(keylet::loanBroker(accountID_, sequence));
|
||||
LoanBrokerEntry<ApplyView> broker{keylet::loanBroker(accountID_, sequence), view, j_};
|
||||
broker.newSLE();
|
||||
|
||||
if (auto const ter = dirLink(view, accountID_, broker))
|
||||
return ter; // LCOV_EXCL_LINE
|
||||
if (auto const ter = dirLink(view, vaultPseudoID, broker, sfVaultNode))
|
||||
return ter; // LCOV_EXCL_LINE
|
||||
|
||||
// Increases the owner count by two: one for the LoanBroker object, and
|
||||
// one for the pseudo-account.
|
||||
increaseOwnerCount(view, owner, {}, 2, j_);
|
||||
if (preFeeBalance_ < accountReserve(view, owner, j_))
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
// sfVaultID and sfOwner must be set before create(), which reads them
|
||||
// (and looks up the vault's pseudo-account) to link the directories.
|
||||
broker->at(sfVaultID) = vaultID;
|
||||
broker->at(sfOwner) = accountID_;
|
||||
|
||||
auto maybePseudo = createPseudoAccount(view, broker->key(), sfLoanBrokerID);
|
||||
if (!maybePseudo)
|
||||
@@ -254,8 +248,6 @@ LoanBrokerSet::doApply()
|
||||
|
||||
// Initialize data fields:
|
||||
broker->at(sfSequence) = sequence;
|
||||
broker->at(sfVaultID) = vaultID;
|
||||
broker->at(sfOwner) = accountID_;
|
||||
broker->at(sfAccount) = pseudoId;
|
||||
// The LoanSequence indexes loans created by this broker, starting at 1
|
||||
broker->at(sfLoanSequence) = 1;
|
||||
@@ -270,7 +262,12 @@ LoanBrokerSet::doApply()
|
||||
if (auto const coverLiq = tx[~sfCoverRateLiquidation])
|
||||
broker->at(sfCoverRateLiquidation) = *coverLiq;
|
||||
|
||||
view.insert(broker);
|
||||
// Reserve check (owner's pre-fee balance, charging two slots for the
|
||||
// broker and its pseudo-account) + link into the owner's directory
|
||||
// (counted) and the vault pseudo-account's directory (tracking) + bump
|
||||
// the owner's OwnerCount by 2 + insert. See LoanBrokerEntry.
|
||||
if (auto const ter = broker.create(preFeeBalance_); !isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
associateAsset(*broker, vaultAsset);
|
||||
}
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
#include <xrpl/basics/Number.h> // IWYU pragma: keep
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/LendingHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STAmount.h> // IWYU pragma: keep
|
||||
@@ -76,7 +78,7 @@ LoanDelete::doApply()
|
||||
auto& view = ctx_.view();
|
||||
|
||||
auto const loanID = tx[sfLoanID];
|
||||
auto const loanSle = view.peek(keylet::loan(loanID));
|
||||
LoanEntry<ApplyView> loanSle{keylet::loan(loanID), view, j_};
|
||||
if (!loanSle)
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
auto const borrower = loanSle->at(sfBorrower);
|
||||
@@ -88,23 +90,16 @@ LoanDelete::doApply()
|
||||
auto const brokerSle = view.peek(keylet::loanBroker(brokerID));
|
||||
if (!brokerSle)
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
auto const brokerPseudoAccount = brokerSle->at(sfAccount);
|
||||
|
||||
auto const vaultSle = view.peek(keylet::vault(brokerSle->at(sfVaultID)));
|
||||
if (!vaultSle)
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
auto const vaultAsset = vaultSle->at(sfAsset);
|
||||
|
||||
// Remove LoanID from Directory of the LoanBroker pseudo-account.
|
||||
if (!view.dirRemove(
|
||||
keylet::ownerDir(brokerPseudoAccount), loanSle->at(sfLoanBrokerNode), loanID, false))
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
// Remove LoanID from Directory of the Borrower.
|
||||
if (!view.dirRemove(keylet::ownerDir(borrower), loanSle->at(sfOwnerNode), loanID, false))
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
|
||||
// Delete the Loan object
|
||||
view.erase(loanSle);
|
||||
// Unlink the loan from the LoanBroker pseudo-account's directory and the
|
||||
// borrower's directory, and erase it. See LoanEntry::destroy().
|
||||
if (auto const ter = loanSle.destroy(); !isTesSuccess(ter))
|
||||
return ter; // LCOV_EXCL_LINE
|
||||
|
||||
// Decrement the LoanBroker's owner count.
|
||||
adjustLoanBrokerOwnerCount(view, brokerSle, -1, j_);
|
||||
@@ -120,7 +115,7 @@ LoanDelete::doApply()
|
||||
roundToAsset(
|
||||
vaultSle->at(sfAsset),
|
||||
debtTotalProxy,
|
||||
getAssetsTotalScale(vaultSle),
|
||||
getAssetsTotalScale(VaultEntry<ReadView>{vaultSle, view}),
|
||||
Number::RoundingMode::TowardsZero) == beast::kZero,
|
||||
"xrpl::LoanDelete::doApply",
|
||||
"last loan, remaining debt rounds to zero");
|
||||
@@ -128,7 +123,7 @@ LoanDelete::doApply()
|
||||
}
|
||||
}
|
||||
// Decrement the borrower's owner count
|
||||
decreaseOwnerCountForObject(view, borrowerSle, loanSle, 1, j_);
|
||||
decreaseOwnerCountForObject(view, borrowerSle, loanSle.mutableSle(), 1, j_);
|
||||
|
||||
// These associations shouldn't do anything, but do them just to be safe
|
||||
associateAsset(*loanSle, vaultAsset);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/LendingHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
@@ -128,7 +129,7 @@ LoanManage::preclaim(PreclaimContext const& ctx)
|
||||
}
|
||||
|
||||
static Number
|
||||
owedToVault(SLE::ref loanSle)
|
||||
owedToVault(LoanEntry<ApplyView> const& loanSle)
|
||||
{
|
||||
// Spec section 3.2.3.2, defines the default amount as
|
||||
//
|
||||
@@ -146,13 +147,14 @@ owedToVault(SLE::ref loanSle)
|
||||
|
||||
TER
|
||||
LoanManage::defaultLoan(
|
||||
ApplyView& view,
|
||||
SLE::ref loanSle,
|
||||
SLE::ref brokerSle,
|
||||
SLE::ref vaultSle,
|
||||
Asset const& vaultAsset,
|
||||
beast::Journal j)
|
||||
LoanEntry<ApplyView>& loanSle,
|
||||
LoanBrokerEntry<ApplyView>& brokerSle,
|
||||
VaultEntry<ApplyView>& vaultSle,
|
||||
Asset const& vaultAsset)
|
||||
{
|
||||
ApplyView& view = loanSle.applyView();
|
||||
beast::Journal const j = loanSle.journal();
|
||||
|
||||
// Calculate the amount of the Default that First-Loss Capital covers:
|
||||
|
||||
std::int32_t const loanScale = loanSle->at(sfLoanScale);
|
||||
@@ -251,7 +253,7 @@ LoanManage::defaultLoan(
|
||||
adjustImpreciseNumber(
|
||||
vaultLossUnrealizedProxy, -totalDefaultAmount, vaultAsset, vaultScale);
|
||||
}
|
||||
view.update(vaultSle);
|
||||
vaultSle.update();
|
||||
}
|
||||
|
||||
// Update the LoanBroker object:
|
||||
@@ -269,7 +271,7 @@ LoanManage::defaultLoan(
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
coverAvailableProxy -= defaultCovered;
|
||||
view.update(brokerSle);
|
||||
brokerSle.update();
|
||||
}
|
||||
|
||||
// Update the Loan object:
|
||||
@@ -282,7 +284,7 @@ LoanManage::defaultLoan(
|
||||
// Zero out the next due date. Since it's default, it'll be removed from
|
||||
// the object.
|
||||
loanSle->at(sfNextPaymentDueDate) = 0;
|
||||
view.update(loanSle);
|
||||
loanSle.update();
|
||||
|
||||
// Return funds from the LoanBroker pseudo-account to the
|
||||
// Vault pseudo-account:
|
||||
@@ -298,12 +300,13 @@ LoanManage::defaultLoan(
|
||||
|
||||
TER
|
||||
LoanManage::impairLoan(
|
||||
ApplyView& view,
|
||||
SLE::ref loanSle,
|
||||
SLE::ref vaultSle,
|
||||
Asset const& vaultAsset,
|
||||
beast::Journal j)
|
||||
LoanEntry<ApplyView>& loanSle,
|
||||
VaultEntry<ApplyView>& vaultSle,
|
||||
Asset const& vaultAsset)
|
||||
{
|
||||
ApplyView const& view = loanSle.applyView();
|
||||
beast::Journal const j = loanSle.journal();
|
||||
|
||||
Number const lossUnrealized = owedToVault(loanSle);
|
||||
|
||||
// The vault may be at a different scale than the loan. Reduce rounding
|
||||
@@ -322,7 +325,7 @@ LoanManage::impairLoan(
|
||||
"corrupt the vault.";
|
||||
return tecLIMIT_EXCEEDED;
|
||||
}
|
||||
view.update(vaultSle);
|
||||
vaultSle.update();
|
||||
|
||||
// Update the Loan object
|
||||
loanSle->setFlag(lsfLoanImpaired);
|
||||
@@ -333,19 +336,20 @@ LoanManage::impairLoan(
|
||||
// move the next payment due date to now
|
||||
loanNextDueProxy = view.parentCloseTime().time_since_epoch().count();
|
||||
}
|
||||
view.update(loanSle);
|
||||
loanSle.update();
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
[[nodiscard]] TER
|
||||
LoanManage::unimpairLoan(
|
||||
ApplyView& view,
|
||||
SLE::ref loanSle,
|
||||
SLE::ref vaultSle,
|
||||
Asset const& vaultAsset,
|
||||
beast::Journal j)
|
||||
LoanEntry<ApplyView>& loanSle,
|
||||
VaultEntry<ApplyView>& vaultSle,
|
||||
Asset const& vaultAsset)
|
||||
{
|
||||
ApplyView const& view = loanSle.applyView();
|
||||
beast::Journal const j = loanSle.journal();
|
||||
|
||||
// The vault may be at a different scale than the loan. Reduce rounding
|
||||
// errors during the accounting by rounding some of the values to that
|
||||
// scale.
|
||||
@@ -364,7 +368,7 @@ LoanManage::unimpairLoan(
|
||||
// Reverse the "paper loss"
|
||||
adjustImpreciseNumber(vaultLossUnrealizedProxy, -lossReversed, vaultAsset, vaultScale);
|
||||
|
||||
view.update(vaultSle);
|
||||
vaultSle.update();
|
||||
|
||||
// Update the Loan object
|
||||
loanSle->clearFlag(lsfLoanImpaired);
|
||||
@@ -382,7 +386,7 @@ LoanManage::unimpairLoan(
|
||||
loanSle->at(sfNextPaymentDueDate) =
|
||||
view.parentCloseTime().time_since_epoch().count() + paymentInterval;
|
||||
}
|
||||
view.update(loanSle);
|
||||
loanSle.update();
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
@@ -394,16 +398,16 @@ LoanManage::doApply()
|
||||
auto& view = ctx_.view();
|
||||
|
||||
auto const loanID = tx[sfLoanID];
|
||||
auto const loanSle = view.peek(keylet::loan(loanID));
|
||||
LoanEntry<ApplyView> loanSle{keylet::loan(loanID), view, j_};
|
||||
if (!loanSle)
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
|
||||
auto const brokerID = loanSle->at(sfLoanBrokerID);
|
||||
auto const brokerSle = view.peek(keylet::loanBroker(brokerID));
|
||||
LoanBrokerEntry<ApplyView> brokerSle{keylet::loanBroker(brokerID), view, j_};
|
||||
if (!brokerSle)
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
|
||||
auto const vaultSle = view.peek(keylet::vault(brokerSle->at(sfVaultID)));
|
||||
VaultEntry<ApplyView> vaultSle{keylet::vault(brokerSle->at(sfVaultID)), view, j_};
|
||||
if (!vaultSle)
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
auto const vaultAsset = vaultSle->at(sfAsset);
|
||||
@@ -412,11 +416,11 @@ LoanManage::doApply()
|
||||
// Valid flag combinations are checked in preflight. No flags is valid -
|
||||
// just a noop.
|
||||
if (tx.isFlag(tfLoanDefault))
|
||||
return defaultLoan(view, loanSle, brokerSle, vaultSle, vaultAsset, j_);
|
||||
return defaultLoan(loanSle, brokerSle, vaultSle, vaultAsset);
|
||||
if (tx.isFlag(tfLoanImpair))
|
||||
return impairLoan(view, loanSle, vaultSle, vaultAsset, j_);
|
||||
return impairLoan(loanSle, vaultSle, vaultAsset);
|
||||
if (tx.isFlag(tfLoanUnimpair))
|
||||
return unimpairLoan(view, loanSle, vaultSle, vaultAsset, j_);
|
||||
return unimpairLoan(loanSle, vaultSle, vaultAsset);
|
||||
// NoOp, as described above.
|
||||
return tesSUCCESS;
|
||||
}();
|
||||
|
||||
@@ -5,9 +5,11 @@
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/json/to_string.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/LendingHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
@@ -287,19 +289,19 @@ LoanPay::doApply()
|
||||
auto const amount = tx[sfAmount];
|
||||
|
||||
auto const loanID = tx[sfLoanID];
|
||||
auto const loanSle = view.peek(keylet::loan(loanID));
|
||||
LoanEntry<ApplyView> loanSle{keylet::loan(loanID), view, j_};
|
||||
if (!loanSle)
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
std::int32_t const loanScale = loanSle->at(sfLoanScale);
|
||||
|
||||
auto const brokerID = loanSle->at(sfLoanBrokerID);
|
||||
auto const brokerSle = view.peek(keylet::loanBroker(brokerID));
|
||||
LoanBrokerEntry<ApplyView> brokerSle{keylet::loanBroker(brokerID), view};
|
||||
if (!brokerSle)
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
auto const brokerOwner = brokerSle->at(sfOwner);
|
||||
auto const brokerPseudoAccount = brokerSle->at(sfAccount);
|
||||
auto const vaultID = brokerSle->at(sfVaultID);
|
||||
auto const vaultSle = view.peek(keylet::vault(vaultID));
|
||||
VaultEntry<ApplyView> vaultSle{keylet::vault(vaultID), view, j_};
|
||||
if (!vaultSle)
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
auto const vaultPseudoAccount = vaultSle->at(sfAccount);
|
||||
@@ -362,7 +364,7 @@ LoanPay::doApply()
|
||||
// change will be discarded.
|
||||
if (loanSle->isFlag(lsfLoanImpaired))
|
||||
{
|
||||
if (auto const ret = LoanManage::unimpairLoan(view, loanSle, vaultSle, asset, j_))
|
||||
if (auto const ret = LoanManage::unimpairLoan(loanSle, vaultSle, asset))
|
||||
{
|
||||
JLOG(j_.fatal()) << "Failed to unimpair loan before payment.";
|
||||
return ret; // LCOV_EXCL_LINE
|
||||
@@ -381,7 +383,7 @@ LoanPay::doApply()
|
||||
}();
|
||||
|
||||
std::expected<LoanPaymentParts, TER> const paymentParts =
|
||||
loanMakePayment(asset, view, loanSle, brokerSle, amount, paymentType, j_);
|
||||
loanMakePayment(asset, loanSle, brokerSle, amount, paymentType);
|
||||
|
||||
if (!paymentParts)
|
||||
{
|
||||
@@ -392,7 +394,7 @@ LoanPay::doApply()
|
||||
|
||||
// If the payment computation completed without error, the loanSle object
|
||||
// has been modified.
|
||||
view.update(loanSle);
|
||||
loanSle.update();
|
||||
|
||||
XRPL_ASSERT_PARTS(
|
||||
// It is possible to pay 0 principal
|
||||
@@ -427,7 +429,7 @@ LoanPay::doApply()
|
||||
|
||||
//------------------------------------------------------
|
||||
// LoanBroker object state changes
|
||||
view.update(brokerSle);
|
||||
brokerSle.update();
|
||||
|
||||
auto assetsAvailableProxy = vaultSle->at(sfAssetsAvailable);
|
||||
auto assetsTotalProxy = vaultSle->at(sfAssetsTotal);
|
||||
@@ -468,7 +470,7 @@ LoanPay::doApply()
|
||||
|
||||
//------------------------------------------------------
|
||||
// Vault object state changes
|
||||
view.update(vaultSle);
|
||||
vaultSle.update();
|
||||
|
||||
Number const assetsAvailableBefore = *assetsAvailableProxy;
|
||||
Number const assetsTotalBefore = *assetsTotalProxy;
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/LendingHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/ledger/helpers/SponsorHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
@@ -388,7 +388,7 @@ LoanSet::doApply()
|
||||
if (!brokerOwnerSle)
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
|
||||
auto const vaultSle = view.peek(keylet ::vault(brokerSle->at(sfVaultID)));
|
||||
VaultEntry<ApplyView> vaultSle{keylet::vault(brokerSle->at(sfVaultID)), view};
|
||||
if (!vaultSle)
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
auto const vaultPseudo = vaultSle->at(sfAccount);
|
||||
@@ -593,7 +593,8 @@ LoanSet::doApply()
|
||||
auto loanSequenceProxy = brokerSle->at(sfLoanSequence);
|
||||
|
||||
// Create the loan
|
||||
auto loan = std::make_shared<SLE>(keylet::loan(brokerID, *loanSequenceProxy));
|
||||
LoanEntry<ApplyView> loan{keylet::loan(brokerID, *loanSequenceProxy), view, j_};
|
||||
loan.newSLE();
|
||||
|
||||
// Prevent copy/paste errors
|
||||
auto setLoanField = [&loan, &tx](auto const& field, std::uint32_t const defValue = 0) {
|
||||
@@ -630,7 +631,6 @@ LoanSet::doApply()
|
||||
loan->at(sfPreviousPaymentDueDate) = 0;
|
||||
loan->at(sfNextPaymentDueDate) = startDate + paymentInterval;
|
||||
loan->at(sfPaymentRemaining) = paymentTotal;
|
||||
view.insert(loan);
|
||||
|
||||
// Update the balances in the vault
|
||||
vaultAvailableProxy -= principalRequested;
|
||||
@@ -639,7 +639,7 @@ LoanSet::doApply()
|
||||
*vaultAvailableProxy <= *vaultTotalProxy,
|
||||
"xrpl::LoanSet::doApply",
|
||||
"assets available must not be greater than assets outstanding");
|
||||
view.update(vaultSle);
|
||||
vaultSle.update();
|
||||
|
||||
// Update the balances in the loan broker
|
||||
adjustImpreciseNumber(brokerSle->at(sfDebtTotal), newDebtDelta, vaultAsset, vaultScale);
|
||||
@@ -651,11 +651,11 @@ LoanSet::doApply()
|
||||
return tecMAX_SEQUENCE_REACHED;
|
||||
view.update(brokerSle);
|
||||
|
||||
// Put the loan into the pseudo-account's directory
|
||||
if (auto const ter = dirLink(view, brokerPseudo, loan, sfLoanBrokerNode))
|
||||
return ter;
|
||||
// Borrower is the owner of the loan
|
||||
if (auto const ter = dirLink(view, borrower, loan, sfOwnerNode))
|
||||
// Insert the loan and link it into the broker pseudo-account's directory
|
||||
// (sfLoanBrokerNode) and the borrower's directory (sfOwnerNode). The
|
||||
// OwnerCount bumps (borrower account, LoanBroker object) were applied above.
|
||||
// See LoanEntry::create().
|
||||
if (auto const ter = loan.create(); !isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
associateAsset(*vaultSle, vaultAsset);
|
||||
|
||||
@@ -4,9 +4,11 @@
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/NFTokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
@@ -55,16 +57,16 @@ TER
|
||||
NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
auto const checkOffer =
|
||||
[&ctx](std::optional<uint256> id) -> std::pair<SLE::const_pointer, TER> {
|
||||
[&ctx](std::optional<uint256> id) -> std::pair<NFTokenOfferEntry<ReadView>, TER> {
|
||||
if (id)
|
||||
{
|
||||
if (id->isZero())
|
||||
return {nullptr, tecOBJECT_NOT_FOUND};
|
||||
return {NFTokenOfferEntry<ReadView>{nullptr, ctx.view}, tecOBJECT_NOT_FOUND};
|
||||
|
||||
auto offerSLE = ctx.view.read(keylet::nftokenOffer(*id));
|
||||
NFTokenOfferEntry<ReadView> offerSLE{keylet::nftokenOffer(*id), ctx.view};
|
||||
|
||||
if (!offerSLE)
|
||||
return {nullptr, tecOBJECT_NOT_FOUND};
|
||||
return {NFTokenOfferEntry<ReadView>{nullptr, ctx.view}, tecOBJECT_NOT_FOUND};
|
||||
|
||||
if (hasExpired(ctx.view, (*offerSLE)[~sfExpiration]))
|
||||
{
|
||||
@@ -72,16 +74,16 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx)
|
||||
// leaving them on ledger forever. After the amendment, we allow expired offers to
|
||||
// reach doApply() where they get deleted and tecEXPIRED is returned.
|
||||
if (!ctx.view.rules().enabled(fixCleanup3_1_3))
|
||||
return {nullptr, tecEXPIRED};
|
||||
return {NFTokenOfferEntry<ReadView>{nullptr, ctx.view}, tecEXPIRED};
|
||||
// Amendment enabled: return the expired offer to be handled in doApply.
|
||||
}
|
||||
|
||||
if ((*offerSLE)[sfAmount].negative())
|
||||
return {nullptr, temBAD_OFFER};
|
||||
return {NFTokenOfferEntry<ReadView>{nullptr, ctx.view}, temBAD_OFFER};
|
||||
|
||||
return {std::move(offerSLE), tesSUCCESS};
|
||||
}
|
||||
return {nullptr, tesSUCCESS};
|
||||
return {NFTokenOfferEntry<ReadView>{nullptr, ctx.view}, tesSUCCESS};
|
||||
};
|
||||
|
||||
auto const [bo, err1] = checkOffer(ctx.tx[~sfNFTokenBuyOffer]);
|
||||
@@ -396,7 +398,7 @@ NFTokenAcceptOffer::transferNFToken(
|
||||
}
|
||||
|
||||
TER
|
||||
NFTokenAcceptOffer::acceptOffer(SLE::ref offer)
|
||||
NFTokenAcceptOffer::acceptOffer(NFTokenOfferEntry<ApplyView> const& offer)
|
||||
{
|
||||
bool const isSell = offer->isFlag(lsfSellNFToken);
|
||||
AccountID const owner = (*offer)[sfOwner];
|
||||
@@ -434,10 +436,9 @@ TER
|
||||
NFTokenAcceptOffer::doApply()
|
||||
{
|
||||
auto const loadToken = [this](std::optional<uint256> const& id) {
|
||||
SLE::pointer sle;
|
||||
if (id)
|
||||
sle = view().peek(keylet::nftokenOffer(*id));
|
||||
return sle;
|
||||
return NFTokenOfferEntry<ApplyView>{keylet::nftokenOffer(*id), view()};
|
||||
return NFTokenOfferEntry<ApplyView>{SLE::pointer{}, view()};
|
||||
};
|
||||
|
||||
auto bo = loadToken(ctx_.tx[~sfNFTokenBuyOffer]);
|
||||
@@ -449,11 +450,12 @@ NFTokenAcceptOffer::doApply()
|
||||
{
|
||||
bool foundExpired = false;
|
||||
|
||||
auto const deleteOfferIfExpired = [this, &foundExpired](SLE::ref offer) -> TER {
|
||||
auto const deleteOfferIfExpired =
|
||||
[this, &foundExpired](NFTokenOfferEntry<ApplyView>& offer) -> TER {
|
||||
if (offer && hasExpired(view(), (*offer)[~sfExpiration]))
|
||||
{
|
||||
JLOG(j_.trace()) << "Offer is expired, deleting: " << offer->key();
|
||||
if (!nft::deleteTokenOffer(view(), offer))
|
||||
if (!nft::deleteTokenOffer(view(), offer.mutableSle()))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j_.fatal())
|
||||
@@ -476,7 +478,7 @@ NFTokenAcceptOffer::doApply()
|
||||
return tecEXPIRED;
|
||||
}
|
||||
|
||||
if (bo && !nft::deleteTokenOffer(view(), bo))
|
||||
if (bo && !nft::deleteTokenOffer(view(), bo.mutableSle()))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j_.fatal()) << "Unable to delete buy offer '" << to_string(bo->key()) << "': ignoring";
|
||||
@@ -484,7 +486,7 @@ NFTokenAcceptOffer::doApply()
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
if (so && !nft::deleteTokenOffer(view(), so))
|
||||
if (so && !nft::deleteTokenOffer(view(), so.mutableSle()))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j_.fatal()) << "Unable to delete sell offer '" << to_string(so->key())
|
||||
|
||||
@@ -3,10 +3,9 @@
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/OracleHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
@@ -14,8 +13,6 @@
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
NotTEC
|
||||
@@ -27,12 +24,10 @@ OracleDelete::preflight(PreflightContext const& ctx)
|
||||
TER
|
||||
OracleDelete::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
if (!ctx.view.exists(keylet::account(ctx.tx.getAccountID(sfAccount))))
|
||||
return terNO_ACCOUNT; // LCOV_EXCL_LINE
|
||||
auto const sle = OracleEntry<ReadView>{
|
||||
ctx.tx.getAccountID(sfAccount), ctx.tx[sfOracleDocumentID], ctx.view, ctx.j};
|
||||
|
||||
auto const sle =
|
||||
ctx.view.read(keylet::oracle(ctx.tx.getAccountID(sfAccount), ctx.tx[sfOracleDocumentID]));
|
||||
if (!sle)
|
||||
if (!sle.exists())
|
||||
{
|
||||
JLOG(ctx.j.debug()) << "Oracle Delete: Oracle does not exist.";
|
||||
return tecNO_ENTRY;
|
||||
@@ -59,32 +54,21 @@ OracleDelete::deleteOracle(
|
||||
if (!sle)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
if (!view.dirRemove(keylet::ownerDir(account), (*sle)[sfOwnerNode], sle->key(), true))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.fatal()) << "Unable to delete Oracle from owner.";
|
||||
return tefBAD_LEDGER;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
auto const sleOwner = view.peek(keylet::account(account));
|
||||
if (!sleOwner)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
std::uint32_t const count = calculateOracleReserve(sle);
|
||||
decreaseOwnerCountForObject(view, sleOwner, sle, count, j);
|
||||
view.erase(sle);
|
||||
|
||||
return tesSUCCESS;
|
||||
// Unlink the Oracle from its owner's directory, decrement the owner's
|
||||
// OwnerCount by reserveCount() (1, or 2 for a large price-data series,
|
||||
// refunding any reserve sponsor), and erase it. See OracleEntry.
|
||||
OracleEntry<ApplyView> oracle{sle, view, j};
|
||||
return oracle.destroy();
|
||||
}
|
||||
|
||||
TER
|
||||
OracleDelete::doApply()
|
||||
{
|
||||
if (auto sle = ctx_.view().peek(keylet::oracle(accountID_, ctx_.tx[sfOracleDocumentID])))
|
||||
return deleteOracle(ctx_.view(), sle, accountID_, j_);
|
||||
auto const sle = OracleEntry<ApplyView>{accountID_, ctx_.tx[sfOracleDocumentID], ctx_.view()};
|
||||
if (!sle.exists())
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
return deleteOracle(ctx_.view(), sle.mutableSle(), accountID_, j_);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
#include <xrpl/basics/chrono.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
#include <xrpl/ledger/helpers/OracleHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/InnerObjectFormats.h>
|
||||
@@ -64,7 +65,8 @@ OracleSet::preflight(PreflightContext const& ctx)
|
||||
TER
|
||||
OracleSet::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
auto const sleSetter = ctx.view.read(keylet::account(ctx.tx.getAccountID(sfAccount)));
|
||||
AccountRootEntry<ReadView> const sleSetter{
|
||||
keylet::account(ctx.tx.getAccountID(sfAccount)), ctx.view};
|
||||
if (!sleSetter)
|
||||
return terNO_ACCOUNT; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -83,8 +85,8 @@ OracleSet::preclaim(PreclaimContext const& ctx)
|
||||
lastUpdateTimeEpoch > (closeTime + kMaxLastUpdateTimeDelta))
|
||||
return tecINVALID_UPDATE_TIME;
|
||||
|
||||
auto const sle =
|
||||
ctx.view.read(keylet::oracle(ctx.tx.getAccountID(sfAccount), ctx.tx[sfOracleDocumentID]));
|
||||
OracleEntry<ReadView> sle{
|
||||
ctx.tx.getAccountID(sfAccount), ctx.tx[sfOracleDocumentID], ctx.view, ctx.j};
|
||||
|
||||
// token pairs to add/update
|
||||
std::set<std::pair<Currency, Currency>> pairs;
|
||||
@@ -153,7 +155,7 @@ OracleSet::preclaim(PreclaimContext const& ctx)
|
||||
if (!pairsDel.empty())
|
||||
return tecTOKEN_PAIR_NOT_FOUND;
|
||||
|
||||
auto const oldCount = calculateOracleReserve(sle);
|
||||
auto const oldCount = sle.reserveCount();
|
||||
auto const newCount = calculateOracleReserve(pairs);
|
||||
|
||||
adjustReserve = newCount - oldCount;
|
||||
@@ -173,7 +175,7 @@ OracleSet::preclaim(PreclaimContext const& ctx)
|
||||
return tecARRAY_TOO_LARGE;
|
||||
|
||||
auto const reserve =
|
||||
accountReserve(ctx.view, sleSetter, ctx.j, {.ownerCountDelta = adjustReserve});
|
||||
accountReserve(ctx.view, sleSetter.sle(), ctx.j, {.ownerCountDelta = adjustReserve});
|
||||
auto const& balance = sleSetter->getFieldAmount(sfBalance);
|
||||
|
||||
if (balance < reserve)
|
||||
@@ -216,8 +218,6 @@ setPriceDataInnerObjTemplate(STObject& obj)
|
||||
TER
|
||||
OracleSet::doApply()
|
||||
{
|
||||
auto const oracleID = keylet::oracle(accountID_, ctx_.tx[sfOracleDocumentID]);
|
||||
|
||||
auto populatePriceData = [](STObject& priceData, STObject const& entry) {
|
||||
setPriceDataInnerObjTemplate(priceData);
|
||||
priceData.setFieldCurrency(sfBaseAsset, entry.getFieldCurrency(sfBaseAsset));
|
||||
@@ -227,7 +227,8 @@ OracleSet::doApply()
|
||||
priceData.setFieldU8(sfScale, entry.getFieldU8(sfScale));
|
||||
};
|
||||
|
||||
if (auto sle = ctx_.view().peek(oracleID))
|
||||
OracleEntry<ApplyView> sle{accountID_, ctx_.tx[sfOracleDocumentID], ctx_.view()};
|
||||
if (sle)
|
||||
{
|
||||
// update
|
||||
// the token pair that doesn't have their price updated will not
|
||||
@@ -287,13 +288,13 @@ OracleSet::doApply()
|
||||
if (adjust != 0 && !adjustOracleOwnerCount(ctx_, adjust))
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
ctx_.view().update(sle);
|
||||
sle.update();
|
||||
}
|
||||
else
|
||||
{
|
||||
// create
|
||||
|
||||
sle = std::make_shared<SLE>(oracleID);
|
||||
sle.newSLE();
|
||||
sle->setAccountID(sfOwner, ctx_.tx.getAccountID(sfAccount));
|
||||
if (ctx_.view().rules().enabled(fixIncludeKeyletFields))
|
||||
{
|
||||
@@ -326,18 +327,10 @@ OracleSet::doApply()
|
||||
sle->setFieldVL(sfAssetClass, ctx_.tx[sfAssetClass]);
|
||||
sle->setFieldU32(sfLastUpdateTime, ctx_.tx[sfLastUpdateTime]);
|
||||
|
||||
auto page = ctx_.view().dirInsert(
|
||||
keylet::ownerDir(accountID_), sle->key(), describeOwnerDir(accountID_));
|
||||
if (!page)
|
||||
return tecDIR_FULL; // LCOV_EXCL_LINE
|
||||
|
||||
(*sle)[sfOwnerNode] = *page;
|
||||
|
||||
auto const count = calculateOracleReserve(series);
|
||||
if (!adjustOracleOwnerCount(ctx_, count))
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
ctx_.view().insert(sle);
|
||||
// Reserve check (owner's pre-fee balance) + link into the owner
|
||||
// directory + bump the owner's OwnerCount by reserveCount() (1, or 2
|
||||
// for a large price-data series) + insert. See OracleEntry.
|
||||
return sle.create(preFeeBalance_);
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
|
||||
@@ -4,10 +4,9 @@
|
||||
#include <xrpl/basics/Slice.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/helpers/CredentialHelpers.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SponsorHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
@@ -21,7 +20,6 @@
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
@@ -154,44 +152,20 @@ DepositPreauth::doApply()
|
||||
auto applyViewContext = ctx_.getApplyViewContext();
|
||||
if (ctx_.tx.isFieldPresent(sfAuthorize))
|
||||
{
|
||||
auto const sleOwner = view().peek(keylet::account(accountID_));
|
||||
if (!sleOwner)
|
||||
return {tefINTERNAL};
|
||||
|
||||
// A preauth counts against the reserve of the issuing account, but we
|
||||
// check the starting balance because we want to allow dipping into the
|
||||
// reserve to pay fees.
|
||||
if (auto const ret = checkReserve(
|
||||
applyViewContext, sleOwner, preFeeBalance_, {.ownerCountDelta = 1}, j_);
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
|
||||
// Preclaim already verified that the Preauth entry does not yet exist.
|
||||
// Create and populate the Preauth entry.
|
||||
AccountID const auth{ctx_.tx[sfAuthorize]};
|
||||
Keylet const preauthKeylet = keylet::depositPreauth(accountID_, auth);
|
||||
auto slePreauth = std::make_shared<SLE>(preauthKeylet);
|
||||
// Build with the ApplyViewContext so create() honors reserve sponsorship.
|
||||
DepositPreauthEntry<ApplyView> slePreauth{preauthKeylet, applyViewContext};
|
||||
slePreauth.newSLE();
|
||||
|
||||
slePreauth->setAccountID(sfAccount, accountID_);
|
||||
slePreauth->setAccountID(sfAuthorize, auth);
|
||||
view().insert(slePreauth);
|
||||
|
||||
auto const page = view().dirInsert(
|
||||
keylet::ownerDir(accountID_), preauthKeylet, describeOwnerDir(accountID_));
|
||||
|
||||
JLOG(j_.trace()) << "Adding DepositPreauth to owner directory "
|
||||
<< to_string(preauthKeylet.key) << ": " << (page ? "success" : "failure");
|
||||
|
||||
if (!page)
|
||||
return tecDIR_FULL; // LCOV_EXCL_LINE
|
||||
|
||||
slePreauth->setFieldU64(sfOwnerNode, *page);
|
||||
|
||||
// If we succeeded, the new entry counts against the creator's reserve.
|
||||
increaseOwnerCount(applyViewContext, sleOwner, 1, j_);
|
||||
addSponsorToLedgerEntry(applyViewContext, slePreauth);
|
||||
return slePreauth.create(preFeeBalance_);
|
||||
}
|
||||
else if (ctx_.tx.isFieldPresent(sfUnauthorize))
|
||||
if (ctx_.tx.isFieldPresent(sfUnauthorize))
|
||||
{
|
||||
auto const preauth = keylet::depositPreauth(accountID_, ctx_.tx[sfUnauthorize]);
|
||||
|
||||
@@ -199,18 +173,6 @@ DepositPreauth::doApply()
|
||||
}
|
||||
else if (ctx_.tx.isFieldPresent(sfAuthorizeCredentials))
|
||||
{
|
||||
auto const sleOwner = view().peek(keylet::account(accountID_));
|
||||
if (!sleOwner)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
// A preauth counts against the reserve of the issuing account, but we
|
||||
// check the starting balance because we want to allow dipping into the
|
||||
// reserve to pay fees.
|
||||
if (auto const ret = checkReserve(
|
||||
applyViewContext, sleOwner, preFeeBalance_, {.ownerCountDelta = 1}, j_);
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
|
||||
// Preclaim already verified that the Preauth entry does not yet exist.
|
||||
// Create and populate the Preauth entry.
|
||||
|
||||
@@ -226,29 +188,14 @@ DepositPreauth::doApply()
|
||||
}
|
||||
|
||||
Keylet const preauthKey = keylet::depositPreauth(accountID_, sortedTX);
|
||||
auto slePreauth = std::make_shared<SLE>(preauthKey);
|
||||
if (!slePreauth)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
// Build with the ApplyViewContext so create() honors reserve sponsorship.
|
||||
DepositPreauthEntry<ApplyView> slePreauth{preauthKey, applyViewContext};
|
||||
slePreauth.newSLE();
|
||||
|
||||
slePreauth->setAccountID(sfAccount, accountID_);
|
||||
slePreauth->peekFieldArray(sfAuthorizeCredentials) = std::move(sortedLE);
|
||||
|
||||
view().insert(slePreauth);
|
||||
|
||||
auto const page = view().dirInsert(
|
||||
keylet::ownerDir(accountID_), preauthKey, describeOwnerDir(accountID_));
|
||||
|
||||
JLOG(j_.trace()) << "Adding DepositPreauth to owner directory " << to_string(preauthKey.key)
|
||||
<< ": " << (page ? "success" : "failure");
|
||||
|
||||
if (!page)
|
||||
return tecDIR_FULL; // LCOV_EXCL_LINE
|
||||
|
||||
slePreauth->setFieldU64(sfOwnerNode, *page);
|
||||
|
||||
// If we succeeded, the new entry counts against the creator's reserve.
|
||||
increaseOwnerCount(applyViewContext, sleOwner, 1, j_);
|
||||
addSponsorToLedgerEntry(applyViewContext, slePreauth);
|
||||
return slePreauth.create(preFeeBalance_);
|
||||
}
|
||||
else if (ctx_.tx.isFieldPresent(sfUnauthorizeCredentials))
|
||||
{
|
||||
@@ -271,26 +218,8 @@ DepositPreauth::removeFromLedger(ApplyView& view, uint256 const& preauthIndex, b
|
||||
return tecNO_ENTRY;
|
||||
}
|
||||
|
||||
AccountID const account{(*slePreauth)[sfAccount]};
|
||||
std::uint64_t const page{(*slePreauth)[sfOwnerNode]};
|
||||
if (!view.dirRemove(keylet::ownerDir(account), page, preauthIndex, false))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.fatal()) << "Unable to delete DepositPreauth from owner.";
|
||||
return tefBAD_LEDGER;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
// If we succeeded, update the DepositPreauth owner's reserve.
|
||||
auto const sleOwner = view.peek(keylet::account(account));
|
||||
if (!sleOwner)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
decreaseOwnerCountForObject(view, sleOwner, slePreauth, 1, j);
|
||||
// Remove DepositPreauth from ledger.
|
||||
view.erase(slePreauth);
|
||||
|
||||
return tesSUCCESS;
|
||||
DepositPreauthEntry<ApplyView> preauth{slePreauth, view, j};
|
||||
return preauth.destroy();
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/helpers/CredentialHelpers.h>
|
||||
#include <xrpl/ledger/helpers/PaymentChannelHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Keylet.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/PayChan.h>
|
||||
@@ -110,7 +110,7 @@ TER
|
||||
PaymentChannelClaim::doApply()
|
||||
{
|
||||
Keylet const k(ltPAYCHAN, ctx_.tx[sfChannel]);
|
||||
auto const slep = ctx_.view().peek(k);
|
||||
PayChannelEntry<ApplyView> slep{k, ctx_.view()};
|
||||
if (!slep)
|
||||
return tecNO_TARGET;
|
||||
|
||||
@@ -159,12 +159,12 @@ PaymentChannelClaim::doApply()
|
||||
return tecUNFUNDED_PAYMENT;
|
||||
}
|
||||
|
||||
auto const sled = ctx_.view().peek(keylet::account(dst));
|
||||
AccountRootEntry<ApplyView> sled{dst, ctx_.view()};
|
||||
if (!sled)
|
||||
return tecNO_DST;
|
||||
|
||||
if (auto err =
|
||||
verifyDepositPreauth(ctx_.tx, ctx_.view(), txAccount, dst, sled, ctx_.journal);
|
||||
if (auto err = verifyDepositPreauth(
|
||||
ctx_.tx, ctx_.view(), txAccount, dst, sled.sle(), ctx_.journal);
|
||||
!isTesSuccess(err))
|
||||
return err;
|
||||
|
||||
@@ -173,8 +173,8 @@ PaymentChannelClaim::doApply()
|
||||
XRPL_ASSERT(
|
||||
reqDelta >= beast::kZero, "xrpl::PaymentChannelClaim::doApply : minimum balance delta");
|
||||
(*sled)[sfBalance] = (*sled)[sfBalance] + reqDelta;
|
||||
ctx_.view().update(sled);
|
||||
ctx_.view().update(slep);
|
||||
sled.update();
|
||||
slep.update();
|
||||
}
|
||||
|
||||
if (ctx_.tx.isFlag(tfRenew))
|
||||
@@ -182,7 +182,7 @@ PaymentChannelClaim::doApply()
|
||||
if (src != txAccount)
|
||||
return tecNO_PERMISSION;
|
||||
(*slep)[~sfExpiration] = std::nullopt;
|
||||
ctx_.view().update(slep);
|
||||
slep.update();
|
||||
}
|
||||
|
||||
if (ctx_.tx.isFlag(tfClose))
|
||||
@@ -199,7 +199,7 @@ PaymentChannelClaim::doApply()
|
||||
if (!curExpiration || *curExpiration > settleExpiration)
|
||||
{
|
||||
(*slep)[~sfExpiration] = settleExpiration;
|
||||
ctx_.view().update(slep);
|
||||
slep.update();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/ledger/helpers/SponsorHelpers.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
@@ -170,7 +170,9 @@ PaymentChannelCreate::doApply()
|
||||
// Note that we use the value from the sequence or ticket as the
|
||||
// payChan sequence. For more explanation see comments in SeqProxy.h.
|
||||
Keylet const payChanKeylet = keylet::payChannel(account, dst, ctx_.tx.getSeqValue());
|
||||
auto const slep = std::make_shared<SLE>(payChanKeylet);
|
||||
// Build with the ApplyViewContext so create() honors reserve sponsorship.
|
||||
PayChannelEntry<ApplyView> slep{payChanKeylet, ctx_.getApplyViewContext()};
|
||||
slep.newSLE();
|
||||
|
||||
// Funds held in this channel
|
||||
(*slep)[sfAmount] = ctx_.tx[sfAmount];
|
||||
@@ -188,33 +190,14 @@ PaymentChannelCreate::doApply()
|
||||
(*slep)[sfSequence] = ctx_.tx.getSeqValue();
|
||||
}
|
||||
|
||||
ctx_.view().insert(slep);
|
||||
|
||||
// Add PayChan to owner directory
|
||||
{
|
||||
auto const page = ctx_.view().dirInsert(
|
||||
keylet::ownerDir(account), payChanKeylet, describeOwnerDir(account));
|
||||
if (!page)
|
||||
return tecDIR_FULL; // LCOV_EXCL_LINE
|
||||
(*slep)[sfOwnerNode] = *page;
|
||||
}
|
||||
|
||||
// Add PayChan to the recipient's owner directory
|
||||
{
|
||||
auto const page =
|
||||
ctx_.view().dirInsert(keylet::ownerDir(dst), payChanKeylet, describeOwnerDir(dst));
|
||||
if (!page)
|
||||
return tecDIR_FULL; // LCOV_EXCL_LINE
|
||||
(*slep)[sfDestinationNode] = *page;
|
||||
}
|
||||
|
||||
// Deduct owner's balance, increment owner count
|
||||
// Deduct the channel amount from the owner's balance.
|
||||
(*sle)[sfBalance] = (*sle)[sfBalance] - ctx_.tx[sfAmount];
|
||||
increaseOwnerCount(ctx_.getApplyViewContext(), sle, 1, ctx_.journal);
|
||||
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), slep);
|
||||
ctx_.view().update(sle);
|
||||
|
||||
return tesSUCCESS;
|
||||
// Reserve check + link into the owner and destination directories + bump
|
||||
// the owner's OwnerCount + stamp any reserve sponsor + insert. See
|
||||
// PayChannelEntry::ownerDirs() and SLEBase::create().
|
||||
return slep.create(preFeeBalance_);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/PaymentChannelHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
@@ -44,7 +45,7 @@ TER
|
||||
PaymentChannelFund::doApply()
|
||||
{
|
||||
Keylet const k(ltPAYCHAN, ctx_.tx[sfChannel]);
|
||||
auto const slep = ctx_.view().peek(k);
|
||||
PayChannelEntry<ApplyView> slep{k, ctx_.view()};
|
||||
if (!slep)
|
||||
return tecNO_ENTRY;
|
||||
|
||||
@@ -79,17 +80,18 @@ PaymentChannelFund::doApply()
|
||||
: TER{temBAD_EXPIRATION};
|
||||
}
|
||||
(*slep)[~sfExpiration] = *newExpiration;
|
||||
ctx_.view().update(slep);
|
||||
slep.update();
|
||||
}
|
||||
|
||||
auto const sle = ctx_.view().peek(keylet::account(txAccount));
|
||||
AccountRootEntry<ApplyView> sle{txAccount, ctx_.view()};
|
||||
if (!sle)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
{
|
||||
// Check reserve and funds availability
|
||||
STAmount const balance = (*sle)[sfBalance];
|
||||
if (auto const ret = checkReserve(ctx_.getApplyViewContext(), sle, balance.xrp(), {}, j_);
|
||||
if (auto const ret =
|
||||
checkReserve(ctx_.getApplyViewContext(), sle.sle(), balance.xrp(), {}, j_);
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
|
||||
@@ -99,7 +101,7 @@ PaymentChannelFund::doApply()
|
||||
// when a sponsor is present and would ignore the source's post-lock
|
||||
// balance entirely. Funding an existing channel adds no owned object,
|
||||
// so there is no owner-count delta.
|
||||
if (balance < accountReserve(ctx_.view(), sle, j_) + ctx_.tx[sfAmount])
|
||||
if (balance < accountReserve(ctx_.view(), sle.sle(), j_) + ctx_.tx[sfAmount])
|
||||
return tecUNFUNDED;
|
||||
}
|
||||
|
||||
@@ -110,10 +112,10 @@ PaymentChannelFund::doApply()
|
||||
}
|
||||
|
||||
(*slep)[sfAmount] = (*slep)[sfAmount] + ctx_.tx[sfAmount];
|
||||
ctx_.view().update(slep);
|
||||
slep.update();
|
||||
|
||||
(*sle)[sfBalance] = (*sle)[sfBalance] - ctx_.tx[sfAmount];
|
||||
ctx_.view().update(sle);
|
||||
sle.update();
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
#include <xrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.h>
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
@@ -28,7 +30,7 @@ TER
|
||||
PermissionedDomainDelete::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
auto const domain = ctx.tx.getFieldH256(sfDomainID);
|
||||
auto const sleDomain = ctx.view.read(keylet::permissionedDomain(domain));
|
||||
PermissionedDomainEntry<ReadView> const sleDomain{keylet::permissionedDomain(domain), ctx.view};
|
||||
|
||||
if (!sleDomain)
|
||||
return tecNO_ENTRY;
|
||||
@@ -52,25 +54,10 @@ PermissionedDomainDelete::doApply()
|
||||
ctx_.tx.isFieldPresent(sfDomainID),
|
||||
"xrpl::PermissionedDomainDelete::doApply : required field present");
|
||||
|
||||
auto const slePd = view().peek(keylet::permissionedDomain(ctx_.tx.at(sfDomainID)));
|
||||
auto const page = (*slePd)[sfOwnerNode];
|
||||
PermissionedDomainEntry<ApplyView> slePd{
|
||||
keylet::permissionedDomain(ctx_.tx.at(sfDomainID)), view()};
|
||||
|
||||
if (!view().dirRemove(keylet::ownerDir(accountID_), page, slePd->key(), true))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j_.fatal()) << "Unable to delete permissioned domain directory entry.";
|
||||
return tefBAD_LEDGER;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
auto const ownerSle = view().peek(keylet::account(accountID_));
|
||||
XRPL_ASSERT(
|
||||
ownerSle && ownerSle->getFieldU32(sfOwnerCount) > 0,
|
||||
"xrpl::PermissionedDomainDelete::doApply : nonzero owner count");
|
||||
decreaseOwnerCountForObject(view(), ownerSle, slePd, 1, ctx_.journal);
|
||||
view().erase(slePd);
|
||||
|
||||
return tesSUCCESS;
|
||||
return slePd.destroy();
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/helpers/CredentialHelpers.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Keylet.h>
|
||||
@@ -62,8 +62,8 @@ PermissionedDomainSet::preclaim(PreclaimContext const& ctx)
|
||||
|
||||
if (ctx.tx.isFieldPresent(sfDomainID))
|
||||
{
|
||||
auto const sleDomain =
|
||||
ctx.view.read(keylet::permissionedDomain(ctx.tx.getFieldH256(sfDomainID)));
|
||||
PermissionedDomainEntry<ReadView> const sleDomain{
|
||||
keylet::permissionedDomain(ctx.tx.getFieldH256(sfDomainID)), ctx.view};
|
||||
if (!sleDomain)
|
||||
return tecNO_ENTRY;
|
||||
if (sleDomain->getAccountID(sfOwner) != account)
|
||||
@@ -79,7 +79,7 @@ PermissionedDomainSet::preclaim(PreclaimContext const& ctx)
|
||||
TER
|
||||
PermissionedDomainSet::doApply()
|
||||
{
|
||||
auto const ownerSle = view().peek(keylet::account(accountID_));
|
||||
AccountRootEntry<ApplyView> const ownerSle{keylet::account(accountID_), view()};
|
||||
if (!ownerSle)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -97,39 +97,30 @@ PermissionedDomainSet::doApply()
|
||||
if (ctx_.tx.isFieldPresent(sfDomainID))
|
||||
{
|
||||
// Modify existing permissioned domain.
|
||||
auto slePd = view().peek(keylet::permissionedDomain(ctx_.tx.getFieldH256(sfDomainID)));
|
||||
PermissionedDomainEntry<ApplyView> slePd{
|
||||
keylet::permissionedDomain(ctx_.tx.getFieldH256(sfDomainID)), view()};
|
||||
if (!slePd)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
slePd->peekFieldArray(sfAcceptedCredentials) = std::move(sortedLE);
|
||||
view().update(slePd);
|
||||
slePd.update();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create new permissioned domain.
|
||||
// Check reserve availability for new object creation
|
||||
auto const balance = STAmount((*ownerSle)[sfBalance]).xrp();
|
||||
auto const reserve =
|
||||
accountReserve(ctx_.view(), ownerSle, ctx_.journal, {.ownerCountDelta = 1});
|
||||
if (balance < reserve)
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
|
||||
bool const fixEnabled = view().rules().enabled(fixCleanup3_1_3);
|
||||
auto const seq = fixEnabled ? ctx_.tx.getSeqValue() : ctx_.tx.getFieldU32(sfSequence);
|
||||
Keylet const pdKeylet = keylet::permissionedDomain(accountID_, seq);
|
||||
auto slePd = std::make_shared<SLE>(pdKeylet);
|
||||
// Adopt a fresh SLE (rather than peek + newSLE) so that the pre-
|
||||
// fixCleanup3_1_3 ticket case, where sfSequence is 0 and the keylet can
|
||||
// collide with an existing domain, throws at insert() (-> tefEXCEPTION)
|
||||
// instead of tripping the newSLE() "no existing SLE" assertion.
|
||||
PermissionedDomainEntry<ApplyView> slePd{std::make_shared<SLE>(pdKeylet), view()};
|
||||
|
||||
slePd->setAccountID(sfOwner, accountID_);
|
||||
slePd->setFieldU32(sfSequence, seq);
|
||||
slePd->peekFieldArray(sfAcceptedCredentials) = std::move(sortedLE);
|
||||
auto const page =
|
||||
view().dirInsert(keylet::ownerDir(accountID_), pdKeylet, describeOwnerDir(accountID_));
|
||||
if (!page)
|
||||
return tecDIR_FULL; // LCOV_EXCL_LINE
|
||||
|
||||
slePd->setFieldU64(sfOwnerNode, *page);
|
||||
// If we succeeded, the new entry counts against the creator's reserve.
|
||||
increaseOwnerCount(view(), ownerSle, {}, 1, ctx_.journal);
|
||||
view().insert(slePd);
|
||||
return slePd.create(preFeeBalance_);
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
|
||||
@@ -8,8 +8,9 @@
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/AmendmentTable.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
#include <xrpl/protocol/PublicKey.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
@@ -25,7 +26,6 @@
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
@@ -162,14 +162,12 @@ Change::applyAmendment()
|
||||
{
|
||||
uint256 const amendment(ctx_.tx.getFieldH256(sfAmendment));
|
||||
|
||||
auto const k = keylet::amendments();
|
||||
|
||||
SLE::pointer amendmentObject = view().peek(k);
|
||||
AmendmentsEntry<ApplyView> amendmentObject{view()};
|
||||
|
||||
if (!amendmentObject)
|
||||
{
|
||||
amendmentObject = std::make_shared<SLE>(k);
|
||||
view().insert(amendmentObject);
|
||||
amendmentObject.newSLE();
|
||||
amendmentObject.insert();
|
||||
}
|
||||
|
||||
STVector256 amendments = amendmentObject->getFieldV256(sfAmendments);
|
||||
@@ -246,7 +244,7 @@ Change::applyAmendment()
|
||||
amendmentObject->setFieldArray(sfMajorities, newMajorities);
|
||||
}
|
||||
|
||||
view().update(amendmentObject);
|
||||
amendmentObject.update();
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
@@ -254,16 +252,14 @@ Change::applyAmendment()
|
||||
TER
|
||||
Change::applyFee()
|
||||
{
|
||||
auto const k = keylet::feeSettings();
|
||||
|
||||
SLE::pointer feeObject = view().peek(k);
|
||||
FeeSettingsEntry<ApplyView> feeObject{view()};
|
||||
|
||||
if (!feeObject)
|
||||
{
|
||||
feeObject = std::make_shared<SLE>(k);
|
||||
view().insert(feeObject);
|
||||
feeObject.newSLE();
|
||||
feeObject.insert();
|
||||
}
|
||||
auto set = [](SLE::pointer& feeObject, STTx const& tx, auto const& field) {
|
||||
auto set = [](auto& feeObject, STTx const& tx, auto const& field) {
|
||||
feeObject->at(field) = tx[field];
|
||||
};
|
||||
if (view().rules().enabled(featureXRPFees))
|
||||
@@ -285,7 +281,7 @@ Change::applyFee()
|
||||
set(feeObject, ctx_.tx, sfReserveIncrement);
|
||||
}
|
||||
|
||||
view().update(feeObject);
|
||||
feeObject.update();
|
||||
|
||||
JLOG(j_.warn()) << "Fees have been changed";
|
||||
return tesSUCCESS;
|
||||
@@ -326,12 +322,11 @@ Change::applyUNLModify()
|
||||
JLOG(j_.info()) << "N-UNL: applyUNLModify, " << (disabling ? "ToDisable" : "ToReEnable")
|
||||
<< " seq=" << seq << " validator data:" << strHex(validator);
|
||||
|
||||
auto const k = keylet::negativeUNL();
|
||||
SLE::pointer negUnlObject = view().peek(k);
|
||||
NegativeUNLEntry<ApplyView> negUnlObject{view()};
|
||||
if (!negUnlObject)
|
||||
{
|
||||
negUnlObject = std::make_shared<SLE>(k);
|
||||
view().insert(negUnlObject);
|
||||
negUnlObject.newSLE();
|
||||
negUnlObject.insert();
|
||||
}
|
||||
|
||||
bool const found = [&] {
|
||||
@@ -404,7 +399,7 @@ Change::applyUNLModify()
|
||||
negUnlObject->setFieldVL(sfValidatorToReEnable, validator);
|
||||
}
|
||||
|
||||
view().update(negUnlObject);
|
||||
negUnlObject.update();
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
#include <xrpl/tx/transactors/system/TicketCreate.h>
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Keylet.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
@@ -67,22 +65,11 @@ TicketCreate::preclaim(PreclaimContext const& ctx)
|
||||
TER
|
||||
TicketCreate::doApply()
|
||||
{
|
||||
SLE::pointer const sleAccountRoot = view().peek(keylet::account(accountID_));
|
||||
AccountRootEntry<ApplyView> sleAccountRoot{keylet::account(accountID_), view()};
|
||||
if (!sleAccountRoot)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
// Each ticket counts against the reserve of the issuing account, but we
|
||||
// check the starting balance because we want to allow dipping into the
|
||||
// reserve to pay fees.
|
||||
std::uint32_t const ticketCount = ctx_.tx[sfTicketCount];
|
||||
if (preFeeBalance_ < accountReserve(
|
||||
view(),
|
||||
sleAccountRoot,
|
||||
j_,
|
||||
{.ownerCountDelta = static_cast<std::int32_t>(ticketCount)}))
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
|
||||
beast::Journal const viewJ{ctx_.registry.get().getJournal("View")};
|
||||
|
||||
// The starting ticket sequence is the same as the current account
|
||||
// root sequence. Before we got here to doApply(), the transaction
|
||||
@@ -100,23 +87,20 @@ TicketCreate::doApply()
|
||||
{
|
||||
std::uint32_t const curTicketSeq = firstTicketSeq + i;
|
||||
Keylet const ticketKeylet = keylet::ticket(accountID_, curTicketSeq);
|
||||
SLE::pointer const sleTicket = std::make_shared<SLE>(ticketKeylet);
|
||||
TicketEntry<ApplyView> sleTicket{ticketKeylet, view()};
|
||||
sleTicket.newSLE();
|
||||
|
||||
sleTicket->setAccountID(sfAccount, accountID_);
|
||||
sleTicket->setFieldU32(sfTicketSequence, curTicketSeq);
|
||||
|
||||
view().insert(sleTicket);
|
||||
|
||||
auto const page = view().dirInsert(
|
||||
keylet::ownerDir(accountID_), ticketKeylet, describeOwnerDir(accountID_));
|
||||
|
||||
JLOG(j_.trace()) << "Creating ticket " << to_string(ticketKeylet.key) << ": "
|
||||
<< (page ? "success" : "failure");
|
||||
|
||||
if (!page)
|
||||
return tecDIR_FULL; // LCOV_EXCL_LINE
|
||||
|
||||
sleTicket->setFieldU64(sfOwnerNode, *page);
|
||||
// Each ticket counts against the reserve of the issuing account, but
|
||||
// the reserve is checked against the starting balance (preFeeBalance_)
|
||||
// because we want to allow dipping into the reserve to pay fees. This
|
||||
// reserve check + owner directory link + OwnerCount bump is handled by
|
||||
// create(). The final ticket's create() enforces the reserve for the
|
||||
// full ticketCount, since OwnerCount grows with each iteration.
|
||||
if (auto const ter = sleTicket.create(preFeeBalance_); !isTesSuccess(ter))
|
||||
return ter;
|
||||
}
|
||||
|
||||
// Update the record of the number of Tickets this account owns.
|
||||
@@ -124,9 +108,6 @@ TicketCreate::doApply()
|
||||
|
||||
sleAccountRoot->setFieldU32(sfTicketCount, oldTicketCount + ticketCount);
|
||||
|
||||
// Every added Ticket counts against the creator's reserve.
|
||||
increaseOwnerCount(view(), sleAccountRoot, {}, ticketCount, viewJ);
|
||||
|
||||
// TicketCreate is the only transaction that can cause an account root's
|
||||
// Sequence field to increase by more than one. October 2018.
|
||||
sleAccountRoot->setFieldU32(sfSequence, firstTicketSeq + ticketCount);
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
#include <xrpl/tx/transactors/token/MPTokenAuthorize.h>
|
||||
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
@@ -46,8 +48,8 @@ MPTokenAuthorize::preclaim(PreclaimContext const& ctx)
|
||||
// `holderID` is NOT used
|
||||
if (!holderID)
|
||||
{
|
||||
SLE::const_pointer const sleMpt =
|
||||
ctx.view.read(keylet::mptoken(ctx.tx[sfMPTokenIssuanceID], accountID));
|
||||
MPTokenEntry<ReadView> const sleMpt{
|
||||
keylet::mptoken(ctx.tx[sfMPTokenIssuanceID], accountID), ctx.view};
|
||||
|
||||
// There is an edge case where all holders have zero balance, issuance
|
||||
// is legally destroyed, then outstanding MPT(s) are deleted afterwards.
|
||||
|
||||
@@ -5,9 +5,7 @@
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SponsorHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
@@ -119,83 +117,63 @@ MPTokenIssuanceCreate::create(
|
||||
beast::Journal journal,
|
||||
MPTCreateArgs const& args)
|
||||
{
|
||||
auto const acct = ctx.view.peek(keylet::account(args.account));
|
||||
if (!acct)
|
||||
if (!ctx.view.exists(keylet::account(args.account)))
|
||||
return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE
|
||||
|
||||
// A reserve sponsor only covers tx.Account's own objects.
|
||||
auto const sponsorExp = getEffectiveTxReserveSponsor(ctx, acct);
|
||||
if (!sponsorExp)
|
||||
return std::unexpected(sponsorExp.error()); // LCOV_EXCL_LINE
|
||||
auto const sponsorSle = *sponsorExp;
|
||||
|
||||
if (args.priorBalance)
|
||||
{
|
||||
if (auto const ret = checkReserve(
|
||||
ctx, acct, *(args.priorBalance), sponsorSle, {.ownerCountDelta = 1}, journal);
|
||||
!isTesSuccess(ret))
|
||||
return std::unexpected(ret);
|
||||
}
|
||||
|
||||
auto const mptId = makeMptID(args.sequence, args.account);
|
||||
auto const mptIssuanceKeylet = keylet::mptokenIssuance(mptId);
|
||||
|
||||
// create the MPTokenIssuance
|
||||
// create the MPTokenIssuance. Build with the ApplyViewContext so create()
|
||||
// performs reserve-sponsorship-aware accounting (a no-op when the issuer is
|
||||
// a pseudo-account, e.g. VaultCreate).
|
||||
MPTokenIssuanceEntry<ApplyView> mptIssuance{mptIssuanceKeylet, ctx, journal};
|
||||
mptIssuance.newSLE();
|
||||
(*mptIssuance)[sfFlags] = args.flags & ~tfUniversal;
|
||||
(*mptIssuance)[sfIssuer] = args.account;
|
||||
(*mptIssuance)[sfOutstandingAmount] = 0;
|
||||
(*mptIssuance)[sfSequence] = args.sequence;
|
||||
|
||||
if (args.maxAmount)
|
||||
(*mptIssuance)[sfMaximumAmount] = *args.maxAmount;
|
||||
|
||||
if (args.assetScale)
|
||||
(*mptIssuance)[sfAssetScale] = *args.assetScale;
|
||||
|
||||
if (args.transferFee)
|
||||
(*mptIssuance)[sfTransferFee] = *args.transferFee;
|
||||
|
||||
if (args.metadata)
|
||||
(*mptIssuance)[sfMPTokenMetadata] = *args.metadata;
|
||||
|
||||
if (args.domainId)
|
||||
(*mptIssuance)[sfDomainID] = *args.domainId;
|
||||
|
||||
if (args.mutableFlags)
|
||||
(*mptIssuance)[sfMutableFlags] = *args.mutableFlags;
|
||||
|
||||
if (args.referenceHolding)
|
||||
{
|
||||
auto const ownerNode = ctx.view.dirInsert(
|
||||
keylet::ownerDir(args.account), mptIssuanceKeylet, describeOwnerDir(args.account));
|
||||
|
||||
if (!ownerNode)
|
||||
return std::unexpected(tecDIR_FULL); // LCOV_EXCL_LINE
|
||||
|
||||
auto mptIssuance = std::make_shared<SLE>(mptIssuanceKeylet);
|
||||
(*mptIssuance)[sfFlags] = args.flags & ~tfUniversal;
|
||||
(*mptIssuance)[sfIssuer] = args.account;
|
||||
(*mptIssuance)[sfOutstandingAmount] = 0;
|
||||
(*mptIssuance)[sfOwnerNode] = *ownerNode;
|
||||
(*mptIssuance)[sfSequence] = args.sequence;
|
||||
|
||||
if (args.maxAmount)
|
||||
(*mptIssuance)[sfMaximumAmount] = *args.maxAmount;
|
||||
|
||||
if (args.assetScale)
|
||||
(*mptIssuance)[sfAssetScale] = *args.assetScale;
|
||||
|
||||
if (args.transferFee)
|
||||
(*mptIssuance)[sfTransferFee] = *args.transferFee;
|
||||
|
||||
if (args.metadata)
|
||||
(*mptIssuance)[sfMPTokenMetadata] = *args.metadata;
|
||||
|
||||
if (args.domainId)
|
||||
(*mptIssuance)[sfDomainID] = *args.domainId;
|
||||
|
||||
if (args.mutableFlags)
|
||||
(*mptIssuance)[sfMutableFlags] = *args.mutableFlags;
|
||||
|
||||
if (args.referenceHolding)
|
||||
{
|
||||
// Defensive: the holding must already exist and be of an
|
||||
// expected type. Callers (currently only VaultCreate)
|
||||
// populate this after the pseudo-account's MPToken /
|
||||
// RippleState has been installed. A missing holding here
|
||||
// would dangle the pointer and is a programmer error.
|
||||
auto const sleHolding = ctx.view.read(keylet::unchecked(*args.referenceHolding));
|
||||
if (!sleHolding)
|
||||
return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE
|
||||
auto const type = sleHolding->getType();
|
||||
if (type != ltMPTOKEN && type != ltRIPPLE_STATE)
|
||||
return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE
|
||||
(*mptIssuance)[sfReferenceHolding] = *args.referenceHolding;
|
||||
}
|
||||
|
||||
addSponsorToLedgerEntry(mptIssuance, sponsorSle);
|
||||
|
||||
ctx.view.insert(mptIssuance);
|
||||
// Defensive: the holding must already exist and be of an
|
||||
// expected type. Callers (currently only VaultCreate)
|
||||
// populate this after the pseudo-account's MPToken /
|
||||
// RippleState has been installed. A missing holding here
|
||||
// would dangle the pointer and is a programmer error.
|
||||
auto const sleHolding = ctx.view.read(keylet::unchecked(*args.referenceHolding));
|
||||
if (!sleHolding)
|
||||
return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE
|
||||
auto const type = sleHolding->getType();
|
||||
if (type != ltMPTOKEN && type != ltRIPPLE_STATE)
|
||||
return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE
|
||||
(*mptIssuance)[sfReferenceHolding] = *args.referenceHolding;
|
||||
}
|
||||
|
||||
// Update owner count.
|
||||
increaseOwnerCount(ctx.view, acct, sponsorSle, 1, journal);
|
||||
// Reserve check against the issuer's pre-fee balance (skipped when
|
||||
// priorBalance is std::nullopt, i.e. VaultCreate's pseudo-account, and
|
||||
// honoring any reserve sponsor) + link into the issuer's owner directory +
|
||||
// bump the issuer's OwnerCount + stamp the reserve sponsor + insert. See
|
||||
// MPTokenIssuanceEntry and SLEBase::create().
|
||||
if (auto const ter = mptIssuance.create(args.priorBalance); !isTesSuccess(ter))
|
||||
return std::unexpected(ter);
|
||||
|
||||
return mptId;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include <xrpl/tx/transactors/token/MPTokenIssuanceDestroy.h>
|
||||
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
@@ -42,17 +44,12 @@ MPTokenIssuanceDestroy::preclaim(PreclaimContext const& ctx)
|
||||
TER
|
||||
MPTokenIssuanceDestroy::doApply()
|
||||
{
|
||||
auto const mpt = view().peek(keylet::mptokenIssuance(ctx_.tx[sfMPTokenIssuanceID]));
|
||||
MPTokenIssuanceEntry<ApplyView> mpt{
|
||||
keylet::mptokenIssuance(ctx_.tx[sfMPTokenIssuanceID]), view()};
|
||||
if (accountID_ != mpt->getAccountID(sfIssuer))
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
if (!view().dirRemove(keylet::ownerDir(accountID_), (*mpt)[sfOwnerNode], mpt->key(), false))
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
|
||||
decreaseOwnerCountForObject(view(), accountID_, mpt, 1, j_);
|
||||
view().erase(mpt);
|
||||
|
||||
return tesSUCCESS;
|
||||
return mpt.destroy();
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/SLEBase.h>
|
||||
#include <xrpl/protocol/ConfidentialTransfer.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
@@ -301,16 +302,10 @@ MPTokenIssuanceSet::doApply()
|
||||
auto const mptIssuanceID = ctx_.tx[sfMPTokenIssuanceID];
|
||||
auto const holderID = ctx_.tx[~sfHolder];
|
||||
auto const domainID = ctx_.tx[~sfDomainID];
|
||||
SLE::pointer sle;
|
||||
|
||||
if (holderID)
|
||||
{
|
||||
sle = view().peek(keylet::mptoken(mptIssuanceID, *holderID));
|
||||
}
|
||||
else
|
||||
{
|
||||
sle = view().peek(keylet::mptokenIssuance(mptIssuanceID));
|
||||
}
|
||||
WritableSLE sle{
|
||||
holderID ? keylet::mptoken(mptIssuanceID, *holderID)
|
||||
: keylet::mptokenIssuance(mptIssuanceID),
|
||||
view()};
|
||||
|
||||
if (!sle)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
@@ -410,7 +405,7 @@ MPTokenIssuanceSet::doApply()
|
||||
sle->setFieldVL(sfAuditorEncryptionKey, *pubKey);
|
||||
}
|
||||
|
||||
view().update(sle);
|
||||
sle.update();
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/VaultHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
@@ -60,7 +63,7 @@ VaultClawback::preflight(PreflightContext const& ctx)
|
||||
|
||||
[[nodiscard]] STAmount
|
||||
clawbackAmount(
|
||||
SLE::const_ref vault,
|
||||
VaultEntry<ReadView> const& vault,
|
||||
std::optional<STAmount> const& maybeAmount,
|
||||
AccountID const& account)
|
||||
{
|
||||
@@ -77,7 +80,7 @@ clawbackAmount(
|
||||
TER
|
||||
VaultClawback::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
auto const vault = ctx.view.read(keylet::vault(ctx.tx[sfVaultID]));
|
||||
VaultEntry<ReadView> const vault{keylet::vault(ctx.tx[sfVaultID]), ctx.view};
|
||||
if (!vault)
|
||||
return tecNO_ENTRY;
|
||||
|
||||
@@ -86,7 +89,8 @@ VaultClawback::preclaim(PreclaimContext const& ctx)
|
||||
auto const holder = ctx.tx[sfHolder];
|
||||
auto const maybeAmount = ctx.tx[~sfAmount];
|
||||
auto const mptIssuanceID = vault->at(sfShareMPTID);
|
||||
auto const sleShareIssuance = ctx.view.read(keylet::mptokenIssuance(mptIssuanceID));
|
||||
MPTokenIssuanceEntry<ReadView> const sleShareIssuance{
|
||||
keylet::mptokenIssuance(mptIssuanceID), ctx.view};
|
||||
if (!sleShareIssuance)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
@@ -180,8 +184,9 @@ VaultClawback::preclaim(PreclaimContext const& ctx)
|
||||
|
||||
return vaultAsset.visit(
|
||||
[&](MPTIssue const& issue) -> TER {
|
||||
auto const mptIssue = ctx.view.read(keylet::mptokenIssuance(issue.getMptID()));
|
||||
if (mptIssue == nullptr)
|
||||
MPTokenIssuanceEntry<ReadView> const mptIssue{
|
||||
keylet::mptokenIssuance(issue.getMptID()), ctx.view};
|
||||
if (!mptIssue)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
if (!mptIssue->isFlag(lsfMPTCanClawback))
|
||||
@@ -194,7 +199,7 @@ VaultClawback::preclaim(PreclaimContext const& ctx)
|
||||
return tesSUCCESS;
|
||||
},
|
||||
[&](Issue const&) -> TER {
|
||||
auto const issuerSle = ctx.view.read(keylet::account(account));
|
||||
AccountRootEntry<ReadView> const issuerSle{keylet::account(account), ctx.view};
|
||||
if (!issuerSle)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
@@ -220,8 +225,8 @@ VaultClawback::preclaim(PreclaimContext const& ctx)
|
||||
|
||||
std::expected<std::pair<STAmount, STAmount>, TER>
|
||||
VaultClawback::assetsToClawback(
|
||||
SLE::ref vault,
|
||||
SLE::const_ref sleShareIssuance,
|
||||
VaultEntry<ApplyView>& vault,
|
||||
MPTokenIssuanceEntry<ReadView> const& sleShareIssuance,
|
||||
AccountID const& holder,
|
||||
STAmount const& clawbackAmount)
|
||||
{
|
||||
@@ -332,12 +337,13 @@ TER
|
||||
VaultClawback::doApply()
|
||||
{
|
||||
auto const& tx = ctx_.tx;
|
||||
auto const vault = view().peek(keylet::vault(tx[sfVaultID]));
|
||||
VaultEntry<ApplyView> vault{keylet::vault(tx[sfVaultID]), view()};
|
||||
if (!vault)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const mptIssuanceID = *vault->at(sfShareMPTID);
|
||||
auto const sleIssuance = view().read(keylet::mptokenIssuance(mptIssuanceID));
|
||||
MPTokenIssuanceEntry<ReadView> const sleIssuance{
|
||||
keylet::mptokenIssuance(mptIssuanceID), view()};
|
||||
if (!sleIssuance)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
@@ -385,7 +391,7 @@ VaultClawback::doApply()
|
||||
|
||||
assetsTotal -= assetsRecovered;
|
||||
assetsAvailable -= assetsRecovered;
|
||||
view().update(vault);
|
||||
vault.update();
|
||||
|
||||
auto const& vaultAccount = vault->at(sfAccount);
|
||||
// Transfer shares from holder to vault.
|
||||
|
||||
@@ -4,9 +4,10 @@
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
@@ -148,18 +149,12 @@ VaultCreate::doApply()
|
||||
auto const& tx = ctx_.tx;
|
||||
auto applyViewContext = ctx_.getApplyViewContext();
|
||||
auto const sequence = tx.getSeqValue();
|
||||
auto const owner = view().peek(keylet::account(accountID_));
|
||||
if (owner == nullptr)
|
||||
AccountRootEntry<ApplyView> const owner{keylet::account(accountID_), view()};
|
||||
if (!owner)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto vault = std::make_shared<SLE>(keylet::vault(accountID_, sequence));
|
||||
|
||||
if (auto ter = dirLink(view(), accountID_, vault))
|
||||
return ter;
|
||||
// We will create Vault and PseudoAccount, hence increase OwnerCount by 2
|
||||
increaseOwnerCount(view(), owner, {}, 2, j_);
|
||||
if (preFeeBalance_ < accountReserve(view(), owner, j_))
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
VaultEntry<ApplyView> vault{keylet::vault(accountID_, sequence), view(), j_};
|
||||
vault.newSLE();
|
||||
|
||||
auto maybePseudo = createPseudoAccount(view(), vault->key(), sfVaultID);
|
||||
if (!maybePseudo)
|
||||
@@ -241,7 +236,12 @@ VaultCreate::doApply()
|
||||
}
|
||||
if (scale != 0u)
|
||||
vault->at(sfScale) = scale;
|
||||
view().insert(vault);
|
||||
|
||||
// Reserve check (owner's pre-fee balance, charging two slots for the vault
|
||||
// and its pseudo-account) + link into the owner's directory + bump the
|
||||
// owner's OwnerCount by 2 + insert. See VaultEntry.
|
||||
if (auto const ter = vault.create(preFeeBalance_); !isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
// Explicitly create MPToken for the vault owner
|
||||
if (auto const err = authorizeMPToken(
|
||||
|
||||
@@ -3,8 +3,11 @@
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
@@ -42,7 +45,7 @@ VaultDelete::preflight(PreflightContext const& ctx)
|
||||
TER
|
||||
VaultDelete::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
auto const vault = ctx.view.read(keylet::vault(ctx.tx[sfVaultID]));
|
||||
VaultEntry<ReadView> const vault{keylet::vault(ctx.tx[sfVaultID]), ctx.view};
|
||||
if (!vault)
|
||||
return tecNO_ENTRY;
|
||||
|
||||
@@ -65,7 +68,8 @@ VaultDelete::preclaim(PreclaimContext const& ctx)
|
||||
}
|
||||
|
||||
// Verify we can destroy MPTokenIssuance
|
||||
auto const sleMPT = ctx.view.read(keylet::mptokenIssuance(vault->at(sfShareMPTID)));
|
||||
MPTokenIssuanceEntry<ReadView> const sleMPT{
|
||||
keylet::mptokenIssuance(vault->at(sfShareMPTID)), ctx.view};
|
||||
|
||||
if (!sleMPT)
|
||||
{
|
||||
@@ -95,7 +99,7 @@ VaultDelete::preclaim(PreclaimContext const& ctx)
|
||||
TER
|
||||
VaultDelete::doApply()
|
||||
{
|
||||
auto const vault = view().peek(keylet::vault(ctx_.tx[sfVaultID]));
|
||||
VaultEntry<ApplyView> vault{keylet::vault(ctx_.tx[sfVaultID]), view()};
|
||||
auto applyViewContext = ctx_.getApplyViewContext();
|
||||
if (!vault)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
@@ -108,7 +112,7 @@ VaultDelete::doApply()
|
||||
return ter;
|
||||
|
||||
auto const& pseudoID = vault->at(sfAccount);
|
||||
auto const pseudoAcct = view().peek(keylet::account(pseudoID));
|
||||
AccountRootEntry<ApplyView> const pseudoAcct{keylet::account(pseudoID), view()};
|
||||
if (!pseudoAcct)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
@@ -120,7 +124,7 @@ VaultDelete::doApply()
|
||||
// Destroy the share issuance. Do not use MPTokenIssuanceDestroy for this,
|
||||
// no special logic needed. First run few checks, duplicated from preclaim.
|
||||
auto const shareMPTID = *vault->at(sfShareMPTID);
|
||||
auto const mpt = view().peek(keylet::mptokenIssuance(shareMPTID));
|
||||
MPTokenIssuanceEntry<ApplyView> mpt{keylet::mptokenIssuance(shareMPTID), view()};
|
||||
if (!mpt)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
@@ -130,7 +134,7 @@ VaultDelete::doApply()
|
||||
}
|
||||
|
||||
// Try to remove MPToken for vault shares for the vault owner if it exists.
|
||||
if (auto const mptoken = view().peek(keylet::mptoken(shareMPTID, accountID_)))
|
||||
if (MPTokenEntry<ApplyView> const mptoken{keylet::mptoken(shareMPTID, accountID_), view()})
|
||||
{
|
||||
if (auto const ter =
|
||||
removeEmptyHolding(applyViewContext, accountID_, MPTIssue(shareMPTID), j_);
|
||||
@@ -154,16 +158,16 @@ VaultDelete::doApply()
|
||||
return tefBAD_LEDGER;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
decreaseOwnerCountForObject(view(), pseudoAcct, mpt, 1, j_);
|
||||
decreaseOwnerCountForObject(view(), pseudoAcct.mutableSle(), mpt.mutableSle(), 1, j_);
|
||||
|
||||
view().erase(mpt);
|
||||
mpt.erase();
|
||||
|
||||
// The pseudo-account's directory should have been deleted already.
|
||||
if (view().peek(keylet::ownerDir(pseudoID)))
|
||||
if (DirectoryNodeEntry<ApplyView>{keylet::ownerDir(pseudoID), view()})
|
||||
return tecHAS_OBLIGATIONS; // LCOV_EXCL_LINE
|
||||
|
||||
// Destroy the pseudo-account.
|
||||
auto vaultPseudoSLE = view().peek(keylet::account(pseudoID));
|
||||
AccountRootEntry<ApplyView> vaultPseudoSLE{keylet::account(pseudoID), view()};
|
||||
if (!vaultPseudoSLE || vaultPseudoSLE->at(~sfVaultID) != vault->key())
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -191,34 +195,12 @@ VaultDelete::doApply()
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
view().erase(vaultPseudoSLE);
|
||||
vaultPseudoSLE.erase();
|
||||
|
||||
// Remove the vault from its owner's directory.
|
||||
auto const ownerID = vault->at(sfOwner);
|
||||
if (!view().dirRemove(keylet::ownerDir(ownerID), vault->at(sfOwnerNode), vault->key(), false))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j_.error()) << "VaultDelete: failed to delete vault object.";
|
||||
return tefBAD_LEDGER;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
auto const owner = view().peek(keylet::account(ownerID));
|
||||
if (!owner)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j_.error()) << "VaultDelete: missing vault owner account.";
|
||||
return tefBAD_LEDGER;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
// We are destroying Vault and PseudoAccount, hence decrease by 2
|
||||
decreaseOwnerCountForObject(view(), owner, vault, 2, j_);
|
||||
|
||||
// Destroy the vault.
|
||||
view().erase(vault);
|
||||
|
||||
return tesSUCCESS;
|
||||
// Unlink the vault from its owner's directory, decrement the owner's
|
||||
// OwnerCount by 2 (the vault and its now-destroyed pseudo-account, refunding
|
||||
// any reserve sponsor), and erase it. See VaultEntry.
|
||||
return vault.destroy();
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/CredentialHelpers.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/VaultHelpers.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
@@ -30,7 +32,7 @@ namespace xrpl {
|
||||
|
||||
[[nodiscard]]
|
||||
static STAmount
|
||||
roundToVaultScale(STAmount const& amount, SLE::const_ref vault)
|
||||
roundToVaultScale(STAmount const& amount, VaultEntry<ReadView> const& vault)
|
||||
{
|
||||
XRPL_ASSERT(vault && vault->getType() == ltVAULT, "xrpl::roundToVaultScale : valid vault sle");
|
||||
XRPL_ASSERT(
|
||||
@@ -67,7 +69,7 @@ VaultDeposit::preclaim(PreclaimContext const& ctx)
|
||||
auto const fix320Enabled = ctx.view.rules().enabled(fixCleanup3_2_0);
|
||||
auto const fix330Enabled = ctx.view.rules().enabled(fixCleanup3_3_0);
|
||||
|
||||
auto const vault = ctx.view.read(keylet::vault(ctx.tx[sfVaultID]));
|
||||
VaultEntry<ReadView> const vault{keylet::vault(ctx.tx[sfVaultID]), ctx.view};
|
||||
if (!vault)
|
||||
return tecNO_ENTRY;
|
||||
|
||||
@@ -94,7 +96,8 @@ VaultDeposit::preclaim(PreclaimContext const& ctx)
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
auto const sleIssuance = ctx.view.read(keylet::mptokenIssuance(mptIssuanceID));
|
||||
MPTokenIssuanceEntry<ReadView> const sleIssuance{
|
||||
keylet::mptokenIssuance(mptIssuanceID), ctx.view};
|
||||
if (!sleIssuance)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
@@ -196,7 +199,7 @@ TER
|
||||
VaultDeposit::doApply()
|
||||
{
|
||||
bool const fix320Enabled = view().rules().enabled(fixCleanup3_2_0);
|
||||
auto const vault = view().peek(keylet::vault(ctx_.tx[sfVaultID]));
|
||||
VaultEntry<ApplyView> vault{keylet::vault(ctx_.tx[sfVaultID]), view()};
|
||||
auto applyViewContext = ctx_.getApplyViewContext();
|
||||
if (!vault)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
@@ -218,7 +221,8 @@ VaultDeposit::doApply()
|
||||
|
||||
// Make sure the depositor can hold shares.
|
||||
auto const mptIssuanceID = (*vault)[sfShareMPTID];
|
||||
auto const sleIssuance = view().read(keylet::mptokenIssuance(mptIssuanceID));
|
||||
MPTokenIssuanceEntry<ReadView> const sleIssuance{
|
||||
keylet::mptokenIssuance(mptIssuanceID), view()};
|
||||
if (!sleIssuance)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
@@ -316,7 +320,7 @@ VaultDeposit::doApply()
|
||||
|
||||
vault->at(sfAssetsTotal) += assetsDeposited;
|
||||
vault->at(sfAssetsAvailable) += assetsDeposited;
|
||||
view().update(vault);
|
||||
vault.update();
|
||||
|
||||
// A deposit must not push the vault over its limit.
|
||||
auto const maximum = *vault->at(sfAssetsMaximum);
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
@@ -63,7 +66,7 @@ VaultSet::preflight(PreflightContext const& ctx)
|
||||
TER
|
||||
VaultSet::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
auto const vault = ctx.view.read(keylet::vault(ctx.tx[sfVaultID]));
|
||||
VaultEntry<ReadView> const vault{keylet::vault(ctx.tx[sfVaultID]), ctx.view};
|
||||
if (!vault)
|
||||
return tecNO_ENTRY;
|
||||
|
||||
@@ -75,7 +78,8 @@ VaultSet::preclaim(PreclaimContext const& ctx)
|
||||
}
|
||||
|
||||
auto const mptIssuanceID = (*vault)[sfShareMPTID];
|
||||
auto const sleIssuance = ctx.view.read(keylet::mptokenIssuance(mptIssuanceID));
|
||||
MPTokenIssuanceEntry<ReadView> const sleIssuance{
|
||||
keylet::mptokenIssuance(mptIssuanceID), ctx.view};
|
||||
if (!sleIssuance)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
@@ -95,7 +99,8 @@ VaultSet::preclaim(PreclaimContext const& ctx)
|
||||
|
||||
if (*domain != beast::kZero)
|
||||
{
|
||||
auto const sleDomain = ctx.view.read(keylet::permissionedDomain(*domain));
|
||||
PermissionedDomainEntry<ReadView> const sleDomain{
|
||||
keylet::permissionedDomain(*domain), ctx.view};
|
||||
if (!sleDomain)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
}
|
||||
@@ -123,14 +128,14 @@ VaultSet::doApply()
|
||||
auto const& tx = ctx_.tx;
|
||||
|
||||
// Update existing object.
|
||||
auto vault = view().peek(keylet::vault(tx[sfVaultID]));
|
||||
VaultEntry<ApplyView> vault{keylet::vault(tx[sfVaultID]), view()};
|
||||
if (!vault)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const vaultAsset = vault->at(sfAsset);
|
||||
|
||||
auto const mptIssuanceID = (*vault)[sfShareMPTID];
|
||||
auto const sleIssuance = view().peek(keylet::mptokenIssuance(mptIssuanceID));
|
||||
MPTokenIssuanceEntry<ApplyView> sleIssuance{keylet::mptokenIssuance(mptIssuanceID), view()};
|
||||
if (!sleIssuance)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
@@ -164,13 +169,13 @@ VaultSet::doApply()
|
||||
{
|
||||
sleIssuance->makeFieldAbsent(sfDomainID);
|
||||
}
|
||||
view().update(sleIssuance);
|
||||
sleIssuance.update();
|
||||
}
|
||||
|
||||
// Note, we must update Vault object even if only DomainID is being updated
|
||||
// in Issuance object. Otherwise it's really difficult for Vault invariants
|
||||
// to verify the operation.
|
||||
view().update(vault);
|
||||
vault.update();
|
||||
|
||||
associateAsset(*vault, vaultAsset);
|
||||
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/VaultHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
@@ -28,7 +30,10 @@
|
||||
namespace xrpl {
|
||||
|
||||
static WaiveUnrealizedLoss
|
||||
shouldWaiveWithdrawal(ReadView const& view, AccountID const& account, SLE::const_ref issuance)
|
||||
shouldWaiveWithdrawal(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
MPTokenIssuanceEntry<ReadView> const& issuance)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
issuance && issuance->getType() == ltMPTOKEN_ISSUANCE,
|
||||
@@ -69,7 +74,7 @@ VaultWithdraw::preclaim(PreclaimContext const& ctx)
|
||||
auto const fix320Enabled = ctx.view.rules().enabled(fixCleanup3_2_0);
|
||||
auto const fix330Enabled = ctx.view.rules().enabled(fixCleanup3_3_0);
|
||||
|
||||
auto const vault = ctx.view.read(keylet::vault(ctx.tx[sfVaultID]));
|
||||
VaultEntry<ReadView> const vault{keylet::vault(ctx.tx[sfVaultID]), ctx.view};
|
||||
if (!vault)
|
||||
return tecNO_ENTRY;
|
||||
|
||||
@@ -109,7 +114,8 @@ VaultWithdraw::preclaim(PreclaimContext const& ctx)
|
||||
// to the equivalent asset amount before checking withdrawal
|
||||
// limits. Pre-amendment the limit check was skipped for
|
||||
// share-denominated withdrawals.
|
||||
auto const sleIssuance = ctx.view.read(keylet::mptokenIssuance(vaultShare));
|
||||
MPTokenIssuanceEntry<ReadView> const sleIssuance{
|
||||
keylet::mptokenIssuance(vaultShare), ctx.view};
|
||||
if (!sleIssuance)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
@@ -193,13 +199,14 @@ VaultWithdraw::preclaim(PreclaimContext const& ctx)
|
||||
TER
|
||||
VaultWithdraw::doApply()
|
||||
{
|
||||
auto const vault = view().peek(keylet::vault(ctx_.tx[sfVaultID]));
|
||||
VaultEntry<ApplyView> vault{keylet::vault(ctx_.tx[sfVaultID]), view()};
|
||||
auto applyViewContext = ctx_.getApplyViewContext();
|
||||
if (!vault)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const mptIssuanceID = *((*vault)[sfShareMPTID]);
|
||||
auto const sleIssuance = view().read(keylet::mptokenIssuance(mptIssuanceID));
|
||||
MPTokenIssuanceEntry<ReadView> const sleIssuance{
|
||||
keylet::mptokenIssuance(mptIssuanceID), view()};
|
||||
if (!sleIssuance)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
@@ -347,7 +354,7 @@ VaultWithdraw::doApply()
|
||||
assetsTotal -= assetsWithdrawn;
|
||||
assetsAvailable -= assetsWithdrawn;
|
||||
}
|
||||
view().update(vault);
|
||||
vault.update();
|
||||
|
||||
auto const& vaultAccount = vault->at(sfAccount);
|
||||
|
||||
|
||||
@@ -67,9 +67,11 @@ struct DID_test : public beast::unit_test::Suite
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 0);
|
||||
|
||||
// Pay alice almost enough to make the reserve for a DID.
|
||||
env(pay(env.master, alice, drops(incReserve + 2 * baseFee - 1)));
|
||||
BEAST_EXPECT(env.balance(alice) == acctReserve + incReserve + drops(baseFee - 1));
|
||||
// Pay alice almost enough to make the reserve for a DID. The reserve is
|
||||
// checked against the pre-fee balance, so leave her one drop short of
|
||||
// the object reserve at the point the DIDSet is evaluated.
|
||||
env(pay(env.master, alice, drops(incReserve + baseFee - 1)));
|
||||
BEAST_EXPECT(env.balance(alice) == acctReserve + incReserve - drops(1));
|
||||
env.close();
|
||||
|
||||
// alice still does not have enough XRP for the reserve of a DID.
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/basics/chrono.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/LendingHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
@@ -1525,27 +1527,21 @@ public:
|
||||
for (auto const& tc : testCases)
|
||||
{
|
||||
testcase("canApplyToBrokerCover: " + tc.name);
|
||||
auto sle = std::make_shared<SLE>(ltLOAN_BROKER, uint256{1u});
|
||||
sle->at(sfCoverAvailable) = tc.coverAvailable;
|
||||
BEAST_EXPECT(
|
||||
canApplyToBrokerCover(*env.current(), sle, iou, tc.amount, env.journal, "test") ==
|
||||
tc.expected);
|
||||
auto broker = std::make_shared<SLE>(Keylet{ltLOAN_BROKER, uint256{1u}});
|
||||
broker->at(sfCoverAvailable) = tc.coverAvailable;
|
||||
LoanBrokerEntry<ReadView> const sle{broker, *env.current(), env.journal};
|
||||
BEAST_EXPECT(canApplyToBrokerCover(sle, iou, tc.amount, "test") == tc.expected);
|
||||
}
|
||||
|
||||
// Amendment off → guard is bypassed regardless of amount.
|
||||
{
|
||||
testcase("canApplyToBrokerCover: amendment disabled");
|
||||
Env const envOff{*this, testableAmendments() - fixCleanup3_2_0};
|
||||
auto sle = std::make_shared<SLE>(ltLOAN_BROKER, uint256{1u});
|
||||
sle->at(sfCoverAvailable) = Number{10};
|
||||
auto broker = std::make_shared<SLE>(Keylet{ltLOAN_BROKER, uint256{1u}});
|
||||
broker->at(sfCoverAvailable) = Number{10};
|
||||
LoanBrokerEntry<ReadView> const sle{broker, *envOff.current(), envOff.journal};
|
||||
BEAST_EXPECT(
|
||||
canApplyToBrokerCover(
|
||||
*envOff.current(),
|
||||
sle,
|
||||
iou,
|
||||
STAmount{iou, Number{0}},
|
||||
envOff.journal,
|
||||
"test") == tesSUCCESS);
|
||||
canApplyToBrokerCover(sle, iou, STAmount{iou, Number{0}}, "test") == tesSUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,9 @@
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/OpenView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
@@ -141,9 +143,9 @@ class LoanBroker_test : public beast::unit_test::Suite
|
||||
VaultInfo const& vault,
|
||||
VaultInfo const& badVault,
|
||||
std::function<jtx::JTx(jtx::JTx const&)> modifyJTx,
|
||||
std::function<void(SLE::const_ref)> checkBroker,
|
||||
std::function<void(SLE::const_ref)> changeBroker,
|
||||
std::function<void(SLE::const_ref)> checkChangedBroker)
|
||||
std::function<void(LoanBrokerEntry<ReadView> const&)> checkBroker,
|
||||
std::function<void(LoanBrokerEntry<ReadView> const&)> changeBroker,
|
||||
std::function<void(LoanBrokerEntry<ReadView> const&)> checkChangedBroker)
|
||||
{
|
||||
{
|
||||
auto const& asset = vault.asset.raw();
|
||||
@@ -221,7 +223,7 @@ class LoanBroker_test : public beast::unit_test::Suite
|
||||
BEAST_EXPECT(broker->at(sfDebtTotal) == 0);
|
||||
BEAST_EXPECT(broker->at(sfCoverAvailable) == 0);
|
||||
if (checkBroker)
|
||||
checkBroker(broker);
|
||||
checkBroker(LoanBrokerEntry<ReadView>{broker, *env.current()});
|
||||
|
||||
// if (auto const vaultSLE = env.le(keylet::vault(vault.vaultID)))
|
||||
//{
|
||||
@@ -471,14 +473,14 @@ class LoanBroker_test : public beast::unit_test::Suite
|
||||
|
||||
// Make modifications to the broker
|
||||
if (changeBroker)
|
||||
changeBroker(broker);
|
||||
changeBroker(LoanBrokerEntry<ReadView>{broker, *env.current()});
|
||||
|
||||
env.close();
|
||||
|
||||
// Check the results of modifications
|
||||
broker = env.le(keylet);
|
||||
if (BEAST_EXPECT(broker) && checkChangedBroker)
|
||||
checkChangedBroker(broker);
|
||||
checkChangedBroker(LoanBrokerEntry<ReadView>{broker, *env.current()});
|
||||
|
||||
// Verify that fields get removed when set to default values
|
||||
// Debt maximum: explicit 0
|
||||
@@ -724,7 +726,7 @@ class LoanBroker_test : public beast::unit_test::Suite
|
||||
badVault,
|
||||
// No modifications
|
||||
{},
|
||||
[&](SLE::const_ref broker) {
|
||||
[&](LoanBrokerEntry<ReadView> const& broker) {
|
||||
// Extra checks
|
||||
BEAST_EXPECT(!broker->isFieldPresent(sfManagementFeeRate));
|
||||
BEAST_EXPECT(!broker->isFieldPresent(sfCoverRateMinimum));
|
||||
@@ -737,7 +739,7 @@ class LoanBroker_test : public beast::unit_test::Suite
|
||||
|
||||
BEAST_EXPECT(env.ownerCount(alice) == aliceOriginalCount + 4);
|
||||
},
|
||||
[&](SLE::const_ref broker) {
|
||||
[&](LoanBrokerEntry<ReadView> const& broker) {
|
||||
// Modifications
|
||||
|
||||
// Update the fields
|
||||
@@ -797,7 +799,7 @@ class LoanBroker_test : public beast::unit_test::Suite
|
||||
kData(testData),
|
||||
kDebtMaximum(debtMax));
|
||||
},
|
||||
[&](SLE::const_ref broker) {
|
||||
[&](LoanBrokerEntry<ReadView> const& broker) {
|
||||
// Check the updated fields
|
||||
BEAST_EXPECT(checkVL(broker->at(sfData), testData));
|
||||
Number const expected = STAmount{vault.asset, Number(175, -1)};
|
||||
@@ -828,7 +830,7 @@ class LoanBroker_test : public beast::unit_test::Suite
|
||||
kCoverRateMinimum(TenthBips32(100)),
|
||||
kCoverRateLiquidation(TenthBips32(200)));
|
||||
},
|
||||
[&](SLE::const_ref broker) {
|
||||
[&](LoanBrokerEntry<ReadView> const& broker) {
|
||||
// Extra checks
|
||||
BEAST_EXPECT(broker->at(sfManagementFeeRate) == 123);
|
||||
BEAST_EXPECT(broker->at(sfCoverRateMinimum) == 100);
|
||||
@@ -836,14 +838,14 @@ class LoanBroker_test : public beast::unit_test::Suite
|
||||
BEAST_EXPECT(broker->at(sfDebtMaximum) == Number(9));
|
||||
BEAST_EXPECT(checkVL(broker->at(sfData), testData));
|
||||
},
|
||||
[&](SLE::const_ref broker) {
|
||||
[&](LoanBrokerEntry<ReadView> const& broker) {
|
||||
// Reset Data & Debt maximum to default values
|
||||
env(set(alice, vault.vaultID),
|
||||
kLoanBrokerId(broker->key()),
|
||||
kData(""),
|
||||
kDebtMaximum(Number(0)));
|
||||
},
|
||||
[&](SLE::const_ref broker) {
|
||||
[&](LoanBrokerEntry<ReadView> const& broker) {
|
||||
// Check the updated fields
|
||||
BEAST_EXPECT(!broker->isFieldPresent(sfData));
|
||||
BEAST_EXPECT(!broker->isFieldPresent(sfDebtMaximum));
|
||||
|
||||
@@ -37,8 +37,10 @@
|
||||
#include <xrpl/beast/xor_shift_engine.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/json/to_string.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/LendingHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SLEWrappers.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/HashPrefix.h>
|
||||
@@ -213,7 +215,7 @@ protected:
|
||||
using namespace jtx;
|
||||
|
||||
auto const vaultSle = env.le(keylet::vault(vaultID));
|
||||
return getAssetsTotalScale(vaultSle);
|
||||
return getAssetsTotalScale(VaultEntry<ReadView>{vaultSle, *env.current()});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -446,7 +448,8 @@ protected:
|
||||
env.test.BEAST_EXPECT(loan->at(sfPeriodicPayment) == periodicPayment);
|
||||
env.test.BEAST_EXPECT(loan->at(sfFlags) == flags);
|
||||
|
||||
auto const ls = constructRoundedLoanState(loan);
|
||||
auto const ls =
|
||||
constructRoundedLoanState(LoanEntry<ReadView>{loan, *env.current()});
|
||||
|
||||
auto const interestRate = TenthBips32{loan->at(sfInterestRate)};
|
||||
auto const paymentInterval = loan->at(sfPaymentInterval);
|
||||
@@ -1119,7 +1122,8 @@ protected:
|
||||
// No reason for this not to exist
|
||||
return;
|
||||
}
|
||||
auto const current = constructRoundedLoanState(loanSle);
|
||||
auto const current =
|
||||
constructRoundedLoanState(LoanEntry<ReadView>{loanSle, *env.current()});
|
||||
auto const errors = nextTrueState - current;
|
||||
log << currencyLabel << " Loan balances: "
|
||||
<< "\n\tAmount taken: " << paymentComponents.trackedValueDelta
|
||||
@@ -6056,7 +6060,8 @@ protected:
|
||||
auto const loanSle = env.le(loanKeylet);
|
||||
if (!BEAST_EXPECT(loanSle))
|
||||
return;
|
||||
auto const state = constructRoundedLoanState(loanSle);
|
||||
auto const state =
|
||||
constructRoundedLoanState(LoanEntry<ReadView>{loanSle, *env.current()});
|
||||
|
||||
log << "Loan state:" << std::endl;
|
||||
log << " ValueOutstanding: " << state.valueOutstanding << std::endl;
|
||||
@@ -8280,7 +8285,8 @@ protected:
|
||||
return std::nullopt;
|
||||
if (!BEAST_EXPECT(tinyLoanSle->at(sfLoanScale) == -12) ||
|
||||
!BEAST_EXPECT(bigLoanSle->at(sfLoanScale) == -11) ||
|
||||
!BEAST_EXPECT(getAssetsTotalScale(vaultSle) == -11))
|
||||
!BEAST_EXPECT(
|
||||
getAssetsTotalScale(VaultEntry<ReadView>{vaultSle, *env.current()}) == -11))
|
||||
return std::nullopt;
|
||||
|
||||
// Use issuer clawback to reduce cover to the minimum the
|
||||
@@ -8407,7 +8413,8 @@ protected:
|
||||
|
||||
auto const coverAvail = brokerSle->at(sfCoverAvailable);
|
||||
auto const debtTotal = brokerSle->at(sfDebtTotal);
|
||||
auto const vaultScale = getAssetsTotalScale(vaultSle);
|
||||
auto const vaultScale =
|
||||
getAssetsTotalScale(VaultEntry<ReadView>{vaultSle, *env.current()});
|
||||
auto const debtScale = scale(debtTotal, asset);
|
||||
|
||||
// Sanity: debt scale differs from vault scale for this setup.
|
||||
@@ -8421,7 +8428,9 @@ protected:
|
||||
debtScale);
|
||||
}();
|
||||
auto const newMin = minimumBrokerCover(
|
||||
debtTotal, TenthBips32{c.brokerParams.coverRateMin}, vaultSle);
|
||||
debtTotal,
|
||||
TenthBips32{c.brokerParams.coverRateMin},
|
||||
VaultEntry<ReadView>{vaultSle, *env.current()});
|
||||
|
||||
// The new (vaultScale) minimum must be strictly larger than
|
||||
// the old (debtScale) minimum — that is the gap the amendment
|
||||
@@ -8505,7 +8514,8 @@ protected:
|
||||
auto const vaultSle = env.le(keylet::vault(c.broker.vaultID));
|
||||
if (!BEAST_EXPECT(vaultSle))
|
||||
return;
|
||||
auto const vaultScale = getAssetsTotalScale(vaultSle);
|
||||
auto const vaultScale =
|
||||
getAssetsTotalScale(VaultEntry<ReadView>{vaultSle, *env.current()});
|
||||
BEAST_EXPECT(vaultScale == -11);
|
||||
|
||||
// Now try to create a tiny additional loan. Principal is
|
||||
|
||||
@@ -509,9 +509,11 @@ class PermissionedDomains_test : public beast::unit_test::Suite
|
||||
|
||||
auto const baseFee = env.current()->fees().base.drops();
|
||||
|
||||
// Pay alice almost enough to make the reserve.
|
||||
env(pay(env.master, alice, incReserve + drops(2 * baseFee) - drops(1)));
|
||||
BEAST_EXPECT(env.balance(alice) == acctReserve + incReserve + drops(baseFee) - drops(1));
|
||||
// Pay alice almost enough to make the reserve. The reserve is checked
|
||||
// against the pre-fee balance, so leave her one drop short of the object
|
||||
// reserve at the point the PermissionedDomainSet is evaluated.
|
||||
env(pay(env.master, alice, incReserve + drops(baseFee) - drops(1)));
|
||||
BEAST_EXPECT(env.balance(alice) == acctReserve + incReserve - drops(1));
|
||||
env.close();
|
||||
|
||||
// alice still does not have enough XRP for the reserve.
|
||||
|
||||
Reference in new issue
Block a user