Merge branch 'develop' into tapanito/transaction-invariant

This commit is contained in:
Vito Tumas
2026-03-31 13:46:04 +02:00
committed by GitHub
481 changed files with 7903 additions and 6976 deletions

View File

@@ -15,7 +15,7 @@
#define ALWAYS_OR_UNREACHABLE(cond, message) assert((message) && (cond))
#define SOMETIMES(cond, message, ...)
#define REACHABLE(message, ...)
#define UNREACHABLE(message, ...) assert((message) && false)
#define UNREACHABLE(message, ...) assert((message) && false) // NOLINT(misc-static-assert)
#endif
#define XRPL_ASSERT ALWAYS_OR_UNREACHABLE

View File

@@ -70,14 +70,24 @@ JobQueue::Coro::resume()
running_ = true;
}
{
std::lock_guard lock(jq_.m_mutex);
std::lock_guard lk(jq_.m_mutex);
--jq_.nSuspend_;
}
auto saved = detail::getLocalValues().release();
detail::getLocalValues().reset(&lvs_);
std::lock_guard lock(mutex_);
XRPL_ASSERT(static_cast<bool>(coro_), "xrpl::JobQueue::Coro::resume : is runnable");
coro_();
// A late resume() can arrive after the coroutine has already completed.
// This is an expected (if rare) outcome of the race condition documented
// in JobQueue.h:354-377 where post() schedules a resume job before the
// coroutine yields — the mutex serializes access, but by the time this
// resume() acquires the lock the coroutine may have already run to
// completion. Calling operator() on a completed boost::coroutine2 is
// undefined behavior, so we must check and skip invoking the coroutine
// body if it has already completed.
if (coro_)
{
coro_();
}
detail::getLocalValues().release();
detail::getLocalValues().reset(saved);
std::lock_guard lk(mutex_run_);

View File

@@ -99,8 +99,8 @@ public:
Effects:
The coroutine continues execution from where it last left off
using this same thread.
Undefined behavior if called after the coroutine has completed
with a return (as opposed to a yield()).
If the coroutine has already completed, returns immediately
(handles the documented post-before-yield race condition).
Undefined behavior if resume() or post() called consecutively
without a corresponding yield.
*/
@@ -316,7 +316,7 @@ private:
// Returns the limit of running jobs for the given job type.
// For jobs with no limit, we return the largest int. Hopefully that
// will be enough.
int
static int
getJobLimit(JobType type);
};
@@ -357,8 +357,10 @@ private:
If the post() job were to be executed before yield(), undefined behavior
would occur. The lock ensures that coro_ is not called again until we exit
the coroutine. At which point a scheduled resume() job waiting on the lock
would gain entry, harmlessly call coro_ and immediately return as we have
already completed the coroutine.
would gain entry. resume() checks if the coroutine has already completed
(coro_ converts to false) and, if so, skips invoking operator() since
calling operator() on a completed boost::coroutine2 pull_type is undefined
behavior.
The race condition occurs as follows:

View File

@@ -3,7 +3,6 @@
#include <xrpl/basics/Blob.h>
#include <xrpl/basics/SHAMapHash.h>
#include <xrpl/basics/TaggedCache.h>
#include <xrpl/ledger/CachedSLEs.h>
#include <boost/asio.hpp>
@@ -23,6 +22,20 @@ class PerfLog;
// This is temporary until we migrate all code to use ServiceRegistry.
class Application;
template <
class Key,
class T,
bool IsKeyCache,
class SharedWeakUnionPointer,
class SharedPointerType,
class Hash,
class KeyEqual,
class Mutex>
class TaggedCache;
class STLedgerEntry;
using SLE = STLedgerEntry;
using CachedSLEs = TaggedCache<uint256, SLE const>;
// Forward declarations
class AcceptedLedger;
class AmendmentTable;
@@ -45,7 +58,7 @@ class NetworkIDService;
class OpenLedger;
class OrderBookDB;
class Overlay;
class PathRequests;
class PathRequestManager;
class PeerReservationTable;
class PendingSaves;
class RelationalDatabase;
@@ -89,7 +102,7 @@ public:
getNodeFamily() = 0;
virtual TimeKeeper&
timeKeeper() = 0;
getTimeKeeper() = 0;
virtual JobQueue&
getJobQueue() = 0;
@@ -98,7 +111,7 @@ public:
getTempNodeCache() = 0;
virtual CachedSLEs&
cachedSLEs() = 0;
getCachedSLEs() = 0;
virtual NetworkIDService&
getNetworkIDService() = 0;
@@ -120,26 +133,26 @@ public:
getValidations() = 0;
virtual ValidatorList&
validators() = 0;
getValidators() = 0;
virtual ValidatorSite&
validatorSites() = 0;
getValidatorSites() = 0;
virtual ManifestCache&
validatorManifests() = 0;
getValidatorManifests() = 0;
virtual ManifestCache&
publisherManifests() = 0;
getPublisherManifests() = 0;
// Network services
virtual Overlay&
overlay() = 0;
getOverlay() = 0;
virtual Cluster&
cluster() = 0;
getCluster() = 0;
virtual PeerReservationTable&
peerReservations() = 0;
getPeerReservations() = 0;
virtual Resource::Manager&
getResourceManager() = 0;
@@ -174,13 +187,13 @@ public:
getLedgerReplayer() = 0;
virtual PendingSaves&
pendingSaves() = 0;
getPendingSaves() = 0;
virtual OpenLedger&
openLedger() = 0;
getOpenLedger() = 0;
virtual OpenLedger const&
openLedger() const = 0;
getOpenLedger() const = 0;
// Transaction and operation services
virtual NetworkOPs&
@@ -195,8 +208,8 @@ public:
virtual TxQ&
getTxQ() = 0;
virtual PathRequests&
getPathRequests() = 0;
virtual PathRequestManager&
getPathRequestManager() = 0;
// Server services
virtual ServerHandler&
@@ -210,16 +223,16 @@ public:
isStopping() const = 0;
virtual beast::Journal
journal(std::string const& name) = 0;
getJournal(std::string const& name) = 0;
virtual boost::asio::io_context&
getIOContext() = 0;
virtual Logs&
logs() = 0;
getLogs() = 0;
virtual std::optional<uint256> const&
trapTxID() const = 0;
getTrapTxID() const = 0;
/** Retrieve the "wallet database" */
virtual DatabaseCon&
@@ -228,7 +241,7 @@ public:
// Temporary: Get the underlying Application for functions that haven't
// been migrated yet. This should be removed once all code is migrated.
virtual Application&
app() = 0;
getApp() = 0;
};
} // namespace xrpl

View File

@@ -5,7 +5,7 @@
namespace xrpl {
enum class StartUpType { FRESH, NORMAL, LOAD, LOAD_FILE, REPLAY, NETWORK };
enum class StartUpType { Fresh, Normal, Load, LoadFile, Replay, Network };
inline std::ostream&
operator<<(std::ostream& os, StartUpType const& type)

View File

@@ -0,0 +1,157 @@
#pragma once
#include <xrpl/basics/CountedObject.h>
#include <xrpl/protocol/RippleLedgerHash.h>
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/SeqProxy.h>
namespace xrpl {
/** Holds transactions which were deferred to the next pass of consensus.
"Canonical" refers to the order in which transactions are applied.
- Puts transactions from the same account in SeqProxy order
*/
// VFALCO TODO rename to SortedTxSet
class CanonicalTXSet : public CountedObject<CanonicalTXSet>
{
private:
class Key
{
public:
Key(uint256 const& account, SeqProxy seqProx, uint256 const& id)
: account_(account), txId_(id), seqProxy_(seqProx)
{
}
friend bool
operator<(Key const& lhs, Key const& rhs);
inline friend bool
operator>(Key const& lhs, Key const& rhs)
{
return rhs < lhs;
}
inline friend bool
operator<=(Key const& lhs, Key const& rhs)
{
return !(lhs > rhs);
}
inline friend bool
operator>=(Key const& lhs, Key const& rhs)
{
return !(lhs < rhs);
}
inline friend bool
operator==(Key const& lhs, Key const& rhs)
{
return lhs.txId_ == rhs.txId_;
}
inline friend bool
operator!=(Key const& lhs, Key const& rhs)
{
return !(lhs == rhs);
}
uint256 const&
getAccount() const
{
return account_;
}
uint256 const&
getTXID() const
{
return txId_;
}
private:
uint256 account_;
uint256 txId_;
SeqProxy seqProxy_;
};
friend bool
operator<(Key const& lhs, Key const& rhs);
// Calculate the salted key for the given account
uint256
accountKey(AccountID const& account);
public:
using const_iterator = std::map<Key, std::shared_ptr<STTx const>>::const_iterator;
public:
explicit CanonicalTXSet(LedgerHash const& saltHash) : salt_(saltHash)
{
}
void
insert(std::shared_ptr<STTx const> const& txn);
// Pops the next transaction on account that follows seqProx in the
// sort order. Normally called when a transaction is successfully
// applied to the open ledger so the next transaction can be resubmitted
// without waiting for ledger close.
//
// The return value is often null, when an account has no more
// transactions.
std::shared_ptr<STTx const>
popAcctTransaction(std::shared_ptr<STTx const> const& tx);
void
reset(LedgerHash const& salt)
{
salt_ = salt;
map_.clear();
}
const_iterator
erase(const_iterator const& it)
{
return map_.erase(it);
}
const_iterator
begin() const
{
return map_.begin();
}
const_iterator
end() const
{
return map_.end();
}
size_t
size() const
{
return map_.size();
}
bool
empty() const
{
return map_.empty();
}
uint256 const&
key() const
{
return salt_;
}
private:
std::map<Key, std::shared_ptr<STTx const>> map_;
// Used to salt the accounts so people can't mine for low account numbers
uint256 salt_;
};
} // namespace xrpl

View File

@@ -1,43 +0,0 @@
#pragma once
#include <xrpl/ledger/View.h>
#include <xrpl/protocol/IOUAmount.h>
#include <xrpl/protocol/STAmount.h>
namespace xrpl {
/** Calculate the maximum amount of IOUs that an account can hold
@param ledger the ledger to check against.
@param account the account of interest.
@param issuer the issuer of the IOU.
@param currency the IOU to check.
@return The maximum amount that can be held.
*/
/** @{ */
STAmount
creditLimit(
ReadView const& view,
AccountID const& account,
AccountID const& issuer,
Currency const& currency);
IOUAmount
creditLimit2(ReadView const& v, AccountID const& acc, AccountID const& iss, Currency const& cur);
/** @} */
/** Returns the amount of IOUs issued by issuer that are held by an account
@param ledger the ledger to check against.
@param account the account of interest.
@param issuer the issuer of the IOU.
@param currency the IOU to check.
*/
/** @{ */
STAmount
creditBalance(
ReadView const& view,
AccountID const& account,
AccountID const& issuer,
Currency const& currency);
/** @} */
} // namespace xrpl

View File

@@ -0,0 +1,428 @@
#pragma once
#include <xrpl/basics/CountedObject.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/CachedView.h>
#include <xrpl/ledger/View.h>
#include <xrpl/protocol/Fees.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Rules.h>
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/Serializer.h>
#include <xrpl/protocol/TxMeta.h>
#include <xrpl/shamap/SHAMap.h>
namespace xrpl {
class ServiceRegistry;
class Job;
class TransactionMaster;
class SqliteStatement;
struct create_genesis_t
{
explicit create_genesis_t() = default;
};
extern create_genesis_t const create_genesis;
/** Holds a ledger.
The ledger is composed of two SHAMaps. The state map holds all of the
ledger entries such as account roots and order books. The tx map holds
all of the transactions and associated metadata that made it into that
particular ledger. Most of the operations on a ledger are concerned
with the state map.
This can hold just the header, a partial set of data, or the entire set
of data. It all depends on what is in the corresponding SHAMap entry.
Various functions are provided to populate or depopulate the caches that
the object holds references to.
Ledgers are constructed as either mutable or immutable.
1) If you are the sole owner of a mutable ledger, you can do whatever you
want with no need for locks.
2) If you have an immutable ledger, you cannot ever change it, so no need
for locks.
3) Mutable ledgers cannot be shared.
@note Presented to clients as ReadView
@note Calls virtuals in the constructor, so marked as final
*/
class Ledger final : public std::enable_shared_from_this<Ledger>,
public DigestAwareReadView,
public TxsRawView,
public CountedObject<Ledger>
{
public:
Ledger(Ledger const&) = delete;
Ledger&
operator=(Ledger const&) = delete;
Ledger(Ledger&&) = delete;
Ledger&
operator=(Ledger&&) = delete;
/** Create the Genesis ledger.
The Genesis ledger contains a single account whose
AccountID is generated with a Generator using the seed
computed from the string "masterpassphrase" and ordinal
zero.
The account has an XRP balance equal to the total amount
of XRP in the system. No more XRP than the amount which
starts in this account can ever exist, with amounts
used to pay fees being destroyed.
Amendments specified are enabled in the genesis ledger
*/
Ledger(
create_genesis_t,
Rules const& rules,
Fees const& fees,
std::vector<uint256> const& amendments,
Family& family);
Ledger(LedgerHeader const& info, Rules const& rules, Family& family);
/** Used for ledgers loaded from JSON files
@param acquire If true, acquires the ledger if not found locally
@note The fees parameter provides default values, but setup() may
override them from the ledger state if fee-related SLEs exist.
*/
Ledger(
LedgerHeader const& info,
bool& loaded,
bool acquire,
Rules const& rules,
Fees const& fees,
Family& family,
beast::Journal j);
/** Create a new ledger following a previous ledger
The ledger will have the sequence number that
follows previous, and have
parentCloseTime == previous.closeTime.
*/
Ledger(Ledger const& previous, NetClock::time_point closeTime);
// used for database ledgers
Ledger(
std::uint32_t ledgerSeq,
NetClock::time_point closeTime,
Rules const& rules,
Fees const& fees,
Family& family);
~Ledger() = default;
//
// ReadView
//
bool
open() const override
{
return false;
}
LedgerHeader const&
header() const override
{
return header_;
}
void
setLedgerInfo(LedgerHeader const& info)
{
header_ = info;
}
Fees const&
fees() const override
{
return fees_;
}
Rules const&
rules() const override
{
return rules_;
}
bool
exists(Keylet const& k) const override;
bool
exists(uint256 const& key) const;
std::optional<uint256>
succ(uint256 const& key, std::optional<uint256> const& last = std::nullopt) const override;
std::shared_ptr<SLE const>
read(Keylet const& k) const override;
std::unique_ptr<sles_type::iter_base>
slesBegin() const override;
std::unique_ptr<sles_type::iter_base>
slesEnd() const override;
std::unique_ptr<sles_type::iter_base>
slesUpperBound(uint256 const& key) const override;
std::unique_ptr<txs_type::iter_base>
txsBegin() const override;
std::unique_ptr<txs_type::iter_base>
txsEnd() const override;
bool
txExists(uint256 const& key) const override;
tx_type
txRead(key_type const& key) const override;
//
// DigestAwareReadView
//
std::optional<digest_type>
digest(key_type const& key) const override;
//
// RawView
//
void
rawErase(std::shared_ptr<SLE> const& sle) override;
void
rawInsert(std::shared_ptr<SLE> const& sle) override;
void
rawErase(uint256 const& key);
void
rawReplace(std::shared_ptr<SLE> const& sle) override;
void
rawDestroyXRP(XRPAmount const& fee) override
{
header_.drops -= fee;
}
//
// TxsRawView
//
void
rawTxInsert(
uint256 const& key,
std::shared_ptr<Serializer const> const& txn,
std::shared_ptr<Serializer const> const& metaData) override;
// Insert the transaction, and return the hash of the SHAMap leaf node
// holding the transaction. The hash can be used to fetch the transaction
// directly, instead of traversing the SHAMap
// @param key transaction ID
// @param txn transaction
// @param metaData transaction metadata
// @return hash of SHAMap leaf node that holds the transaction
uint256
rawTxInsertWithHash(
uint256 const& key,
std::shared_ptr<Serializer const> const& txn,
std::shared_ptr<Serializer const> const& metaData);
//--------------------------------------------------------------------------
void
setValidated() const
{
header_.validated = true;
}
void
setAccepted(
NetClock::time_point closeTime,
NetClock::duration closeResolution,
bool correctCloseTime);
void
setImmutable(bool rehash = true);
bool
isImmutable() const
{
return mImmutable;
}
/* Mark this ledger as "should be full".
"Full" is metadata property of the ledger, it indicates
that the local server wants all the corresponding nodes
in durable storage.
This is marked `const` because it reflects metadata
and not data that is in common with other nodes on the
network.
*/
void
setFull() const
{
txMap_.setFull();
txMap_.setLedgerSeq(header_.seq);
stateMap_.setFull();
stateMap_.setLedgerSeq(header_.seq);
}
void
setTotalDrops(std::uint64_t totDrops)
{
header_.drops = totDrops;
}
SHAMap const&
stateMap() const
{
return stateMap_;
}
SHAMap&
stateMap()
{
return stateMap_;
}
SHAMap const&
txMap() const
{
return txMap_;
}
SHAMap&
txMap()
{
return txMap_;
}
// returns false on error
bool
addSLE(SLE const& sle);
//--------------------------------------------------------------------------
void
updateSkipList();
bool
walkLedger(beast::Journal j, bool parallel = false) const;
bool
isSensible() const;
void
invariants() const;
void
unshare() const;
/**
* get Negative UNL validators' master public keys
*
* @return the public keys
*/
hash_set<PublicKey>
negativeUNL() const;
/**
* get the to be disabled validator's master public key if any
*
* @return the public key if any
*/
std::optional<PublicKey>
validatorToDisable() const;
/**
* get the to be re-enabled validator's master public key if any
*
* @return the public key if any
*/
std::optional<PublicKey>
validatorToReEnable() const;
/**
* update the Negative UNL ledger component.
* @note must be called at and only at flag ledgers
* must be called before applying UNLModify Tx
*/
void
updateNegativeUNL();
/** Returns true if the ledger is a flag ledger */
bool
isFlagLedger() const;
/** Returns true if the ledger directly precedes a flag ledger */
bool
isVotingLedger() const;
std::shared_ptr<SLE>
peek(Keylet const& k) const;
private:
class sles_iter_impl;
class txs_iter_impl;
bool
setup();
/** @brief Deserialize a SHAMapItem containing a single STTx.
*
* @param item The SHAMapItem to deserialize.
* @return A shared pointer to the deserialized transaction.
* @throw May throw on deserialization error.
*/
static std::shared_ptr<STTx const>
deserializeTx(SHAMapItem const& item);
/** @brief Deserialize a SHAMapItem containing STTx + STObject metadata.
*
* The SHAMapItem must contain two variable length serialization objects.
*
* @param item The SHAMapItem to deserialize.
* @return A pair containing shared pointers to the deserialized transaction
* and metadata.
* @throw May throw on deserialization error.
*/
static std::pair<std::shared_ptr<STTx const>, std::shared_ptr<STObject const>>
deserializeTxPlusMeta(SHAMapItem const& item);
bool mImmutable;
// A SHAMap containing the transactions associated with this ledger.
SHAMap mutable txMap_;
// A SHAMap containing the state objects for this ledger.
SHAMap mutable stateMap_;
// Protects fee variables
std::mutex mutable mutex_;
Fees fees_;
Rules rules_;
LedgerHeader header_;
beast::Journal j_;
};
/** A ledger wrapped in a CachedView. */
using CachedLedger = CachedView<Ledger>;
} // namespace xrpl

View File

@@ -0,0 +1,146 @@
#pragma once
#include <xrpl/basics/chrono.h>
#include <xrpl/beast/utility/Journal.h>
#include <chrono>
namespace xrpl {
/** Possible ledger close time resolutions.
Values should not be duplicated.
@see getNextLedgerTimeResolution
*/
std::chrono::seconds constexpr ledgerPossibleTimeResolutions[] = {
std::chrono::seconds{10},
std::chrono::seconds{20},
std::chrono::seconds{30},
std::chrono::seconds{60},
std::chrono::seconds{90},
std::chrono::seconds{120}};
//! Initial resolution of ledger close time.
auto constexpr ledgerDefaultTimeResolution = ledgerPossibleTimeResolutions[2];
//! Close time resolution in genesis ledger
auto constexpr ledgerGenesisTimeResolution = ledgerPossibleTimeResolutions[0];
//! How often we increase the close time resolution (in numbers of ledgers)
auto constexpr increaseLedgerTimeResolutionEvery = 8;
//! How often we decrease the close time resolution (in numbers of ledgers)
auto constexpr decreaseLedgerTimeResolutionEvery = 1;
/** Calculates the close time resolution for the specified ledger.
The Ripple protocol uses binning to represent time intervals using only one
timestamp. This allows servers to derive a common time for the next ledger,
without the need for perfectly synchronized clocks.
The time resolution (i.e. the size of the intervals) is adjusted dynamically
based on what happened in the last ledger, to try to avoid disagreements.
@param previousResolution the resolution used for the prior ledger
@param previousAgree whether consensus agreed on the close time of the prior
ledger
@param ledgerSeq the sequence number of the new ledger
@pre previousResolution must be a valid bin
from @ref ledgerPossibleTimeResolutions
@tparam Rep Type representing number of ticks in std::chrono::duration
@tparam Period An std::ratio representing tick period in
std::chrono::duration
@tparam Seq Unsigned integer-like type corresponding to the ledger sequence
number. It should be comparable to 0 and support modular
division. Built-in and tagged_integers are supported.
*/
template <class Rep, class Period, class Seq>
std::chrono::duration<Rep, Period>
getNextLedgerTimeResolution(
std::chrono::duration<Rep, Period> previousResolution,
bool previousAgree,
Seq ledgerSeq)
{
XRPL_ASSERT(ledgerSeq != Seq{0}, "ripple:getNextLedgerTimeResolution : valid ledger sequence");
using namespace std::chrono;
// Find the current resolution:
auto iter = std::find(
std::begin(ledgerPossibleTimeResolutions),
std::end(ledgerPossibleTimeResolutions),
previousResolution);
XRPL_ASSERT(
iter != std::end(ledgerPossibleTimeResolutions),
"ripple:getNextLedgerTimeResolution : found time resolution");
// This should never happen, but just as a precaution
if (iter == std::end(ledgerPossibleTimeResolutions))
return previousResolution;
// If we did not previously agree, we try to decrease the resolution to
// improve the chance that we will agree now.
if (!previousAgree && (ledgerSeq % Seq{decreaseLedgerTimeResolutionEvery} == Seq{0}))
{
if (++iter != std::end(ledgerPossibleTimeResolutions))
return *iter;
}
// If we previously agreed, we try to increase the resolution to determine
// if we can continue to agree.
if (previousAgree && (ledgerSeq % Seq{increaseLedgerTimeResolutionEvery} == Seq{0}))
{
if (iter-- != std::begin(ledgerPossibleTimeResolutions))
return *iter;
}
return previousResolution;
}
/** Calculates the close time for a ledger, given a close time resolution.
@param closeTime The time to be rounded
@param closeResolution The resolution
@return @b closeTime rounded to the nearest multiple of @b closeResolution.
Rounds up if @b closeTime is midway between multiples of @b closeResolution.
*/
template <class Clock, class Duration, class Rep, class Period>
std::chrono::time_point<Clock, Duration>
roundCloseTime(
std::chrono::time_point<Clock, Duration> closeTime,
std::chrono::duration<Rep, Period> closeResolution)
{
using time_point = decltype(closeTime);
if (closeTime == time_point{})
return closeTime;
closeTime += (closeResolution / 2);
return closeTime - (closeTime.time_since_epoch() % closeResolution);
}
/** Calculate the effective ledger close time
After adjusting the ledger close time based on the current resolution, also
ensure it is sufficiently separated from the prior close time.
@param closeTime The raw ledger close time
@param resolution The current close time resolution
@param priorCloseTime The close time of the prior ledger
*/
template <class Clock, class Duration, class Rep, class Period>
std::chrono::time_point<Clock, Duration>
effCloseTime(
std::chrono::time_point<Clock, Duration> closeTime,
std::chrono::duration<Rep, Period> resolution,
std::chrono::time_point<Clock, Duration> priorCloseTime)
{
using namespace std::chrono_literals;
using time_point = decltype(closeTime);
if (closeTime == time_point{})
return closeTime;
return std::max<time_point>(roundCloseTime(closeTime, resolution), (priorCloseTime + 1s));
}
} // namespace xrpl

View File

@@ -76,16 +76,33 @@ public:
@return true if a book from this issue to XRP exists
*/
virtual bool
isBookToXRP(Issue const& issue, std::optional<Domain> domain = std::nullopt) = 0;
isBookToXRP(Issue const& issue, std::optional<Domain> const& domain = std::nullopt) = 0;
/**
* Process a transaction for order book tracking.
* @param ledger The ledger the transaction was applied to
* @param alTx The transaction to process
* @param jvObj The JSON object of the transaction
*/
virtual void
processTxn(
std::shared_ptr<ReadView const> const& ledger,
AcceptedLedgerTx const& alTx,
MultiApiJson const& jvObj) = 0;
/**
* Get the book listeners for a book.
* @param book The book to get the listeners for
* @return The book listeners for the book
*/
virtual BookListeners::pointer
getBookListeners(Book const&) = 0;
/**
* Create a new book listeners for a book.
* @param book The book to create the listeners for
* @return The new book listeners for the book
*/
virtual BookListeners::pointer
makeBookListeners(Book const&) = 0;
};

View File

@@ -0,0 +1,126 @@
#pragma once
#include <xrpl/protocol/Protocol.h>
#include <condition_variable>
#include <map>
#include <mutex>
namespace xrpl {
/** Keeps track of which ledgers haven't been fully saved.
During the ledger building process this collection will keep
track of those ledgers that are being built but have not yet
been completely written.
*/
class PendingSaves
{
private:
std::mutex mutable mutex_;
std::map<LedgerIndex, bool> map_;
std::condition_variable await_;
public:
/** Start working on a ledger
This is called prior to updating the SQLite indexes.
@return 'true' if work should be done
*/
bool
startWork(LedgerIndex seq)
{
std::lock_guard lock(mutex_);
auto it = map_.find(seq);
if ((it == map_.end()) || it->second)
{
// Work done or another thread is doing it
return false;
}
it->second = true;
return true;
}
/** Finish working on a ledger
This is called after updating the SQLite indexes.
The tracking of the work in progress is removed and
threads awaiting completion are notified.
*/
void
finishWork(LedgerIndex seq)
{
std::lock_guard lock(mutex_);
map_.erase(seq);
await_.notify_all();
}
/** Return `true` if a ledger is in the progress of being saved. */
bool
pending(LedgerIndex seq)
{
std::lock_guard lock(mutex_);
return map_.find(seq) != map_.end();
}
/** Check if a ledger should be dispatched
Called to determine whether work should be done or
dispatched. If work is already in progress and the
call is synchronous, wait for work to be completed.
@return 'true' if work should be done or dispatched
*/
bool
shouldWork(LedgerIndex seq, bool isSynchronous)
{
std::unique_lock<std::mutex> lock(mutex_);
do
{
auto it = map_.find(seq);
if (it == map_.end())
{
map_.emplace(seq, false);
return true;
}
if (!isSynchronous)
{
// Already dispatched
return false;
}
if (!it->second)
{
// Scheduled, but not dispatched
return true;
}
// Already in progress, just need to wait
await_.wait(lock);
} while (true);
}
/** Get a snapshot of the pending saves
Each entry in the returned map corresponds to a ledger
that is in progress or dispatched. The boolean indicates
whether work is currently in progress.
*/
std::map<LedgerIndex, bool>
getSnapshot() const
{
std::lock_guard lock(mutex_);
return map_;
}
};
} // namespace xrpl

View File

@@ -2,26 +2,23 @@
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/OpenView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/protocol/Asset.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/MPTIssue.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/Rate.h>
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STObject.h>
#include <xrpl/protocol/Serializer.h>
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/TER.h>
#include <cstdint>
#include <functional>
#include <initializer_list>
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <utility>
namespace xrpl {
enum class WaiveTransferFee : bool { No = false, Yes };
enum class SkipEntry : bool { No = false, Yes };
//------------------------------------------------------------------------------
@@ -54,24 +51,6 @@ enum class SkipEntry : bool { No = false, Yes };
[[nodiscard]] bool
hasExpired(ReadView const& view, std::optional<std::uint32_t> const& exp);
/** Controls the treatment of frozen account balances */
enum FreezeHandling { fhIGNORE_FREEZE, fhZERO_IF_FROZEN };
/** Controls the treatment of unauthorized MPT balances */
enum AuthHandling { ahIGNORE_AUTH, ahZERO_IF_UNAUTHORIZED };
/** Controls whether to include the account's full spendable balance */
enum SpendableHandling { shSIMPLE_BALANCE, shFULL_BALANCE };
[[nodiscard]] bool
isGlobalFrozen(ReadView const& view, AccountID const& issuer);
[[nodiscard]] bool
isGlobalFrozen(ReadView const& view, MPTIssue const& mptIssue);
[[nodiscard]] bool
isGlobalFrozen(ReadView const& view, Asset const& asset);
// Note, depth parameter is used to limit the recursion depth
[[nodiscard]] bool
isVaultPseudoAccountFrozen(
@@ -80,175 +59,6 @@ isVaultPseudoAccountFrozen(
MPTIssue const& mptShare,
int depth);
[[nodiscard]] bool
isIndividualFrozen(
ReadView const& view,
AccountID const& account,
Currency const& currency,
AccountID const& issuer);
[[nodiscard]] inline bool
isIndividualFrozen(ReadView const& view, AccountID const& account, Issue const& issue)
{
return isIndividualFrozen(view, account, issue.currency, issue.account);
}
[[nodiscard]] bool
isIndividualFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue);
[[nodiscard]] inline bool
isIndividualFrozen(ReadView const& view, AccountID const& account, Asset const& asset)
{
return std::visit(
[&](auto const& issue) { return isIndividualFrozen(view, account, issue); }, asset.value());
}
[[nodiscard]] bool
isFrozen(
ReadView const& view,
AccountID const& account,
Currency const& currency,
AccountID const& issuer);
[[nodiscard]] inline bool
isFrozen(ReadView const& view, AccountID const& account, Issue const& issue, int = 0 /*ignored*/)
{
return isFrozen(view, account, issue.currency, issue.account);
}
[[nodiscard]] bool
isFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue, int depth = 0);
/**
* isFrozen check is recursive for MPT shares in a vault, descending to
* assets in the vault, up to maxAssetCheckDepth recursion depth. This is
* purely defensive, as we currently do not allow such vaults to be created.
*/
[[nodiscard]] inline bool
isFrozen(ReadView const& view, AccountID const& account, Asset const& asset, int depth = 0)
{
return std::visit(
[&](auto const& issue) { return isFrozen(view, account, issue, depth); }, asset.value());
}
[[nodiscard]] inline TER
checkFrozen(ReadView const& view, AccountID const& account, Issue const& issue)
{
return isFrozen(view, account, issue) ? (TER)tecFROZEN : (TER)tesSUCCESS;
}
[[nodiscard]] inline TER
checkFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue)
{
return isFrozen(view, account, mptIssue) ? (TER)tecLOCKED : (TER)tesSUCCESS;
}
[[nodiscard]] inline TER
checkFrozen(ReadView const& view, AccountID const& account, Asset const& asset)
{
return std::visit(
[&](auto const& issue) { return checkFrozen(view, account, issue); }, asset.value());
}
[[nodiscard]] bool
isAnyFrozen(
ReadView const& view,
std::initializer_list<AccountID> const& accounts,
MPTIssue const& mptIssue,
int depth = 0);
[[nodiscard]] inline bool
isAnyFrozen(
ReadView const& view,
std::initializer_list<AccountID> const& accounts,
Issue const& issue)
{
for (auto const& account : accounts)
{
if (isFrozen(view, account, issue.currency, issue.account))
return true;
}
return false;
}
[[nodiscard]] inline bool
isAnyFrozen(
ReadView const& view,
std::initializer_list<AccountID> const& accounts,
Asset const& asset,
int depth = 0)
{
return std::visit(
[&]<ValidIssueType TIss>(TIss const& issue) {
if constexpr (std::is_same_v<TIss, Issue>)
return isAnyFrozen(view, accounts, issue);
else
return isAnyFrozen(view, accounts, issue, depth);
},
asset.value());
}
[[nodiscard]] bool
isDeepFrozen(
ReadView const& view,
AccountID const& account,
Currency const& currency,
AccountID const& issuer);
[[nodiscard]] inline bool
isDeepFrozen(
ReadView const& view,
AccountID const& account,
Issue const& issue,
int = 0 /*ignored*/)
{
return isDeepFrozen(view, account, issue.currency, issue.account);
}
[[nodiscard]] inline bool
isDeepFrozen(
ReadView const& view,
AccountID const& account,
MPTIssue const& mptIssue,
int depth = 0)
{
// Unlike IOUs, frozen / locked MPTs are not allowed to send or receive
// funds, so checking "deep frozen" is the same as checking "frozen".
return isFrozen(view, account, mptIssue, depth);
}
/**
* isFrozen check is recursive for MPT shares in a vault, descending to
* assets in the vault, up to maxAssetCheckDepth recursion depth. This is
* purely defensive, as we currently do not allow such vaults to be created.
*/
[[nodiscard]] inline bool
isDeepFrozen(ReadView const& view, AccountID const& account, Asset const& asset, int depth = 0)
{
return std::visit(
[&](auto const& issue) { return isDeepFrozen(view, account, issue, depth); },
asset.value());
}
[[nodiscard]] inline TER
checkDeepFrozen(ReadView const& view, AccountID const& account, Issue const& issue)
{
return isDeepFrozen(view, account, issue) ? (TER)tecFROZEN : (TER)tesSUCCESS;
}
[[nodiscard]] inline TER
checkDeepFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue)
{
return isDeepFrozen(view, account, mptIssue) ? (TER)tecLOCKED : (TER)tesSUCCESS;
}
[[nodiscard]] inline TER
checkDeepFrozen(ReadView const& view, AccountID const& account, Asset const& asset)
{
return std::visit(
[&](auto const& issue) { return checkDeepFrozen(view, account, issue); }, asset.value());
}
[[nodiscard]] bool
isLPTokenFrozen(
ReadView const& view,
@@ -256,159 +66,6 @@ isLPTokenFrozen(
Issue const& asset,
Issue const& asset2);
// Returns the amount an account can spend.
//
// If shSIMPLE_BALANCE is specified, this is the amount the account can spend
// without going into debt.
//
// If shFULL_BALANCE is specified, this is the amount the account can spend
// total. Specifically:
// * The account can go into debt if using a trust line, and the other side has
// a non-zero limit.
// * If the account is the asset issuer the limit is defined by the asset /
// issuance.
//
// <-- saAmount: amount of currency held by account. May be negative.
[[nodiscard]] STAmount
accountHolds(
ReadView const& view,
AccountID const& account,
Currency const& currency,
AccountID const& issuer,
FreezeHandling zeroIfFrozen,
beast::Journal j,
SpendableHandling includeFullBalance = shSIMPLE_BALANCE);
[[nodiscard]] STAmount
accountHolds(
ReadView const& view,
AccountID const& account,
Issue const& issue,
FreezeHandling zeroIfFrozen,
beast::Journal j,
SpendableHandling includeFullBalance = shSIMPLE_BALANCE);
[[nodiscard]] STAmount
accountHolds(
ReadView const& view,
AccountID const& account,
MPTIssue const& mptIssue,
FreezeHandling zeroIfFrozen,
AuthHandling zeroIfUnauthorized,
beast::Journal j,
SpendableHandling includeFullBalance = shSIMPLE_BALANCE);
[[nodiscard]] STAmount
accountHolds(
ReadView const& view,
AccountID const& account,
Asset const& asset,
FreezeHandling zeroIfFrozen,
AuthHandling zeroIfUnauthorized,
beast::Journal j,
SpendableHandling includeFullBalance = shSIMPLE_BALANCE);
// Returns the amount an account can spend of the currency type saDefault, or
// returns saDefault if this account is the issuer of the currency in
// question. Should be used in favor of accountHolds when questioning how much
// an account can spend while also allowing currency issuers to spend
// unlimited amounts of their own currency (since they can always issue more).
[[nodiscard]] STAmount
accountFunds(
ReadView const& view,
AccountID const& id,
STAmount const& saDefault,
FreezeHandling freezeHandling,
beast::Journal j);
// Return the account's liquid (not reserved) XRP. Generally prefer
// calling accountHolds() over this interface. However, this interface
// allows the caller to temporarily adjust the owner count should that be
// necessary.
//
// @param ownerCountAdj positive to add to count, negative to reduce count.
[[nodiscard]] XRPAmount
xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, beast::Journal j);
/** Iterate all items in the given directory. */
void
forEachItem(
ReadView const& view,
Keylet const& root,
std::function<void(std::shared_ptr<SLE const> const&)> const& f);
/** Iterate all items after an item in the given directory.
@param after The key of the item to start after
@param hint The directory page containing `after`
@param limit The maximum number of items to return
@return `false` if the iteration failed
*/
bool
forEachItemAfter(
ReadView const& view,
Keylet const& root,
uint256 const& after,
std::uint64_t const hint,
unsigned int limit,
std::function<bool(std::shared_ptr<SLE const> const&)> const& f);
/** Iterate all items in an account's owner directory. */
inline void
forEachItem(
ReadView const& view,
AccountID const& id,
std::function<void(std::shared_ptr<SLE const> const&)> const& f)
{
return forEachItem(view, keylet::ownerDir(id), f);
}
/** Iterate all items after an item in an owner directory.
@param after The key of the item to start after
@param hint The directory page containing `after`
@param limit The maximum number of items to return
@return `false` if the iteration failed
*/
inline bool
forEachItemAfter(
ReadView const& view,
AccountID const& id,
uint256 const& after,
std::uint64_t const hint,
unsigned int limit,
std::function<bool(std::shared_ptr<SLE const> const&)> const& f)
{
return forEachItemAfter(view, keylet::ownerDir(id), after, hint, limit, f);
}
/** Returns IOU issuer transfer fee as Rate. Rate specifies
* the fee as fractions of 1 billion. For example, 1% transfer rate
* is represented as 1,010,000,000.
* @param issuer The IOU issuer
*/
[[nodiscard]] Rate
transferRate(ReadView const& view, AccountID const& issuer);
/** Returns MPT transfer fee as Rate. Rate specifies
* the fee as fractions of 1 billion. For example, 1% transfer rate
* is represented as 1,010,000,000.
* @param issuanceID MPTokenIssuanceID of MPTTokenIssuance object
*/
[[nodiscard]] Rate
transferRate(ReadView const& view, MPTID const& issuanceID);
/** Returns the transfer fee as Rate based on the type of token
* @param view The ledger view
* @param amount The amount to transfer
*/
[[nodiscard]] Rate
transferRate(ReadView const& view, STAmount const& amount);
/** Returns `true` if the directory is empty
@param key The key of the directory
*/
[[nodiscard]] bool
dirIsEmpty(ReadView const& view, Keylet const& k);
// Return the list of enabled amendments
[[nodiscard]] std::set<uint256>
getEnabledAmendments(ReadView const& view);
@@ -474,81 +131,6 @@ areCompatible(
//
//------------------------------------------------------------------------------
/** Adjust the owner count up or down. */
void
adjustOwnerCount(
ApplyView& view,
std::shared_ptr<SLE> const& sle,
std::int32_t amount,
beast::Journal j);
/** @{ */
/** Returns the first entry in the directory, advancing the index
@deprecated These are legacy function that are considered deprecated
and will soon be replaced with an iterator-based model
that is easier to use. You should not use them in new code.
@param view The view against which to operate
@param root The root (i.e. first page) of the directory to iterate
@param page The current page
@param index The index inside the current page
@param entry The entry at the current index
@return true if the directory isn't empty; false otherwise
*/
bool
cdirFirst(
ReadView const& view,
uint256 const& root,
std::shared_ptr<SLE const>& page,
unsigned int& index,
uint256& entry);
bool
dirFirst(
ApplyView& view,
uint256 const& root,
std::shared_ptr<SLE>& page,
unsigned int& index,
uint256& entry);
/** @} */
/** @{ */
/** Returns the next entry in the directory, advancing the index
@deprecated These are legacy function that are considered deprecated
and will soon be replaced with an iterator-based model
that is easier to use. You should not use them in new code.
@param view The view against which to operate
@param root The root (i.e. first page) of the directory to iterate
@param page The current page
@param index The index inside the current page
@param entry The entry at the current index
@return true if the directory isn't empty; false otherwise
*/
bool
cdirNext(
ReadView const& view,
uint256 const& root,
std::shared_ptr<SLE const>& page,
unsigned int& index,
uint256& entry);
bool
dirNext(
ApplyView& view,
uint256 const& root,
std::shared_ptr<SLE>& page,
unsigned int& index,
uint256& entry);
/** @} */
[[nodiscard]] std::function<void(SLE::ref)>
describeOwnerDir(AccountID const& account);
[[nodiscard]] TER
dirLink(
ApplyView& view,
@@ -556,63 +138,6 @@ dirLink(
std::shared_ptr<SLE>& object,
SF_UINT64 const& node = sfOwnerNode);
AccountID
pseudoAccountAddress(ReadView const& view, uint256 const& pseudoOwnerKey);
/**
*
* Create pseudo-account, storing pseudoOwnerKey into ownerField.
*
* The list of valid ownerField is maintained in View.cpp and the caller to
* this function must perform necessary amendment check(s) before using a
* field. The amendment check is **not** performed in createPseudoAccount.
*/
[[nodiscard]] Expected<std::shared_ptr<SLE>, TER>
createPseudoAccount(ApplyView& view, uint256 const& pseudoOwnerKey, SField const& ownerField);
// Returns true if and only if sleAcct is a pseudo-account or specific
// pseudo-accounts in pseudoFieldFilter.
//
// Returns false if sleAcct is
// * NOT a pseudo-account OR
// * NOT a ltACCOUNT_ROOT OR
// * null pointer
[[nodiscard]] bool
isPseudoAccount(
std::shared_ptr<SLE const> sleAcct,
std::set<SField const*> const& pseudoFieldFilter = {});
// Returns the list of fields that define an ACCOUNT_ROOT as a pseudo-account if
// set
// Pseudo-account designator fields MUST be maintained by including the
// SField::sMD_PseudoAccount flag in the SField definition. (Don't forget to
// "| SField::sMD_Default"!) The fields do NOT need to be amendment-gated,
// since a non-active amendment will not set any field, by definition.
// Specific properties of a pseudo-account are NOT checked here, that's what
// InvariantCheck is for.
[[nodiscard]] std::vector<SField const*> const&
getPseudoAccountFields();
[[nodiscard]] inline bool
isPseudoAccount(
ReadView const& view,
AccountID const& accountId,
std::set<SField const*> const& pseudoFieldFilter = {})
{
return isPseudoAccount(view.read(keylet::account(accountId)), pseudoFieldFilter);
}
[[nodiscard]] TER
canAddHolding(ReadView const& view, Asset const& asset);
/** Validates that the destination SLE and tag are valid
- Checks that the SLE is not null.
- If the SLE requires a destination tag, checks that there is a tag.
*/
[[nodiscard]] TER
checkDestinationAndTag(SLE::const_ref toSle, bool hasDestinationTag);
/** Checks that can withdraw funds from an object to itself or a destination.
*
* The receiver may be either the submitting account (sfAccount) or a different
@@ -686,351 +211,6 @@ doWithdraw(
STAmount const& amount,
beast::Journal j);
/// Any transactors that call addEmptyHolding() in doApply must call
/// canAddHolding() in preflight with the same View and Asset
[[nodiscard]] TER
addEmptyHolding(
ApplyView& view,
AccountID const& accountID,
XRPAmount priorBalance,
Issue const& issue,
beast::Journal journal);
[[nodiscard]] TER
addEmptyHolding(
ApplyView& view,
AccountID const& accountID,
XRPAmount priorBalance,
MPTIssue const& mptIssue,
beast::Journal journal);
[[nodiscard]] inline TER
addEmptyHolding(
ApplyView& view,
AccountID const& accountID,
XRPAmount priorBalance,
Asset const& asset,
beast::Journal journal)
{
return std::visit(
[&]<ValidIssueType TIss>(TIss const& issue) -> TER {
return addEmptyHolding(view, accountID, priorBalance, issue, journal);
},
asset.value());
}
[[nodiscard]] TER
authorizeMPToken(
ApplyView& view,
XRPAmount const& priorBalance,
MPTID const& mptIssuanceID,
AccountID const& account,
beast::Journal journal,
std::uint32_t flags = 0,
std::optional<AccountID> holderID = std::nullopt);
// VFALCO NOTE Both STAmount parameters should just
// be "Amount", a unit-less number.
//
/** Create a trust line
This can set an initial balance.
*/
[[nodiscard]] TER
trustCreate(
ApplyView& view,
bool const bSrcHigh,
AccountID const& uSrcAccountID,
AccountID const& uDstAccountID,
uint256 const& uIndex, // --> ripple state entry
SLE::ref sleAccount, // --> the account being set.
bool const bAuth, // --> authorize account.
bool const bNoRipple, // --> others cannot ripple through
bool const bFreeze, // --> funds cannot leave
bool bDeepFreeze, // --> can neither receive nor send funds
STAmount const& saBalance, // --> balance of account being set.
// Issuer should be noAccount()
STAmount const& saLimit, // --> limit for account being set.
// Issuer should be the account being set.
std::uint32_t uSrcQualityIn,
std::uint32_t uSrcQualityOut,
beast::Journal j);
[[nodiscard]] TER
removeEmptyHolding(
ApplyView& view,
AccountID const& accountID,
Issue const& issue,
beast::Journal journal);
[[nodiscard]] TER
removeEmptyHolding(
ApplyView& view,
AccountID const& accountID,
MPTIssue const& mptIssue,
beast::Journal journal);
[[nodiscard]] inline TER
removeEmptyHolding(
ApplyView& view,
AccountID const& accountID,
Asset const& asset,
beast::Journal journal)
{
return std::visit(
[&]<ValidIssueType TIss>(TIss const& issue) -> TER {
return removeEmptyHolding(view, accountID, issue, journal);
},
asset.value());
}
[[nodiscard]] TER
trustDelete(
ApplyView& view,
std::shared_ptr<SLE> const& sleRippleState,
AccountID const& uLowAccountID,
AccountID const& uHighAccountID,
beast::Journal j);
/** Delete an offer.
Requirements:
The passed `sle` be obtained from a prior
call to view.peek()
*/
// [[nodiscard]] // nodiscard commented out so Flow, BookTip and others compile.
TER
offerDelete(ApplyView& view, std::shared_ptr<SLE> const& sle, beast::Journal j);
//------------------------------------------------------------------------------
//
// Money Transfers
//
// Direct send w/o fees:
// - Redeeming IOUs and/or sending sender's own IOUs.
// - Create trust line of needed.
// --> bCheckIssuer : normally require issuer to be involved.
// [[nodiscard]] // nodiscard commented out so DirectStep.cpp compiles.
/** Calls static rippleCreditIOU if saAmount represents Issue.
* Calls static rippleCreditMPT if saAmount represents MPTIssue.
*/
TER
rippleCredit(
ApplyView& view,
AccountID const& uSenderID,
AccountID const& uReceiverID,
STAmount const& saAmount,
bool bCheckIssuer,
beast::Journal j);
TER
rippleLockEscrowMPT(
ApplyView& view,
AccountID const& uGrantorID,
STAmount const& saAmount,
beast::Journal j);
TER
rippleUnlockEscrowMPT(
ApplyView& view,
AccountID const& uGrantorID,
AccountID const& uGranteeID,
STAmount const& netAmount,
STAmount const& grossAmount,
beast::Journal j);
/** Calls static accountSendIOU if saAmount represents Issue.
* Calls static accountSendMPT if saAmount represents MPTIssue.
*/
[[nodiscard]] TER
accountSend(
ApplyView& view,
AccountID const& from,
AccountID const& to,
STAmount const& saAmount,
beast::Journal j,
WaiveTransferFee waiveFee = WaiveTransferFee::No);
using MultiplePaymentDestinations = std::vector<std::pair<AccountID, Number>>;
/** Like accountSend, except one account is sending multiple payments (with the
* same asset!) simultaneously
*
* Calls static accountSendMultiIOU if saAmount represents Issue.
* Calls static accountSendMultiMPT if saAmount represents MPTIssue.
*/
[[nodiscard]] TER
accountSendMulti(
ApplyView& view,
AccountID const& senderID,
Asset const& asset,
MultiplePaymentDestinations const& receivers,
beast::Journal j,
WaiveTransferFee waiveFee = WaiveTransferFee::No);
[[nodiscard]] TER
issueIOU(
ApplyView& view,
AccountID const& account,
STAmount const& amount,
Issue const& issue,
beast::Journal j);
[[nodiscard]] TER
redeemIOU(
ApplyView& view,
AccountID const& account,
STAmount const& amount,
Issue const& issue,
beast::Journal j);
[[nodiscard]] TER
transferXRP(
ApplyView& view,
AccountID const& from,
AccountID const& to,
STAmount const& amount,
beast::Journal j);
/* Check if MPToken (for MPT) or trust line (for IOU) exists:
* - StrongAuth - before checking if authorization is required
* - WeakAuth
* for MPT - after checking lsfMPTRequireAuth flag
* for IOU - do not check if trust line exists
* - Legacy
* for MPT - before checking lsfMPTRequireAuth flag i.e. same as StrongAuth
* for IOU - do not check if trust line exists i.e. same as WeakAuth
*/
enum class AuthType { StrongAuth, WeakAuth, Legacy };
/** Check if the account lacks required authorization.
*
* Return tecNO_AUTH or tecNO_LINE if it does
* and tesSUCCESS otherwise.
*
* If StrongAuth then return tecNO_LINE if the RippleState doesn't exist. Return
* tecNO_AUTH if lsfRequireAuth is set on the issuer's AccountRoot, and the
* RippleState does exist, and the RippleState is not authorized.
*
* If WeakAuth then return tecNO_AUTH if lsfRequireAuth is set, and the
* RippleState exists, and is not authorized. Return tecNO_LINE if
* lsfRequireAuth is set and the RippleState doesn't exist. Consequently, if
* WeakAuth and lsfRequireAuth is *not* set, this function will return
* tesSUCCESS even if RippleState does *not* exist.
*
* The default "Legacy" auth type is equivalent to WeakAuth.
*/
[[nodiscard]] TER
requireAuth(
ReadView const& view,
Issue const& issue,
AccountID const& account,
AuthType authType = AuthType::Legacy);
/** Check if the account lacks required authorization.
*
* This will also check for expired credentials. If it is called directly
* from preclaim, the user should convert result tecEXPIRED to tesSUCCESS and
* proceed to also check permissions with enforceMPTokenAuthorization inside
* doApply. This will ensure that any expired credentials are deleted.
*
* requireAuth check is recursive for MPT shares in a vault, descending to
* assets in the vault, up to maxAssetCheckDepth recursion depth. This is
* purely defensive, as we currently do not allow such vaults to be created.
*
* If StrongAuth then return tecNO_AUTH if MPToken doesn't exist or
* lsfMPTRequireAuth is set and MPToken is not authorized. Vault and LoanBroker
* pseudo-accounts are implicitly authorized.
*
* If WeakAuth then return tecNO_AUTH if lsfMPTRequireAuth is set and MPToken
* doesn't exist or is not authorized (explicitly or via credentials, if
* DomainID is set in MPTokenIssuance). Consequently, if WeakAuth and
* lsfMPTRequireAuth is *not* set, this function will return true even if
* MPToken does *not* exist.
*
* The default "Legacy" auth type is equivalent to StrongAuth.
*/
[[nodiscard]] TER
requireAuth(
ReadView const& view,
MPTIssue const& mptIssue,
AccountID const& account,
AuthType authType = AuthType::Legacy,
int depth = 0);
[[nodiscard]] TER inline requireAuth(
ReadView const& view,
Asset const& asset,
AccountID const& account,
AuthType authType = AuthType::Legacy)
{
return std::visit(
[&]<ValidIssueType TIss>(TIss const& issue_) {
return requireAuth(view, issue_, account, authType);
},
asset.value());
}
/** Enforce account has MPToken to match its authorization.
*
* Called from doApply - it will check for expired (and delete if found any)
* credentials matching DomainID set in MPTokenIssuance. Must be called if
* requireAuth(...MPTIssue...) returned tesSUCCESS or tecEXPIRED in preclaim,
* which implies that preclaim should replace `tecEXPIRED` with `tesSUCCESS`
* in order for the transactor to proceed to doApply.
*
* This function will create MPToken (if needed) on the basis of any
* non-expired credentials and will delete any expired credentials, indirectly
* via verifyValidDomain, as per DomainID (if set in MPTokenIssuance).
*
* The caller does NOT need to ensure that DomainID is actually set - this
* function handles gracefully both cases when DomainID is set and when not.
*
* The caller does NOT need to look for existing MPToken to match
* mptIssue/account - this function checks lsfMPTAuthorized of an existing
* MPToken iff DomainID is not set.
*
* Do not use for accounts which hold implied permission e.g. object owners or
* if MPTokenIssuance does not require authorization. In both cases use
* MPTokenAuthorize::authorize if MPToken does not yet exist.
*/
[[nodiscard]] TER
enforceMPTokenAuthorization(
ApplyView& view,
MPTID const& mptIssuanceID,
AccountID const& account,
XRPAmount const& priorBalance,
beast::Journal j);
/** Check if the destination account is allowed
* to receive MPT. Return tecNO_AUTH if it doesn't
* and tesSUCCESS otherwise.
*/
[[nodiscard]] TER
canTransfer(
ReadView const& view,
MPTIssue const& mptIssue,
AccountID const& from,
AccountID const& to);
[[nodiscard]] TER
canTransfer(ReadView const& view, Issue const& issue, AccountID const& from, AccountID const& to);
[[nodiscard]] TER inline canTransfer(
ReadView const& view,
Asset const& asset,
AccountID const& from,
AccountID const& to)
{
return std::visit(
[&]<ValidIssueType TIss>(TIss const& issue) -> TER {
return canTransfer(view, issue, from, to);
},
asset.value());
}
/** Deleter function prototype. Returns the status of the entry deletion
* (if should not be skipped) and if the entry should be skipped. The status
* is always tesSUCCESS if the entry should be skipped.
@@ -1052,57 +232,6 @@ cleanupOnAccountDelete(
beast::Journal j,
std::optional<std::uint16_t> maxNodesToDelete = std::nullopt);
/** Delete trustline to AMM. The passed `sle` must be obtained from a prior
* call to view.peek(). Fail if neither side of the trustline is AMM or
* if ammAccountID is seated and is not one of the trustline's side.
*/
[[nodiscard]] TER
deleteAMMTrustLine(
ApplyView& view,
std::shared_ptr<SLE> sleState,
std::optional<AccountID> const& ammAccountID,
beast::Journal j);
// From the perspective of a vault, return the number of shares to give the
// depositor when they deposit a fixed amount of assets. Since shares are MPT
// this number is integral and always truncated in this calculation.
[[nodiscard]] std::optional<STAmount>
assetsToSharesDeposit(
std::shared_ptr<SLE const> const& vault,
std::shared_ptr<SLE const> const& issuance,
STAmount const& assets);
// From the perspective of a vault, return the number of assets to take from
// depositor when they receive a fixed amount of shares. Note, since shares are
// MPT, they are always an integral number.
[[nodiscard]] std::optional<STAmount>
sharesToAssetsDeposit(
std::shared_ptr<SLE const> const& vault,
std::shared_ptr<SLE const> const& issuance,
STAmount const& shares);
enum class TruncateShares : bool { no = false, yes = true };
// From the perspective of a vault, return the number of shares to demand from
// the depositor when they ask to withdraw a fixed amount of assets. Since
// shares are MPT this number is integral, and it will be rounded to nearest
// unless explicitly requested to be truncated instead.
[[nodiscard]] std::optional<STAmount>
assetsToSharesWithdraw(
std::shared_ptr<SLE const> const& vault,
std::shared_ptr<SLE const> const& issuance,
STAmount const& assets,
TruncateShares truncate = TruncateShares::no);
// From the perspective of a vault, return the number of assets to give the
// depositor when they redeem a fixed amount of shares. Note, since shares are
// MPT, they are always an integral number.
[[nodiscard]] std::optional<STAmount>
sharesToAssetsWithdraw(
std::shared_ptr<SLE const> const& vault,
std::shared_ptr<SLE const> const& issuance,
STAmount const& shares);
/** Has the specified time passed?
@param now the current time

View File

@@ -0,0 +1,112 @@
#pragma once
#include <xrpl/basics/Expected.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Rate.h>
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/TER.h>
#include <memory>
#include <set>
#include <vector>
namespace xrpl {
/** Check if the issuer has the global freeze flag set.
@param issuer The account to check
@return true if the account has global freeze set
*/
[[nodiscard]] bool
isGlobalFrozen(ReadView const& view, AccountID const& issuer);
// Calculate liquid XRP balance for an account.
// This function may be used to calculate the amount of XRP that
// the holder is able to freely spend. It subtracts reserve requirements.
//
// ownerCountAdj adjusts the owner count in case the caller calculates
// before ledger entries are added or removed. Positive to add, negative
// to subtract.
//
// @param ownerCountAdj positive to add to count, negative to reduce count.
[[nodiscard]] XRPAmount
xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, beast::Journal j);
/** Adjust the owner count up or down. */
void
adjustOwnerCount(
ApplyView& view,
std::shared_ptr<SLE> const& sle,
std::int32_t amount,
beast::Journal j);
/** Returns IOU issuer transfer fee as Rate. Rate specifies
* the fee as fractions of 1 billion. For example, 1% transfer rate
* is represented as 1,010,000,000.
* @param issuer The IOU issuer
*/
[[nodiscard]] Rate
transferRate(ReadView const& view, AccountID const& issuer);
/** Generate a pseudo-account address from a pseudo owner key.
@param pseudoOwnerKey The key to generate the address from
@return The generated account ID
*/
AccountID
pseudoAccountAddress(ReadView const& view, uint256 const& pseudoOwnerKey);
/** Returns the list of fields that define an ACCOUNT_ROOT as a pseudo-account
if set.
The list is constructed during initialization and is const after that.
Pseudo-account designator fields MUST be maintained by including the
SField::sMD_PseudoAccount flag in the SField definition.
*/
[[nodiscard]] std::vector<SField const*> const&
getPseudoAccountFields();
/** Returns true if and only if sleAcct is a pseudo-account or specific
pseudo-accounts in pseudoFieldFilter.
Returns false if sleAcct is:
- NOT a pseudo-account OR
- NOT a ltACCOUNT_ROOT OR
- null pointer
*/
[[nodiscard]] bool
isPseudoAccount(
std::shared_ptr<SLE const> sleAcct,
std::set<SField const*> const& pseudoFieldFilter = {});
/** Convenience overload that reads the account from the view. */
[[nodiscard]] inline bool
isPseudoAccount(
ReadView const& view,
AccountID const& accountId,
std::set<SField const*> const& pseudoFieldFilter = {})
{
return isPseudoAccount(view.read(keylet::account(accountId)), pseudoFieldFilter);
}
/**
* Create pseudo-account, storing pseudoOwnerKey into ownerField.
*
* The list of valid ownerField is maintained in AccountRootHelpers.cpp and
* the caller to this function must perform necessary amendment check(s)
* before using a field. The amendment check is **not** performed in
* createPseudoAccount.
*/
[[nodiscard]] Expected<std::shared_ptr<SLE>, TER>
createPseudoAccount(ApplyView& view, uint256 const& pseudoOwnerKey, SField const& ownerField);
/** Checks the destination and tag.
- Checks that the SLE is not null.
- If the SLE requires a destination tag, checks that there is a tag.
*/
[[nodiscard]] TER
checkDestinationAndTag(SLE::const_ref toSle, bool hasDestinationTag);
} // namespace xrpl

View File

@@ -0,0 +1,223 @@
#pragma once
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/TER.h>
#include <functional>
#include <memory>
#include <type_traits>
namespace xrpl {
namespace detail {
template <
class V,
class N,
class = std::enable_if_t<
std::is_same_v<std::remove_cv_t<N>, SLE> && std::is_base_of_v<ReadView, V>>>
bool
internalDirNext(
V& view,
uint256 const& root,
std::shared_ptr<N>& page,
unsigned int& index,
uint256& entry)
{
auto const& svIndexes = page->getFieldV256(sfIndexes);
XRPL_ASSERT(index <= svIndexes.size(), "xrpl::detail::internalDirNext : index inside range");
if (index >= svIndexes.size())
{
auto const next = page->getFieldU64(sfIndexNext);
if (!next)
{
entry.zero();
return false;
}
if constexpr (std::is_const_v<N>)
{
page = view.read(keylet::page(root, next));
}
else
{
page = view.peek(keylet::page(root, next));
}
XRPL_ASSERT(page, "xrpl::detail::internalDirNext : non-null root");
if (!page)
return false;
index = 0;
return internalDirNext(view, root, page, index, entry);
}
entry = svIndexes[index++];
return true;
}
template <
class V,
class N,
class = std::enable_if_t<
std::is_same_v<std::remove_cv_t<N>, SLE> && std::is_base_of_v<ReadView, V>>>
bool
internalDirFirst(
V& view,
uint256 const& root,
std::shared_ptr<N>& page,
unsigned int& index,
uint256& entry)
{
if constexpr (std::is_const_v<N>)
{
page = view.read(keylet::page(root));
}
else
{
page = view.peek(keylet::page(root));
}
if (!page)
return false;
index = 0;
return internalDirNext(view, root, page, index, entry);
}
} // namespace detail
/** @{ */
/** Returns the first entry in the directory, advancing the index
@deprecated These are legacy function that are considered deprecated
and will soon be replaced with an iterator-based model
that is easier to use. You should not use them in new code.
@param view The view against which to operate
@param root The root (i.e. first page) of the directory to iterate
@param page The current page
@param index The index inside the current page
@param entry The entry at the current index
@return true if the directory isn't empty; false otherwise
*/
bool
cdirFirst(
ReadView const& view,
uint256 const& root,
std::shared_ptr<SLE const>& page,
unsigned int& index,
uint256& entry);
bool
dirFirst(
ApplyView& view,
uint256 const& root,
std::shared_ptr<SLE>& page,
unsigned int& index,
uint256& entry);
/** @} */
/** @{ */
/** Returns the next entry in the directory, advancing the index
@deprecated These are legacy function that are considered deprecated
and will soon be replaced with an iterator-based model
that is easier to use. You should not use them in new code.
@param view The view against which to operate
@param root The root (i.e. first page) of the directory to iterate
@param page The current page
@param index The index inside the current page
@param entry The entry at the current index
@return true if the directory isn't empty; false otherwise
*/
bool
cdirNext(
ReadView const& view,
uint256 const& root,
std::shared_ptr<SLE const>& page,
unsigned int& index,
uint256& entry);
bool
dirNext(
ApplyView& view,
uint256 const& root,
std::shared_ptr<SLE>& page,
unsigned int& index,
uint256& entry);
/** @} */
/** Iterate all items in the given directory. */
void
forEachItem(
ReadView const& view,
Keylet const& root,
std::function<void(std::shared_ptr<SLE const> const&)> const& f);
/** Iterate all items after an item in the given directory.
@param after The key of the item to start after
@param hint The directory page containing `after`
@param limit The maximum number of items to return
@return `false` if the iteration failed
*/
bool
forEachItemAfter(
ReadView const& view,
Keylet const& root,
uint256 const& after,
std::uint64_t const hint,
unsigned int limit,
std::function<bool(std::shared_ptr<SLE const> const&)> const& f);
/** Iterate all items in an account's owner directory. */
inline void
forEachItem(
ReadView const& view,
AccountID const& id,
std::function<void(std::shared_ptr<SLE const> const&)> const& f)
{
return forEachItem(view, keylet::ownerDir(id), f);
}
/** Iterate all items after an item in an owner directory.
@param after The key of the item to start after
@param hint The directory page containing `after`
@param limit The maximum number of items to return
@return `false` if the iteration failed
*/
inline bool
forEachItemAfter(
ReadView const& view,
AccountID const& id,
uint256 const& after,
std::uint64_t const hint,
unsigned int limit,
std::function<bool(std::shared_ptr<SLE const> const&)> const& f)
{
return forEachItemAfter(view, keylet::ownerDir(id), after, hint, limit, f);
}
/** Returns `true` if the directory is empty
@param key The key of the directory
*/
[[nodiscard]] bool
dirIsEmpty(ReadView const& view, Keylet const& k);
/** Returns a function that sets the owner on a directory SLE */
[[nodiscard]] std::function<void(SLE::ref)>
describeOwnerDir(AccountID const& account);
} // namespace xrpl

View File

@@ -0,0 +1,160 @@
#pragma once
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/MPTIssue.h>
#include <xrpl/protocol/Rate.h>
#include <xrpl/protocol/STAmount.h>
#include <xrpl/protocol/TER.h>
#include <initializer_list>
#include <optional>
namespace xrpl {
//------------------------------------------------------------------------------
//
// Freeze checking (MPT-specific)
//
//------------------------------------------------------------------------------
[[nodiscard]] bool
isGlobalFrozen(ReadView const& view, MPTIssue const& mptIssue);
[[nodiscard]] bool
isIndividualFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue);
[[nodiscard]] bool
isFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue, int depth = 0);
[[nodiscard]] bool
isAnyFrozen(
ReadView const& view,
std::initializer_list<AccountID> const& accounts,
MPTIssue const& mptIssue,
int depth = 0);
//------------------------------------------------------------------------------
//
// Transfer rate (MPT-specific)
//
//------------------------------------------------------------------------------
/** Returns MPT transfer fee as Rate. Rate specifies
* the fee as fractions of 1 billion. For example, 1% transfer rate
* is represented as 1,010,000,000.
* @param issuanceID MPTokenIssuanceID of MPTTokenIssuance object
*/
[[nodiscard]] Rate
transferRate(ReadView const& view, MPTID const& issuanceID);
//------------------------------------------------------------------------------
//
// Holding checks (MPT-specific)
//
//------------------------------------------------------------------------------
[[nodiscard]] TER
canAddHolding(ReadView const& view, MPTIssue const& mptIssue);
//------------------------------------------------------------------------------
//
// Authorization (MPT-specific)
//
//------------------------------------------------------------------------------
[[nodiscard]] TER
authorizeMPToken(
ApplyView& view,
XRPAmount const& priorBalance,
MPTID const& mptIssuanceID,
AccountID const& account,
beast::Journal journal,
std::uint32_t flags = 0,
std::optional<AccountID> holderID = std::nullopt);
/** Check if the account lacks required authorization for MPT.
*
* requireAuth check is recursive for MPT shares in a vault, descending to
* assets in the vault, up to maxAssetCheckDepth recursion depth. This is
* purely defensive, as we currently do not allow such vaults to be created.
*/
[[nodiscard]] TER
requireAuth(
ReadView const& view,
MPTIssue const& mptIssue,
AccountID const& account,
AuthType authType = AuthType::Legacy,
int depth = 0);
/** Enforce account has MPToken to match its authorization.
*
* Called from doApply - it will check for expired (and delete if found any)
* credentials matching DomainID set in MPTokenIssuance. Must be called if
* requireAuth(...MPTIssue...) returned tesSUCCESS or tecEXPIRED in preclaim.
*/
[[nodiscard]] TER
enforceMPTokenAuthorization(
ApplyView& view,
MPTID const& mptIssuanceID,
AccountID const& account,
XRPAmount const& priorBalance,
beast::Journal j);
/** Check if the destination account is allowed
* to receive MPT. Return tecNO_AUTH if it doesn't
* and tesSUCCESS otherwise.
*/
[[nodiscard]] TER
canTransfer(
ReadView const& view,
MPTIssue const& mptIssue,
AccountID const& from,
AccountID const& to);
//------------------------------------------------------------------------------
//
// Empty holding operations (MPT-specific)
//
//------------------------------------------------------------------------------
[[nodiscard]] TER
addEmptyHolding(
ApplyView& view,
AccountID const& accountID,
XRPAmount priorBalance,
MPTIssue const& mptIssue,
beast::Journal journal);
[[nodiscard]] TER
removeEmptyHolding(
ApplyView& view,
AccountID const& accountID,
MPTIssue const& mptIssue,
beast::Journal journal);
//------------------------------------------------------------------------------
//
// Escrow operations (MPT-specific)
//
//------------------------------------------------------------------------------
TER
rippleLockEscrowMPT(
ApplyView& view,
AccountID const& uGrantorID,
STAmount const& saAmount,
beast::Journal j);
TER
rippleUnlockEscrowMPT(
ApplyView& view,
AccountID const& uGrantorID,
AccountID const& uGranteeID,
STAmount const& netAmount,
STAmount const& grossAmount,
beast::Journal j);
} // namespace xrpl

View File

@@ -0,0 +1,28 @@
#pragma once
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/TER.h>
#include <memory>
namespace xrpl {
/** Delete an offer.
Requirements:
The offer must exist.
The caller must have already checked permissions.
@param view The ApplyView to modify.
@param sle The offer to delete.
@param j Journal for logging.
@return tesSUCCESS on success, otherwise an error code.
*/
// [[nodiscard]] // nodiscard commented out so Flow, BookTip and others compile.
TER
offerDelete(ApplyView& view, std::shared_ptr<SLE> const& sle, beast::Journal j);
} // namespace xrpl

View File

@@ -0,0 +1,255 @@
#pragma once
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/IOUAmount.h>
#include <xrpl/protocol/Issue.h>
#include <xrpl/protocol/STAmount.h>
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/TER.h>
//------------------------------------------------------------------------------
//
// RippleState (Trustline) helpers
//
//------------------------------------------------------------------------------
namespace xrpl {
//------------------------------------------------------------------------------
//
// Credit functions (from Credit.h)
//
//------------------------------------------------------------------------------
/** Calculate the maximum amount of IOUs that an account can hold
@param view the ledger to check against.
@param account the account of interest.
@param issuer the issuer of the IOU.
@param currency the IOU to check.
@return The maximum amount that can be held.
*/
/** @{ */
STAmount
creditLimit(
ReadView const& view,
AccountID const& account,
AccountID const& issuer,
Currency const& currency);
IOUAmount
creditLimit2(ReadView const& v, AccountID const& acc, AccountID const& iss, Currency const& cur);
/** @} */
/** Returns the amount of IOUs issued by issuer that are held by an account
@param view the ledger to check against.
@param account the account of interest.
@param issuer the issuer of the IOU.
@param currency the IOU to check.
*/
/** @{ */
STAmount
creditBalance(
ReadView const& view,
AccountID const& account,
AccountID const& issuer,
Currency const& currency);
/** @} */
//------------------------------------------------------------------------------
//
// Freeze checking (IOU-specific)
//
//------------------------------------------------------------------------------
[[nodiscard]] bool
isIndividualFrozen(
ReadView const& view,
AccountID const& account,
Currency const& currency,
AccountID const& issuer);
[[nodiscard]] inline bool
isIndividualFrozen(ReadView const& view, AccountID const& account, Issue const& issue)
{
return isIndividualFrozen(view, account, issue.currency, issue.account);
}
[[nodiscard]] bool
isFrozen(
ReadView const& view,
AccountID const& account,
Currency const& currency,
AccountID const& issuer);
[[nodiscard]] inline bool
isFrozen(ReadView const& view, AccountID const& account, Issue const& issue)
{
return isFrozen(view, account, issue.currency, issue.account);
}
// Overload with depth parameter for uniformity with MPTIssue version.
// The depth parameter is ignored for IOUs since they don't have vault recursion.
[[nodiscard]] inline bool
isFrozen(ReadView const& view, AccountID const& account, Issue const& issue, int /*depth*/)
{
return isFrozen(view, account, issue);
}
[[nodiscard]] bool
isDeepFrozen(
ReadView const& view,
AccountID const& account,
Currency const& currency,
AccountID const& issuer);
[[nodiscard]] inline bool
isDeepFrozen(
ReadView const& view,
AccountID const& account,
Issue const& issue,
int = 0 /*ignored*/)
{
return isDeepFrozen(view, account, issue.currency, issue.account);
}
[[nodiscard]] inline TER
checkDeepFrozen(ReadView const& view, AccountID const& account, Issue const& issue)
{
return isDeepFrozen(view, account, issue) ? (TER)tecFROZEN : (TER)tesSUCCESS;
}
//------------------------------------------------------------------------------
//
// Trust line operations
//
//------------------------------------------------------------------------------
/** Create a trust line
This can set an initial balance.
*/
[[nodiscard]] TER
trustCreate(
ApplyView& view,
bool const bSrcHigh,
AccountID const& uSrcAccountID,
AccountID const& uDstAccountID,
uint256 const& uIndex, // --> ripple state entry
SLE::ref sleAccount, // --> the account being set.
bool const bAuth, // --> authorize account.
bool const bNoRipple, // --> others cannot ripple through
bool const bFreeze, // --> funds cannot leave
bool bDeepFreeze, // --> can neither receive nor send funds
STAmount const& saBalance, // --> balance of account being set.
// Issuer should be noAccount()
STAmount const& saLimit, // --> limit for account being set.
// Issuer should be the account being set.
std::uint32_t uQualityIn,
std::uint32_t uQualityOut,
beast::Journal j);
[[nodiscard]] TER
trustDelete(
ApplyView& view,
std::shared_ptr<SLE> const& sleRippleState,
AccountID const& uLowAccountID,
AccountID const& uHighAccountID,
beast::Journal j);
//------------------------------------------------------------------------------
//
// IOU issuance/redemption
//
//------------------------------------------------------------------------------
[[nodiscard]] TER
issueIOU(
ApplyView& view,
AccountID const& account,
STAmount const& amount,
Issue const& issue,
beast::Journal j);
[[nodiscard]] TER
redeemIOU(
ApplyView& view,
AccountID const& account,
STAmount const& amount,
Issue const& issue,
beast::Journal j);
//------------------------------------------------------------------------------
//
// Authorization and transfer checks (IOU-specific)
//
//------------------------------------------------------------------------------
/** Check if the account lacks required authorization.
*
* Return tecNO_AUTH or tecNO_LINE if it does
* and tesSUCCESS otherwise.
*
* If StrongAuth then return tecNO_LINE if the RippleState doesn't exist. Return
* tecNO_AUTH if lsfRequireAuth is set on the issuer's AccountRoot, and the
* RippleState does exist, and the RippleState is not authorized.
*
* If WeakAuth then return tecNO_AUTH if lsfRequireAuth is set, and the
* RippleState exists, and is not authorized. Return tecNO_LINE if
* lsfRequireAuth is set and the RippleState doesn't exist. Consequently, if
* WeakAuth and lsfRequireAuth is *not* set, this function will return
* tesSUCCESS even if RippleState does *not* exist.
*
* The default "Legacy" auth type is equivalent to WeakAuth.
*/
[[nodiscard]] TER
requireAuth(
ReadView const& view,
Issue const& issue,
AccountID const& account,
AuthType authType = AuthType::Legacy);
/** Check if the destination account is allowed
* to receive IOU. Return terNO_RIPPLE if rippling is
* disabled on both sides and tesSUCCESS otherwise.
*/
[[nodiscard]] TER
canTransfer(ReadView const& view, Issue const& issue, AccountID const& from, AccountID const& to);
//------------------------------------------------------------------------------
//
// Empty holding operations (IOU-specific)
//
//------------------------------------------------------------------------------
/// Any transactors that call addEmptyHolding() in doApply must call
/// canAddHolding() in preflight with the same View and Asset
[[nodiscard]] TER
addEmptyHolding(
ApplyView& view,
AccountID const& accountID,
XRPAmount priorBalance,
Issue const& issue,
beast::Journal journal);
[[nodiscard]] TER
removeEmptyHolding(
ApplyView& view,
AccountID const& accountID,
Issue const& issue,
beast::Journal journal);
/** Delete trustline to AMM. The passed `sle` must be obtained from a prior
* call to view.peek(). Fail if neither side of the trustline is AMM or
* if ammAccountID is seated and is not one of the trustline's side.
*/
[[nodiscard]] TER
deleteAMMTrustLine(
ApplyView& view,
std::shared_ptr<SLE> sleState,
std::optional<AccountID> const& ammAccountID,
beast::Journal j);
} // namespace xrpl

View File

@@ -0,0 +1,286 @@
#pragma once
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/protocol/Asset.h>
#include <xrpl/protocol/MPTIssue.h>
#include <xrpl/protocol/Rate.h>
#include <xrpl/protocol/STAmount.h>
#include <xrpl/protocol/TER.h>
#include <initializer_list>
#include <vector>
namespace xrpl {
//------------------------------------------------------------------------------
//
// Enums for token handling
//
//------------------------------------------------------------------------------
/** Controls the treatment of frozen account balances */
enum FreezeHandling { fhIGNORE_FREEZE, fhZERO_IF_FROZEN };
/** Controls the treatment of unauthorized MPT balances */
enum AuthHandling { ahIGNORE_AUTH, ahZERO_IF_UNAUTHORIZED };
/** Controls whether to include the account's full spendable balance */
enum SpendableHandling { shSIMPLE_BALANCE, shFULL_BALANCE };
enum class WaiveTransferFee : bool { No = false, Yes };
/* Check if MPToken (for MPT) or trust line (for IOU) exists:
* - StrongAuth - before checking if authorization is required
* - WeakAuth
* for MPT - after checking lsfMPTRequireAuth flag
* for IOU - do not check if trust line exists
* - Legacy
* for MPT - before checking lsfMPTRequireAuth flag i.e. same as StrongAuth
* for IOU - do not check if trust line exists i.e. same as WeakAuth
*/
enum class AuthType { StrongAuth, WeakAuth, Legacy };
//------------------------------------------------------------------------------
//
// Freeze checking (Asset-based dispatchers)
//
//------------------------------------------------------------------------------
[[nodiscard]] bool
isGlobalFrozen(ReadView const& view, Asset const& asset);
[[nodiscard]] bool
isIndividualFrozen(ReadView const& view, AccountID const& account, Asset const& asset);
/**
* isFrozen check is recursive for MPT shares in a vault, descending to
* assets in the vault, up to maxAssetCheckDepth recursion depth. This is
* purely defensive, as we currently do not allow such vaults to be created.
*/
[[nodiscard]] bool
isFrozen(ReadView const& view, AccountID const& account, Asset const& asset, int depth = 0);
[[nodiscard]] TER
checkFrozen(ReadView const& view, AccountID const& account, Issue const& issue);
[[nodiscard]] TER
checkFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue);
[[nodiscard]] TER
checkFrozen(ReadView const& view, AccountID const& account, Asset const& asset);
[[nodiscard]] bool
isAnyFrozen(
ReadView const& view,
std::initializer_list<AccountID> const& accounts,
Issue const& issue);
[[nodiscard]] bool
isAnyFrozen(
ReadView const& view,
std::initializer_list<AccountID> const& accounts,
Asset const& asset,
int depth = 0);
[[nodiscard]] bool
isDeepFrozen(
ReadView const& view,
AccountID const& account,
MPTIssue const& mptIssue,
int depth = 0);
/**
* isFrozen check is recursive for MPT shares in a vault, descending to
* assets in the vault, up to maxAssetCheckDepth recursion depth. This is
* purely defensive, as we currently do not allow such vaults to be created.
*/
[[nodiscard]] bool
isDeepFrozen(ReadView const& view, AccountID const& account, Asset const& asset, int depth = 0);
[[nodiscard]] TER
checkDeepFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue);
[[nodiscard]] TER
checkDeepFrozen(ReadView const& view, AccountID const& account, Asset const& asset);
//------------------------------------------------------------------------------
//
// Account balance functions (Asset-based dispatchers)
//
//------------------------------------------------------------------------------
// Returns the amount an account can spend.
//
// If shSIMPLE_BALANCE is specified, this is the amount the account can spend
// without going into debt.
//
// If shFULL_BALANCE is specified, this is the amount the account can spend
// total. Specifically:
// * The account can go into debt if using a trust line, and the other side has
// a non-zero limit.
// * If the account is the asset issuer the limit is defined by the asset /
// issuance.
//
// <-- saAmount: amount of currency held by account. May be negative.
[[nodiscard]] STAmount
accountHolds(
ReadView const& view,
AccountID const& account,
Currency const& currency,
AccountID const& issuer,
FreezeHandling zeroIfFrozen,
beast::Journal j,
SpendableHandling includeFullBalance = shSIMPLE_BALANCE);
[[nodiscard]] STAmount
accountHolds(
ReadView const& view,
AccountID const& account,
Issue const& issue,
FreezeHandling zeroIfFrozen,
beast::Journal j,
SpendableHandling includeFullBalance = shSIMPLE_BALANCE);
[[nodiscard]] STAmount
accountHolds(
ReadView const& view,
AccountID const& account,
MPTIssue const& mptIssue,
FreezeHandling zeroIfFrozen,
AuthHandling zeroIfUnauthorized,
beast::Journal j,
SpendableHandling includeFullBalance = shSIMPLE_BALANCE);
[[nodiscard]] STAmount
accountHolds(
ReadView const& view,
AccountID const& account,
Asset const& asset,
FreezeHandling zeroIfFrozen,
AuthHandling zeroIfUnauthorized,
beast::Journal j,
SpendableHandling includeFullBalance = shSIMPLE_BALANCE);
// Returns the amount an account can spend of the currency type saDefault, or
// returns saDefault if this account is the issuer of the currency in
// question. Should be used in favor of accountHolds when questioning how much
// an account can spend while also allowing currency issuers to spend
// unlimited amounts of their own currency (since they can always issue more).
[[nodiscard]] STAmount
accountFunds(
ReadView const& view,
AccountID const& id,
STAmount const& saDefault,
FreezeHandling freezeHandling,
beast::Journal j);
/** Returns the transfer fee as Rate based on the type of token
* @param view The ledger view
* @param amount The amount to transfer
*/
[[nodiscard]] Rate
transferRate(ReadView const& view, STAmount const& amount);
//------------------------------------------------------------------------------
//
// Holding operations (Asset-based dispatchers)
//
//------------------------------------------------------------------------------
[[nodiscard]] TER
canAddHolding(ReadView const& view, Asset const& asset);
[[nodiscard]] TER
addEmptyHolding(
ApplyView& view,
AccountID const& accountID,
XRPAmount priorBalance,
Asset const& asset,
beast::Journal journal);
[[nodiscard]] TER
removeEmptyHolding(
ApplyView& view,
AccountID const& accountID,
Asset const& asset,
beast::Journal journal);
//------------------------------------------------------------------------------
//
// Authorization and transfer checks (Asset-based dispatchers)
//
//------------------------------------------------------------------------------
[[nodiscard]] TER
requireAuth(
ReadView const& view,
Asset const& asset,
AccountID const& account,
AuthType authType = AuthType::Legacy);
[[nodiscard]] TER
canTransfer(ReadView const& view, Asset const& asset, AccountID const& from, AccountID const& to);
//------------------------------------------------------------------------------
//
// Money Transfers (Asset-based dispatchers)
//
//------------------------------------------------------------------------------
// Direct send w/o fees:
// - Redeeming IOUs and/or sending sender's own IOUs.
// - Create trust line of needed.
// --> bCheckIssuer : normally require issuer to be involved.
// [[nodiscard]] // nodiscard commented out so DirectStep.cpp compiles.
/** Calls static rippleCreditIOU if saAmount represents Issue.
* Calls static rippleCreditMPT if saAmount represents MPTIssue.
*/
TER
rippleCredit(
ApplyView& view,
AccountID const& uSenderID,
AccountID const& uReceiverID,
STAmount const& saAmount,
bool bCheckIssuer,
beast::Journal j);
/** Calls static accountSendIOU if saAmount represents Issue.
* Calls static accountSendMPT if saAmount represents MPTIssue.
*/
[[nodiscard]] TER
accountSend(
ApplyView& view,
AccountID const& from,
AccountID const& to,
STAmount const& saAmount,
beast::Journal j,
WaiveTransferFee waiveFee = WaiveTransferFee::No);
using MultiplePaymentDestinations = std::vector<std::pair<AccountID, Number>>;
/** Like accountSend, except one account is sending multiple payments (with the
* same asset!) simultaneously
*
* Calls static accountSendMultiIOU if saAmount represents Issue.
* Calls static accountSendMultiMPT if saAmount represents MPTIssue.
*/
[[nodiscard]] TER
accountSendMulti(
ApplyView& view,
AccountID const& senderID,
Asset const& asset,
MultiplePaymentDestinations const& receivers,
beast::Journal j,
WaiveTransferFee waiveFee = WaiveTransferFee::No);
[[nodiscard]] TER
transferXRP(
ApplyView& view,
AccountID const& from,
AccountID const& to,
STAmount const& amount,
beast::Journal j);
} // namespace xrpl

View File

@@ -0,0 +1,81 @@
#pragma once
#include <xrpl/protocol/STAmount.h>
#include <xrpl/protocol/STLedgerEntry.h>
#include <memory>
#include <optional>
namespace xrpl {
/** From the perspective of a vault, return the number of shares to give
depositor when they offer a fixed amount of assets. Note, since shares are
MPT, this number is integral and always truncated in this calculation.
@param vault The vault SLE.
@param issuance The MPTokenIssuance SLE for the vault's shares.
@param assets The amount of assets to convert.
@return The number of shares, or nullopt on error.
*/
[[nodiscard]] std::optional<STAmount>
assetsToSharesDeposit(
std::shared_ptr<SLE const> const& vault,
std::shared_ptr<SLE const> const& issuance,
STAmount const& assets);
/** From the perspective of a vault, return the number of assets to take from
depositor when they receive a fixed amount of shares. Note, since shares are
MPT, they are always an integral number.
@param vault The vault SLE.
@param issuance The MPTokenIssuance SLE for the vault's shares.
@param shares The amount of shares to convert.
@return The number of assets, or nullopt on error.
*/
[[nodiscard]] std::optional<STAmount>
sharesToAssetsDeposit(
std::shared_ptr<SLE const> const& vault,
std::shared_ptr<SLE const> const& issuance,
STAmount const& shares);
/** Controls whether to truncate shares instead of rounding. */
enum class TruncateShares : bool { no = false, yes = true };
/** From the perspective of a vault, return the number of shares to demand from
the depositor when they ask to withdraw a fixed amount of assets. Since
shares are MPT this number is integral, and it will be rounded to nearest
unless explicitly requested to be truncated instead.
@param vault The vault SLE.
@param issuance The MPTokenIssuance SLE for the vault's shares.
@param assets The amount of assets to convert.
@param truncate Whether to truncate instead of rounding.
@return The number of shares, or nullopt on error.
*/
[[nodiscard]] std::optional<STAmount>
assetsToSharesWithdraw(
std::shared_ptr<SLE const> const& vault,
std::shared_ptr<SLE const> const& issuance,
STAmount const& assets,
TruncateShares truncate = TruncateShares::no);
/** From the perspective of a vault, return the number of assets to give the
depositor when they redeem a fixed amount of shares. Note, since shares are
MPT, they are always an integral number.
@param vault The vault SLE.
@param issuance The MPTokenIssuance SLE for the vault's shares.
@param shares The amount of shares to convert.
@return The number of assets, or nullopt on error.
*/
[[nodiscard]] std::optional<STAmount>
sharesToAssetsWithdraw(
std::shared_ptr<SLE const> const& vault,
std::shared_ptr<SLE const> const& issuance,
STAmount const& shares);
} // namespace xrpl

View File

@@ -4,6 +4,10 @@
namespace xrpl {
// Deprecated constant for backwards compatibility with pre-XRPFees amendment.
// This was the reference fee units used in the old fee calculation.
inline constexpr std::uint32_t FEE_UNITS_DEPRECATED = 10;
/** Reflects the fee settings for a particular ledger.
The fees are always the same for any transactions applied
@@ -11,15 +15,25 @@ namespace xrpl {
*/
struct Fees
{
XRPAmount base{0}; // Reference tx cost (drops)
XRPAmount reserve{0}; // Reserve base (drops)
XRPAmount increment{0}; // Reserve increment (drops)
/** @brief Cost of a reference transaction in drops. */
XRPAmount base{0};
/** @brief Minimum XRP an account must hold to exist on the ledger. */
XRPAmount reserve{0};
/** @brief Additional XRP reserve required per owned ledger object. */
XRPAmount increment{0};
explicit Fees() = default;
Fees(Fees const&) = default;
Fees&
operator=(Fees const&) = default;
Fees(XRPAmount base_, XRPAmount reserve_, XRPAmount increment_)
: base(base_), reserve(reserve_), increment(increment_)
{
}
/** Returns the account reserve given the owner count, in drops.
The reserve is calculated as the reserve base plus

View File

@@ -72,4 +72,8 @@ deserializeHeader(Slice data, bool hasHash = false);
LedgerHeader
deserializePrefixedHeader(Slice data, bool hasHash = false);
/** Calculate the hash of a ledger header. */
uint256
calculateLedgerHash(LedgerHeader const& info);
} // namespace xrpl

View File

@@ -72,12 +72,12 @@ public:
isDelegable(std::uint32_t const& permissionValue, Rules const& rules) const;
// for tx level permission, permission value is equal to tx type plus one
uint32_t
txToPermissionType(TxType const& type) const;
static uint32_t
txToPermissionType(TxType const& type);
// tx type value is permission value minus one
TxType
permissionToTxType(uint32_t const& value) const;
static TxType
permissionToTxType(uint32_t const& value);
};
} // namespace xrpl

View File

@@ -69,9 +69,6 @@ public:
std::vector<uint256>::iterator
insert(std::vector<uint256>::const_iterator pos, uint256 const& value);
std::vector<uint256>::iterator
insert(std::vector<uint256>::const_iterator pos, uint256&& value);
void
push_back(uint256 const& v);
@@ -184,12 +181,6 @@ STVector256::insert(std::vector<uint256>::const_iterator pos, uint256 const& val
return mValue.insert(pos, value);
}
inline std::vector<uint256>::iterator
STVector256::insert(std::vector<uint256>::const_iterator pos, uint256&& value)
{
return mValue.insert(pos, std::move(value));
}
inline void
STVector256::push_back(uint256 const& v)
{

View File

@@ -336,7 +336,7 @@ public:
static_assert(N > 0, "");
}
std::size_t
[[nodiscard]] bool
empty() const noexcept
{
return remain_ == 0;

View File

@@ -2,6 +2,6 @@
namespace xrpl {
enum class TxSearched { all, some, unknown };
enum class TxSearched { All, Some, Unknown };
}

View File

@@ -16,6 +16,7 @@
// Add new amendments to the top of this list.
// Keep it sorted in reverse chronological order.
XRPL_FIX (Security3_1_3, Supported::no, VoteBehavior::DefaultNo)
XRPL_FIX (PermissionedDomainInvariant, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (ExpiredNFTokenOfferRemoval, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (BatchInnerSigs, Supported::no, VoteBehavior::DefaultNo)

View File

@@ -1,6 +1,7 @@
#pragma once
#include <xrpl/core/PerfLog.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/core/StartUpType.h>
#include <xrpl/rdb/DBInit.h>
#include <xrpl/rdb/SociDB.h>
@@ -69,7 +70,7 @@ public:
{
explicit Setup() = default;
StartUpType startUp = StartUpType::NORMAL;
StartUpType startUp = StartUpType::Normal;
bool standAlone = false;
boost::filesystem::path dataDir;
// Indicates whether or not to return the `globalPragma`
@@ -94,7 +95,7 @@ public:
struct CheckpointerSetup
{
JobQueue* jobQueue;
Logs* logs;
std::reference_wrapper<ServiceRegistry> registry;
};
template <std::size_t N, std::size_t M>
@@ -106,9 +107,8 @@ public:
beast::Journal journal)
// Use temporary files or regular DB files?
: DatabaseCon(
setup.standAlone && setup.startUp != StartUpType::LOAD &&
setup.startUp != StartUpType::LOAD_FILE &&
setup.startUp != StartUpType::REPLAY
setup.standAlone && setup.startUp != StartUpType::Load &&
setup.startUp != StartUpType::LoadFile && setup.startUp != StartUpType::Replay
? ""
: (setup.dataDir / dbName),
setup.commonPragma(),
@@ -129,7 +129,7 @@ public:
beast::Journal journal)
: DatabaseCon(setup, dbName, pragma, initSQL, journal)
{
setupCheckpointing(checkpointerSetup.jobQueue, *checkpointerSetup.logs);
setupCheckpointing(checkpointerSetup.jobQueue, checkpointerSetup.registry.get());
}
template <std::size_t N, std::size_t M>
@@ -154,7 +154,7 @@ public:
beast::Journal journal)
: DatabaseCon(dataDir, dbName, pragma, initSQL, journal)
{
setupCheckpointing(checkpointerSetup.jobQueue, *checkpointerSetup.logs);
setupCheckpointing(checkpointerSetup.jobQueue, checkpointerSetup.registry.get());
}
~DatabaseCon();
@@ -177,7 +177,7 @@ public:
private:
void
setupCheckpointing(JobQueue*, Logs&);
setupCheckpointing(JobQueue*, ServiceRegistry&);
template <std::size_t N, std::size_t M>
DatabaseCon(

View File

@@ -49,8 +49,9 @@ public:
struct AccountTxOptions
{
AccountID const& account;
std::uint32_t minLedger;
std::uint32_t maxLedger;
/// Ledger sequence range to search. A value of 0 for min or max
/// means unbounded in that direction (no constraint applied).
LedgerRange ledgerRange;
std::uint32_t offset;
std::uint32_t limit;
bool bUnlimited;
@@ -59,8 +60,7 @@ public:
struct AccountTxPageOptions
{
AccountID const& account;
std::uint32_t minLedger;
std::uint32_t maxLedger;
LedgerRange ledgerRange;
std::optional<AccountTxMarker> marker;
std::uint32_t limit;
bool bAdmin;
@@ -247,7 +247,7 @@ public:
* @return Struct CountMinMax which contains the minimum sequence,
* maximum sequence and number of ledgers.
*/
virtual struct CountMinMax
virtual CountMinMax
getLedgerCountMinMax() = 0;
/**
@@ -405,10 +405,10 @@ public:
* @param id Hash of the transaction.
* @param range Range of ledgers to check, if present.
* @param ec Default error code value.
* @return Transaction and its metadata if found, otherwise TxSearched::all
* @return Transaction and its metadata if found, otherwise TxSearched::All
* if a range is provided and all ledgers from the range are present
* in the database, TxSearched::some if a range is provided and not
* all ledgers are present, TxSearched::unknown if the range is not
* in the database, TxSearched::Some if a range is provided and not
* all ledgers are present, TxSearched::Unknown if the range is not
* provided or a deserializing error occurred. In the last case the
* error code is returned via the ec parameter, in other cases the
* default error code is not changed.
@@ -455,9 +455,10 @@ public:
closeTransactionDB() = 0;
};
template <class T, class C>
template <typename T, typename C>
T
rangeCheckedCast(C c)
requires(std::is_arithmetic_v<T> && std::is_arithmetic_v<C> && std::convertible_to<C, T>)
{
if ((c > std::numeric_limits<T>::max()) || (!std::numeric_limits<T>::is_signed && c < 0) ||
(std::numeric_limits<T>::is_signed && std::numeric_limits<C>::is_signed &&

View File

@@ -13,8 +13,8 @@
#pragma clang diagnostic ignored "-Wdeprecated"
#endif
#include <xrpl/basics/Log.h>
#include <xrpl/core/JobQueue.h>
#include <xrpl/core/ServiceRegistry.h>
#define SOCI_USE_BOOST
#include <soci/soci.h>
@@ -111,7 +111,7 @@ public:
and so must outlive them both.
*/
std::shared_ptr<Checkpointer>
makeCheckpointer(std::uintptr_t id, std::weak_ptr<soci::session>, JobQueue&, Logs&);
makeCheckpointer(std::uintptr_t id, std::weak_ptr<soci::session>, JobQueue&, ServiceRegistry&);
} // namespace xrpl

View File

@@ -520,7 +520,7 @@ private:
// getMissingNodes helper functions
void
gmn_ProcessNodes(MissingNodes&, MissingNodes::StackEntry& node);
void
static void
gmn_ProcessDeferredReads(MissingNodes&);
// fetch from DB helper function

View File

@@ -37,7 +37,7 @@ public:
XRPL_ASSERT((flags & tapBATCH) == 0, "Batch apply flag should not be set");
}
ServiceRegistry& registry;
std::reference_wrapper<ServiceRegistry> registry;
STTx const& tx;
TER const preclaimResult;
XRPAmount const baseFee;
@@ -111,7 +111,7 @@ public:
checkInvariants(TER const result, XRPAmount const fee);
private:
TER
static TER
failInvariantCheck(TER const result);
template <std::size_t... Is>

View File

@@ -13,7 +13,7 @@ namespace xrpl {
struct PreflightContext
{
public:
ServiceRegistry& registry;
std::reference_wrapper<ServiceRegistry> registry;
STTx const& tx;
Rules const rules;
ApplyFlags flags;
@@ -56,7 +56,7 @@ public:
struct PreclaimContext
{
public:
ServiceRegistry& registry;
std::reference_wrapper<ServiceRegistry> registry;
ReadView const& view;
TER preflightResult;
ApplyFlags flags;

View File

@@ -48,7 +48,7 @@ private:
bool
isValidEntry(std::shared_ptr<SLE const> const& before, std::shared_ptr<SLE const> const& after);
STAmount
static STAmount
calculateBalanceChange(
std::shared_ptr<SLE const> const& before,
std::shared_ptr<SLE const> const& after,
@@ -63,7 +63,7 @@ private:
std::shared_ptr<SLE const>
findIssuer(AccountID const& issuerID, ReadView const& view);
bool
static bool
validateIssuerChanges(
std::shared_ptr<SLE const> const& issuer,
IssuerChanges const& changes,
@@ -71,7 +71,7 @@ private:
beast::Journal const& j,
bool enforce);
bool
static bool
validateFrozenState(
BalanceChange const& change,
bool high,

View File

@@ -7,6 +7,7 @@
#include <xrpl/protocol/TER.h>
#include <xrpl/tx/invariants/AMMInvariant.h>
#include <xrpl/tx/invariants/FreezeInvariant.h>
#include <xrpl/tx/invariants/LoanBrokerInvariant.h>
#include <xrpl/tx/invariants/LoanInvariant.h>
#include <xrpl/tx/invariants/MPTInvariant.h>
#include <xrpl/tx/invariants/NFTInvariant.h>
@@ -111,7 +112,7 @@ public:
void
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
bool
static bool
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
};

View File

@@ -0,0 +1,55 @@
#pragma once
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/TER.h>
#include <map>
#include <vector>
namespace xrpl {
/**
* @brief Invariants: Loan brokers are internally consistent
*
* 1. If `LoanBroker.OwnerCount = 0` the `DirectoryNode` will have at most one
* node (the root), which will only hold entries for `RippleState` or
* `MPToken` objects.
*
*/
class ValidLoanBroker
{
// Not all of these elements will necessarily be populated. Remaining items
// will be looked up as needed.
struct BrokerInfo
{
SLE::const_pointer brokerBefore = nullptr;
// After is used for most of the checks, except
// those that check changed values.
SLE::const_pointer brokerAfter = nullptr;
};
// Collect all the LoanBrokers found directly or indirectly through
// pseudo-accounts. Key is the brokerID / index. It will be used to find the
// LoanBroker object if brokerBefore and brokerAfter are nullptr
std::map<uint256, BrokerInfo> brokers_;
// Collect all the modified trust lines. Their high and low accounts will be
// loaded to look for LoanBroker pseudo-accounts.
std::vector<SLE::const_pointer> lines_;
// Collect all the modified MPTokens. Their accounts will be loaded to look
// for LoanBroker pseudo-accounts.
std::vector<SLE::const_pointer> mpts_;
static bool
goodZeroDirectory(ReadView const& view, SLE::const_ref dir, beast::Journal const& j);
public:
void
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
bool
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
};
} // namespace xrpl

View File

@@ -1,57 +1,14 @@
#pragma once
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/TER.h>
#include <map>
#include <vector>
namespace xrpl {
/**
* @brief Invariants: Loan brokers are internally consistent
*
* 1. If `LoanBroker.OwnerCount = 0` the `DirectoryNode` will have at most one
* node (the root), which will only hold entries for `RippleState` or
* `MPToken` objects.
*
*/
class ValidLoanBroker
{
// Not all of these elements will necessarily be populated. Remaining items
// will be looked up as needed.
struct BrokerInfo
{
SLE::const_pointer brokerBefore = nullptr;
// After is used for most of the checks, except
// those that check changed values.
SLE::const_pointer brokerAfter = nullptr;
};
// Collect all the LoanBrokers found directly or indirectly through
// pseudo-accounts. Key is the brokerID / index. It will be used to find the
// LoanBroker object if brokerBefore and brokerAfter are nullptr
std::map<uint256, BrokerInfo> brokers_;
// Collect all the modified trust lines. Their high and low accounts will be
// loaded to look for LoanBroker pseudo-accounts.
std::vector<SLE::const_pointer> lines_;
// Collect all the modified MPTokens. Their accounts will be loaded to look
// for LoanBroker pseudo-accounts.
std::vector<SLE::const_pointer> mpts_;
bool
goodZeroDirectory(ReadView const& view, SLE::const_ref dir, beast::Journal const& j) const;
public:
void
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
bool
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
};
/**
* @brief Invariants: Loans are internally consistent
*

View File

@@ -0,0 +1,131 @@
#pragma once
#include <xrpl/basics/Log.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/View.h>
#include <xrpl/protocol/Quality.h>
#include <xrpl/tx/transactors/dex/AMMContext.h>
#include <xrpl/tx/transactors/dex/AMMHelpers.h>
#include <xrpl/tx/transactors/dex/AMMUtils.h>
namespace xrpl {
template <typename TIn, typename TOut>
class AMMOffer;
/** AMMLiquidity class provides AMM offers to BookStep class.
* The offers are generated in two ways. If there are multiple
* paths specified to the payment transaction then the offers
* are generated based on the Fibonacci sequence with
* a limited number of payment engine iterations consuming AMM offers.
* These offers behave the same way as CLOB offers in that if
* there is a limiting step, then the offers are adjusted
* based on their quality.
* If there is only one path specified in the payment transaction
* then the offers are generated based on the competing CLOB offer
* quality. In this case the offer's size is set in such a way
* that the new AMM's pool spot price quality is equal to the CLOB's
* offer quality.
*/
template <typename TIn, typename TOut>
class AMMLiquidity
{
private:
inline static Number const InitialFibSeqPct = Number(5) / 20000;
AMMContext& ammContext_;
AccountID const ammAccountID_;
std::uint32_t const tradingFee_;
Issue const issueIn_;
Issue const issueOut_;
// Initial AMM pool balances
TAmounts<TIn, TOut> const initialBalances_;
beast::Journal const j_;
public:
AMMLiquidity(
ReadView const& view,
AccountID const& ammAccountID,
std::uint32_t tradingFee,
Issue const& in,
Issue const& out,
AMMContext& ammContext,
beast::Journal j);
~AMMLiquidity() = default;
AMMLiquidity(AMMLiquidity const&) = delete;
AMMLiquidity&
operator=(AMMLiquidity const&) = delete;
/** Generate AMM offer. Returns nullopt if clobQuality is provided
* and it is better than AMM offer quality. Otherwise returns AMM offer.
* If clobQuality is provided then AMM offer size is set based on the
* quality.
*/
std::optional<AMMOffer<TIn, TOut>>
getOffer(ReadView const& view, std::optional<Quality> const& clobQuality) const;
AccountID const&
ammAccount() const
{
return ammAccountID_;
}
bool
multiPath() const
{
return ammContext_.multiPath();
}
std::uint32_t
tradingFee() const
{
return tradingFee_;
}
AMMContext&
context() const
{
return ammContext_;
}
Issue const&
issueIn() const
{
return issueIn_;
}
Issue const&
issueOut() const
{
return issueOut_;
}
private:
/** Fetches current AMM balances.
*/
TAmounts<TIn, TOut>
fetchBalances(ReadView const& view) const;
/** Generate AMM offers with the offer size based on Fibonacci sequence.
* The sequence corresponds to the payment engine iterations with AMM
* liquidity. Iterations that don't consume AMM offers don't count.
* The number of iterations with AMM offers is limited.
* If the generated offer exceeds the pool balance then the function
* throws overflow exception.
*/
TAmounts<TIn, TOut>
generateFibSeqOffer(TAmounts<TIn, TOut> const& balances) const;
/** Generate max offer.
* If `fixAMMOverflowOffer` is active, the offer is generated as:
* takerGets = 99% * balances.out takerPays = swapOut(takerGets).
* Return nullopt if takerGets is 0 or takerGets == balances.out.
*
* If `fixAMMOverflowOffer` is not active, the offer is generated as:
* takerPays = max input amount;
* takerGets = swapIn(takerPays).
*/
std::optional<AMMOffer<TIn, TOut>>
maxOffer(TAmounts<TIn, TOut> const& balances, Rules const& rules) const;
};
} // namespace xrpl

View File

@@ -0,0 +1,126 @@
#pragma once
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/Quality.h>
#include <xrpl/protocol/TER.h>
namespace xrpl {
template <typename TIn, typename TOut>
class AMMLiquidity;
class QualityFunction;
/** Represents synthetic AMM offer in BookStep. AMMOffer mirrors TOffer
* methods for use in generic BookStep methods. AMMOffer amounts
* are changed indirectly in BookStep limiting steps.
*/
template <typename TIn, typename TOut>
class AMMOffer
{
private:
AMMLiquidity<TIn, TOut> const& ammLiquidity_;
// Initial offer amounts. It is fibonacci seq generated for multi-path.
// If the offer size is set based on the competing CLOB offer then
// the AMM offer size is such that if the offer is consumed then
// the updated AMM pool SP quality is going to be equal to competing
// CLOB offer quality. If there is no competing CLOB offer then
// the initial size is set to in=cMax[Native,Value],balances.out.
// While this is not a "real" offer it simulates the case of
// the swap out of the entire side of the pool, in which case
// the swap in amount is infinite.
TAmounts<TIn, TOut> const amounts_;
// Current pool balances.
TAmounts<TIn, TOut> const balances_;
// The Spot Price quality if balances != amounts
// else the amounts quality
Quality const quality_;
// AMM offer can be consumed once at a given iteration
bool consumed_;
public:
AMMOffer(
AMMLiquidity<TIn, TOut> const& ammLiquidity,
TAmounts<TIn, TOut> const& amounts,
TAmounts<TIn, TOut> const& balances,
Quality const& quality);
Quality
quality() const noexcept
{
return quality_;
}
Issue const&
issueIn() const;
AccountID const&
owner() const;
std::optional<uint256>
key() const
{
return std::nullopt;
}
TAmounts<TIn, TOut> const&
amount() const;
void
consume(ApplyView& view, TAmounts<TIn, TOut> const& consumed);
bool
fully_consumed() const
{
return consumed_;
}
/** Limit out of the provided offer. If one-path then swapOut
* using current balances. If multi-path then ceil_out using
* current quality.
*/
TAmounts<TIn, TOut>
limitOut(TAmounts<TIn, TOut> const& offerAmount, TOut const& limit, bool roundUp) const;
/** Limit in of the provided offer. If one-path then swapIn
* using current balances. If multi-path then ceil_in using
* current quality.
*/
TAmounts<TIn, TOut>
limitIn(TAmounts<TIn, TOut> const& offerAmount, TIn const& limit, bool roundUp) const;
QualityFunction
getQualityFunc() const;
/** Send funds without incurring the transfer fee
*/
template <typename... Args>
static TER
send(Args&&... args)
{
return accountSend(std::forward<Args>(args)..., WaiveTransferFee::Yes);
}
bool
isFunded() const
{
// AMM offer is fully funded by the pool
return true;
}
static std::pair<std::uint32_t, std::uint32_t>
adjustRates(std::uint32_t ofrInRate, std::uint32_t ofrOutRate)
{
// AMM doesn't pay transfer fee on Payment tx
return {ofrInRate, QUALITY_ONE};
}
/** Check the new pool product is greater or equal to the old pool
* product or if decreases then within some threshold.
*/
bool
checkInvariant(TAmounts<TIn, TOut> const& consumed, beast::Journal j) const;
};
} // namespace xrpl

View File

@@ -1,6 +1,6 @@
#pragma once
#include <xrpl/basics/Log.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/PaymentSandbox.h>
#include <xrpl/protocol/STAmount.h>
#include <xrpl/protocol/TER.h>
@@ -92,7 +92,7 @@ public:
STPathSet const& spsPaths,
std::optional<uint256> const& domainID,
Logs& l,
ServiceRegistry& registry,
Input const* const pInputs = nullptr);
// The view we are currently working on

View File

@@ -0,0 +1,92 @@
#pragma once
#include <xrpl/basics/Log.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/View.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/UintTypes.h>
namespace xrpl {
inline TER
checkFreeze(
ReadView const& view,
AccountID const& src,
AccountID const& dst,
Currency const& currency)
{
XRPL_ASSERT(src != dst, "xrpl::checkFreeze : unequal input accounts");
// check freeze
if (auto sle = view.read(keylet::account(dst)))
{
if (sle->isFlag(lsfGlobalFreeze))
{
return terNO_LINE;
}
}
if (auto sle = view.read(keylet::line(src, dst, currency)))
{
if (sle->isFlag((dst > src) ? lsfHighFreeze : lsfLowFreeze))
{
return terNO_LINE;
}
// Unlike normal freeze, a deep frozen trust line acts the same
// regardless of which side froze it
if (sle->isFlag(lsfHighDeepFreeze) || sle->isFlag(lsfLowDeepFreeze))
{
return terNO_LINE;
}
}
if (view.rules().enabled(fixFrozenLPTokenTransfer))
{
if (auto const sleDst = view.read(keylet::account(dst));
sleDst && sleDst->isFieldPresent(sfAMMID))
{
auto const sleAmm = view.read(keylet::amm((*sleDst)[sfAMMID]));
if (!sleAmm)
return tecINTERNAL; // LCOV_EXCL_LINE
if (isLPTokenFrozen(
view, src, (*sleAmm)[sfAsset].get<Issue>(), (*sleAmm)[sfAsset2].get<Issue>()))
{
return terNO_LINE;
}
}
}
return tesSUCCESS;
}
inline TER
checkNoRipple(
ReadView const& view,
AccountID const& prev,
AccountID const& cur,
// This is the account whose constraints we are checking
AccountID const& next,
Currency const& currency,
beast::Journal j)
{
// fetch the ripple lines into and out of this node
auto sleIn = view.read(keylet::line(prev, cur, currency));
auto sleOut = view.read(keylet::line(cur, next, currency));
if (!sleIn || !sleOut)
return terNO_LINE;
if ((*sleIn)[sfFlags] & ((cur > prev) ? lsfHighNoRipple : lsfLowNoRipple) &&
(*sleOut)[sfFlags] & ((cur > next) ? lsfHighNoRipple : lsfLowNoRipple))
{
JLOG(j.info()) << "Path violates noRipple constraint between " << prev << ", " << cur
<< " and " << next;
return terNO_RIPPLE;
}
return tesSUCCESS;
}
} // namespace xrpl

View File

@@ -1,7 +1,9 @@
#pragma once
#include <xrpl/basics/Log.h>
#include <xrpl/ledger/Credit.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/helpers/OfferHelpers.h>
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/IOUAmount.h>
#include <xrpl/protocol/XRPAmount.h>

View File

@@ -203,7 +203,7 @@ getAMMOfferStartWithTakerGets(
// Try to reduce the offer size to improve the quality.
// The quality might still not match the targetQuality for a tiny offer.
if (auto const amounts = getAmounts(*nTakerGets); Quality{amounts} < targetQuality)
if (auto amounts = getAmounts(*nTakerGets); Quality{amounts} < targetQuality)
return getAmounts(detail::reduceOffer(amounts.out));
else
return amounts;
@@ -270,7 +270,7 @@ getAMMOfferStartWithTakerPays(
// Try to reduce the offer size to improve the quality.
// The quality might still not match the targetQuality for a tiny offer.
if (auto const amounts = getAmounts(*nTakerPays); Quality{amounts} < targetQuality)
if (auto amounts = getAmounts(*nTakerPays); Quality{amounts} < targetQuality)
return getAmounts(detail::reduceOffer(amounts.in));
else
return amounts;
@@ -335,8 +335,7 @@ changeSpotPriceQuality(
}
auto const takerPays = toAmount<TIn>(getIssue(pool.in), nTakerPays, Number::upward);
// should not fail
if (auto const amounts =
TAmounts<TIn, TOut>{takerPays, swapAssetIn(pool, takerPays, tfee)};
if (auto amounts = TAmounts<TIn, TOut>{takerPays, swapAssetIn(pool, takerPays, tfee)};
Quality{amounts} < quality &&
!withinRelativeDistance(Quality{amounts}, quality, Number(1, -7)))
{
@@ -362,7 +361,7 @@ changeSpotPriceQuality(
// Generate the offer starting with XRP side. Return seated offer amounts
// if the offer can be generated, otherwise nullopt.
auto const amounts = [&]() {
auto amounts = [&]() {
if (isXRP(getIssue(pool.out)))
return getAMMOfferStartWithTakerGets(pool, quality, tfee);
return getAMMOfferStartWithTakerPays(pool, quality, tfee);

View File

@@ -3,6 +3,7 @@
#include <xrpl/basics/Expected.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
#include <xrpl/protocol/STAmount.h>
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/TER.h>

View File

@@ -1,6 +1,7 @@
#pragma once
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
#include <xrpl/tx/Transactor.h>
namespace xrpl {