1#include <xrpl/tx/transactors/oracle/OracleSet.h>
3#include <xrpl/basics/chrono.h>
4#include <xrpl/core/ServiceRegistry.h>
5#include <xrpl/ledger/helpers/AccountRootHelpers.h>
6#include <xrpl/ledger/helpers/DirectoryHelpers.h>
7#include <xrpl/protocol/Feature.h>
8#include <xrpl/protocol/Indexes.h>
9#include <xrpl/protocol/InnerObjectFormats.h>
10#include <xrpl/protocol/Protocol.h>
11#include <xrpl/protocol/SField.h>
12#include <xrpl/protocol/SOTemplate.h>
13#include <xrpl/protocol/STLedgerEntry.h>
14#include <xrpl/protocol/STObject.h>
15#include <xrpl/protocol/STTx.h>
16#include <xrpl/protocol/TER.h>
17#include <xrpl/protocol/UintTypes.h>
18#include <xrpl/protocol/XRPAmount.h>
19#include <xrpl/tx/ApplyContext.h>
20#include <xrpl/tx/Transactor.h>
32static inline std::pair<Currency, Currency>
44 if (dataSeries.empty())
49 auto isInvalidLength = [&](
auto const& sField,
std::size_t length) {
51 (ctx.
tx[sField].length() == 0 || ctx.
tx[sField].length() > length);
93 if (entry[sfBaseAsset] == entry[sfQuoteAsset])
100 if (entry.isFieldPresent(sfAssetPrice))
118 auto const v = ctx.
tx[~field];
119 return !v || *v == (*sle)[field];
129 if (ctx.
tx[sfLastUpdateTime] <= (*sle)[sfLastUpdateTime])
135 for (
auto const& entry : sle->getFieldArray(sfPriceDataSeries))
150 if (!pairsDel.
empty())
153 auto const oldCount = sle->getFieldArray(sfPriceDataSeries).size() > 5 ? 2 : 1;
154 auto const newCount = pairs.
size() > 5 ? 2 : 1;
155 adjustReserve = newCount - oldCount;
163 adjustReserve = pairs.
size() > 5 ? 2 : 1;
173 auto const& balance = sleSetter->getFieldAmount(sfBalance);
175 if (balance < reserve)
208 priceData.
setFieldCurrency(sfBaseAsset, entry.getFieldCurrency(sfBaseAsset));
209 priceData.
setFieldCurrency(sfQuoteAsset, entry.getFieldCurrency(sfQuoteAsset));
210 priceData.
setFieldU64(sfAssetPrice, entry.getFieldU64(sfAssetPrice));
211 if (entry.isFieldPresent(sfScale))
212 priceData.
setFieldU8(sfScale, entry.getFieldU8(sfScale));
215 if (
auto sle =
ctx_.view().peek(oracleID))
223 for (
auto const& entry : sle->getFieldArray(sfPriceDataSeries))
227 priceData.
setFieldCurrency(sfBaseAsset, entry.getFieldCurrency(sfBaseAsset));
228 priceData.
setFieldCurrency(sfQuoteAsset, entry.getFieldCurrency(sfQuoteAsset));
231 auto const oldCount = pairs.
size() > 5 ? 2 : 1;
233 for (
auto const& entry :
ctx_.tx.getFieldArray(sfPriceDataSeries))
236 if (!entry.isFieldPresent(sfAssetPrice))
241 else if (
auto iter = pairs.
find(key); iter != pairs.
end())
244 iter->second.setFieldU64(sfAssetPrice, entry.getFieldU64(sfAssetPrice));
245 if (entry.isFieldPresent(sfScale))
246 iter->second.setFieldU8(sfScale, entry.getFieldU8(sfScale));
252 populatePriceData(priceData, entry);
253 pairs.
emplace(key, std::move(priceData));
257 for (
auto const& iter : pairs)
258 updatedSeries.
pushBack(iter.second);
259 sle->setFieldArray(sfPriceDataSeries, updatedSeries);
260 if (
ctx_.tx.isFieldPresent(sfURI))
261 sle->setFieldVL(sfURI,
ctx_.tx[sfURI]);
262 sle->setFieldU32(sfLastUpdateTime,
ctx_.tx[sfLastUpdateTime]);
263 if (!sle->isFieldPresent(sfOracleDocumentID) &&
264 ctx_.view().rules().enabled(fixIncludeKeyletFields))
266 (*sle)[sfOracleDocumentID] =
ctx_.tx[sfOracleDocumentID];
269 auto const newCount = pairs.
size() > 5 ? 2 : 1;
270 auto const adjust = newCount - oldCount;
274 ctx_.view().update(sle);
281 sle->setAccountID(sfOwner,
ctx_.tx.getAccountID(sfAccount));
282 if (
ctx_.view().rules().enabled(fixIncludeKeyletFields))
284 (*sle)[sfOracleDocumentID] =
ctx_.tx[sfOracleDocumentID];
286 sle->setFieldVL(sfProvider,
ctx_.tx[sfProvider]);
287 if (
ctx_.tx.isFieldPresent(sfURI))
288 sle->setFieldVL(sfURI,
ctx_.tx[sfURI]);
291 if (!
ctx_.view().rules().enabled(fixPriceOracleOrder))
293 series =
ctx_.tx.getFieldArray(sfPriceDataSeries);
298 for (
auto const& entry :
ctx_.tx.getFieldArray(sfPriceDataSeries))
302 populatePriceData(priceData, entry);
303 pairs.
emplace(key, std::move(priceData));
305 for (
auto const& iter : pairs)
309 sle->setFieldArray(sfPriceDataSeries, series);
310 sle->setFieldVL(sfAssetClass,
ctx_.tx[sfAssetClass]);
311 sle->setFieldU32(sfLastUpdateTime,
ctx_.tx[sfLastUpdateTime]);
313 auto page =
ctx_.view().dirInsert(
318 (*sle)[sfOwnerNode] = *page;
320 auto const count = series.
size() > 5 ? 2 : 1;
324 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 Fees const & fees() const =0
Returns the fees for the base ledger.
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)
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.
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.
constexpr std::size_t kMaxOracleUri
The maximum length of a URI inside an Oracle.
void adjustOwnerCount(ApplyView &view, SLE::ref sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
std::function< void(SLE::ref)> describeOwnerDir(AccountID const &account)
Returns a function that sets the owner on a directory SLE.
TERSubset< CanCvtToTER > TER
bool isConsistent(Asset const &asset)
@ tecINSUFFICIENT_RESERVE
@ tecTOKEN_PAIR_NOT_FOUND
constexpr std::size_t kMaxOracleDataSeries
The maximum size of a data series array inside an Oracle.
XRPAmount accountReserve(std::size_t ownerCount) const
Returns the account reserve given the owner count, in drops.
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)