1#include <xrpl/tx/transactors/oracle/OracleSet.h>
3#include <xrpl/basics/chrono.h>
4#include <xrpl/beast/utility/instrumentation.h>
5#include <xrpl/core/ServiceRegistry.h>
6#include <xrpl/ledger/helpers/AccountRootHelpers.h>
7#include <xrpl/ledger/helpers/DirectoryHelpers.h>
8#include <xrpl/ledger/helpers/OracleHelpers.h>
9#include <xrpl/protocol/Feature.h>
10#include <xrpl/protocol/Indexes.h>
11#include <xrpl/protocol/InnerObjectFormats.h>
12#include <xrpl/protocol/Protocol.h>
13#include <xrpl/protocol/SField.h>
14#include <xrpl/protocol/SOTemplate.h>
15#include <xrpl/protocol/STLedgerEntry.h>
16#include <xrpl/protocol/STObject.h>
17#include <xrpl/protocol/STTx.h>
18#include <xrpl/protocol/TER.h>
19#include <xrpl/protocol/UintTypes.h>
20#include <xrpl/protocol/XRPAmount.h>
21#include <xrpl/tx/ApplyContext.h>
22#include <xrpl/tx/Transactor.h>
35static inline std::pair<Currency, Currency>
47 if (dataSeries.empty())
52 auto isInvalidLength = [&](
auto const& sField,
std::size_t length) {
54 (ctx.
tx[sField].length() == 0 || ctx.
tx[sField].length() > length);
96 if (entry[sfBaseAsset] == entry[sfQuoteAsset])
103 if (entry.isFieldPresent(sfAssetPrice))
121 auto const v = ctx.
tx[~field];
122 return !v || *v == (*sle)[field];
132 if (ctx.
tx[sfLastUpdateTime] <= (*sle)[sfLastUpdateTime])
138 for (
auto const& entry : sle->getFieldArray(sfPriceDataSeries))
153 if (!pairsDel.
empty())
159 adjustReserve = newCount - oldCount;
177 auto const& balance = sleSetter->getFieldAmount(sfBalance);
179 if (balance < reserve)
188 XRPL_ASSERT(std::abs(
count) <= 2,
"xrpl::adjustOracleOwnerCount abs(counter) <= 2");
223 priceData.
setFieldCurrency(sfBaseAsset, entry.getFieldCurrency(sfBaseAsset));
224 priceData.
setFieldCurrency(sfQuoteAsset, entry.getFieldCurrency(sfQuoteAsset));
225 priceData.
setFieldU64(sfAssetPrice, entry.getFieldU64(sfAssetPrice));
226 if (entry.isFieldPresent(sfScale))
227 priceData.
setFieldU8(sfScale, entry.getFieldU8(sfScale));
230 if (
auto sle =
ctx_.view().peek(oracleID))
238 for (
auto const& entry : sle->getFieldArray(sfPriceDataSeries))
242 priceData.
setFieldCurrency(sfBaseAsset, entry.getFieldCurrency(sfBaseAsset));
243 priceData.
setFieldCurrency(sfQuoteAsset, entry.getFieldCurrency(sfQuoteAsset));
248 for (
auto const& entry :
ctx_.tx.getFieldArray(sfPriceDataSeries))
251 if (!entry.isFieldPresent(sfAssetPrice))
256 else if (
auto iter = pairs.
find(key); iter != pairs.
end())
259 iter->second.setFieldU64(sfAssetPrice, entry.getFieldU64(sfAssetPrice));
260 if (entry.isFieldPresent(sfScale))
261 iter->second.setFieldU8(sfScale, entry.getFieldU8(sfScale));
267 populatePriceData(priceData, entry);
268 pairs.
emplace(key, std::move(priceData));
272 for (
auto const& iter : pairs)
273 updatedSeries.
pushBack(iter.second);
274 sle->setFieldArray(sfPriceDataSeries, updatedSeries);
275 if (
ctx_.tx.isFieldPresent(sfURI))
276 sle->setFieldVL(sfURI,
ctx_.tx[sfURI]);
277 sle->setFieldU32(sfLastUpdateTime,
ctx_.tx[sfLastUpdateTime]);
278 if (!sle->isFieldPresent(sfOracleDocumentID) &&
279 ctx_.view().rules().enabled(fixIncludeKeyletFields))
281 (*sle)[sfOracleDocumentID] =
ctx_.tx[sfOracleDocumentID];
285 int32_t const adjust = newCount - oldCount;
290 ctx_.view().update(sle);
297 sle->setAccountID(sfOwner,
ctx_.tx.getAccountID(sfAccount));
298 if (
ctx_.view().rules().enabled(fixIncludeKeyletFields))
300 (*sle)[sfOracleDocumentID] =
ctx_.tx[sfOracleDocumentID];
302 sle->setFieldVL(sfProvider,
ctx_.tx[sfProvider]);
303 if (
ctx_.tx.isFieldPresent(sfURI))
304 sle->setFieldVL(sfURI,
ctx_.tx[sfURI]);
307 if (!
ctx_.view().rules().enabled(fixPriceOracleOrder))
309 series =
ctx_.tx.getFieldArray(sfPriceDataSeries);
314 for (
auto const& entry :
ctx_.tx.getFieldArray(sfPriceDataSeries))
318 populatePriceData(priceData, entry);
319 pairs.
emplace(key, std::move(priceData));
321 for (
auto const& iter : pairs)
325 sle->setFieldArray(sfPriceDataSeries, series);
326 sle->setFieldVL(sfAssetClass,
ctx_.tx[sfAssetClass]);
327 sle->setFieldU32(sfLastUpdateTime,
ctx_.tx[sfLastUpdateTime]);
329 auto page =
ctx_.view().dirInsert(
334 (*sle)[sfOwnerNode] = *page;
340 ctx_.view().insert(sle);
A generic endpoint for log messages.
State information when applying a tx.
beast::Journal const journal
virtual SLE::pointer peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
bool finalizeInvariants(STTx const &tx, TER result, XRPAmount fee, ReadView const &view, beast::Journal const &j) override
Check transaction-specific post-conditions after all entries have been visited.
static TER preclaim(PreclaimContext const &ctx)
static NotTEC preflight(PreflightContext const &ctx)
void visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override
Inspect a single ledger entry modified by this transaction.
virtual SLE::const_pointer read(Keylet const &k) const =0
Return the state item associated with a key.
virtual LedgerHeader const & header() const =0
Returns information about the ledger.
Defines the fields and their attributes within a STObject.
void pushBack(STObject const &object)
Currency const & currency() const
std::shared_ptr< STLedgerEntry const > const & const_ref
void setFieldU8(SField const &field, unsigned char)
STCurrency const & getFieldCurrency(SField const &field) const
void setFieldU64(SField const &field, std::uint64_t)
STArray const & getFieldArray(SField const &field) const
bool isFieldPresent(SField const &field) const
void set(SOTemplate const &)
void setFieldCurrency(SField const &field, STCurrency const &)
AccountID getAccountID(SField const &field) const
AccountID const accountID_
T duration_cast(T... args)
Keylet oracle(AccountID const &account, std::uint32_t const documentID) noexcept
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Keylet account(AccountID const &id) noexcept
AccountID root.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
static constexpr std::chrono::seconds kEpochOffset
Clock for measuring the network time.
static std::pair< Currency, Currency > tokenPairKey(STObject const &pair)
void increaseOwnerCount(ApplyView &view, SLE::ref accountSle, SLE::ref sponsorSle, std::uint32_t count, beast::Journal j)
Increase owner-count fields when the caller supplies the sponsor.
constexpr std::size_t kMaxPriceScale
The maximum price scaling factor.
constexpr std::size_t kMaxLastUpdateTimeDelta
The maximum allowed time difference between lastUpdateTime and the time of the last closed ledger.
static bool adjustOracleOwnerCount(ApplyContext &ctx, int count)
constexpr std::size_t kMaxOracleSymbolClass
The maximum length of a SymbolClass inside an Oracle.
TERSubset< CanCvtToNotTEC > NotTEC
static void setPriceDataInnerObjTemplate(STObject &obj)
constexpr std::size_t kMaxOracleProvider
The maximum length of a Provider inside an Oracle.
void decreaseOwnerCount(ApplyView &view, SLE::ref accountSle, SLE::ref sponsorSle, std::uint32_t count, beast::Journal j)
Decrease owner-count fields when the caller supplies the sponsor.
constexpr std::size_t kMaxOracleUri
The maximum length of a URI inside an Oracle.
std::function< void(SLE::ref)> describeOwnerDir(AccountID const &account)
Returns a function that sets the owner on a directory SLE.
std::uint32_t calculateOracleReserve(T const &priceDataSeries)
TERSubset< CanCvtToTER > TER
bool isConsistent(Asset const &asset)
@ tecINSUFFICIENT_RESERVE
@ tecTOKEN_PAIR_NOT_FOUND
XRPAmount accountReserve(ReadView const &view, SLE::const_ref sle, beast::Journal j, Adjustment adj={})
Returns the account reserve, in drops.
constexpr std::size_t kMaxOracleDataSeries
The maximum size of a data series array inside an Oracle.
State information when determining if a tx is likely to claim a fee.
State information when preflighting a tx.
T time_since_epoch(T... args)