From 6b1ed8f454701f0fb907766769a9f64a6c3ecab5 Mon Sep 17 00:00:00 2001 From: Gregory Tsipenyuk Date: Mon, 29 Apr 2024 16:37:13 -0400 Subject: [PATCH] Revert " Price Oracle (XLS-47d): (#4789) (#4789)" This reverts commit e718378 in order to add input validation in get_aggregate_price and make consistent error handling in Oracle API's. --- Builds/CMake/RippledCore.cmake | 7 - src/ripple/app/tx/impl/DeleteAccount.cpp | 15 - src/ripple/app/tx/impl/DeleteOracle.cpp | 110 --- src/ripple/app/tx/impl/DeleteOracle.h | 64 -- src/ripple/app/tx/impl/InvariantCheck.cpp | 1 - src/ripple/app/tx/impl/SetOracle.cpp | 312 -------- src/ripple/app/tx/impl/SetOracle.h | 57 -- src/ripple/app/tx/impl/applySteps.cpp | 6 - src/ripple/protocol/ErrorCodes.h | 6 +- src/ripple/protocol/Feature.h | 3 +- src/ripple/protocol/Indexes.h | 3 - src/ripple/protocol/LedgerFormats.h | 5 - src/ripple/protocol/Protocol.h | 25 - src/ripple/protocol/SField.h | 15 - src/ripple/protocol/STCurrency.h | 138 ---- src/ripple/protocol/STObject.h | 5 - src/ripple/protocol/TER.h | 9 +- src/ripple/protocol/TxFormats.h | 6 - src/ripple/protocol/impl/ErrorCodes.cpp | 3 +- src/ripple/protocol/impl/Feature.cpp | 1 - src/ripple/protocol/impl/Indexes.cpp | 7 - .../protocol/impl/InnerObjectFormats.cpp | 9 - src/ripple/protocol/impl/LedgerFormats.cpp | 16 - src/ripple/protocol/impl/SField.cpp | 14 +- src/ripple/protocol/impl/STCurrency.cpp | 114 --- src/ripple/protocol/impl/STObject.cpp | 14 - src/ripple/protocol/impl/STParsedJSON.cpp | 13 - src/ripple/protocol/impl/STVar.cpp | 7 - src/ripple/protocol/impl/TER.cpp | 6 - src/ripple/protocol/impl/TxFormats.cpp | 19 - src/ripple/protocol/jss.h | 29 - src/ripple/rpc/handlers/GetAggregatePrice.cpp | 340 --------- src/ripple/rpc/handlers/Handlers.h | 2 - src/ripple/rpc/handlers/LedgerEntry.cpp | 46 -- src/ripple/rpc/impl/Handler.cpp | 4 - src/ripple/rpc/impl/RPCHelpers.cpp | 3 +- src/test/app/Oracle_test.cpp | 698 ------------------ src/test/jtx/Oracle.h | 186 ----- src/test/jtx/impl/Oracle.cpp | 292 -------- src/test/ledger/Directory_test.cpp | 2 +- src/test/rpc/GetAggregatePrice_test.cpp | 260 ------- 41 files changed, 7 insertions(+), 2865 deletions(-) delete mode 100644 src/ripple/app/tx/impl/DeleteOracle.cpp delete mode 100644 src/ripple/app/tx/impl/DeleteOracle.h delete mode 100644 src/ripple/app/tx/impl/SetOracle.cpp delete mode 100644 src/ripple/app/tx/impl/SetOracle.h delete mode 100644 src/ripple/protocol/STCurrency.h delete mode 100644 src/ripple/protocol/impl/STCurrency.cpp delete mode 100644 src/ripple/rpc/handlers/GetAggregatePrice.cpp delete mode 100644 src/test/app/Oracle_test.cpp delete mode 100644 src/test/jtx/Oracle.h delete mode 100644 src/test/jtx/impl/Oracle.cpp delete mode 100644 src/test/rpc/GetAggregatePrice_test.cpp diff --git a/Builds/CMake/RippledCore.cmake b/Builds/CMake/RippledCore.cmake index 6b7b2aae683..97fcb230a47 100644 --- a/Builds/CMake/RippledCore.cmake +++ b/Builds/CMake/RippledCore.cmake @@ -149,7 +149,6 @@ target_sources (xrpl_core PRIVATE src/ripple/protocol/impl/STArray.cpp src/ripple/protocol/impl/STBase.cpp src/ripple/protocol/impl/STBlob.cpp - src/ripple/protocol/impl/STCurrency.cpp src/ripple/protocol/impl/STInteger.cpp src/ripple/protocol/impl/STLedgerEntry.cpp src/ripple/protocol/impl/STObject.cpp @@ -614,7 +613,6 @@ target_sources (rippled PRIVATE src/ripple/app/tx/impl/CreateOffer.cpp src/ripple/app/tx/impl/CreateTicket.cpp src/ripple/app/tx/impl/DeleteAccount.cpp - src/ripple/app/tx/impl/DeleteOracle.cpp src/ripple/app/tx/impl/DepositPreauth.cpp src/ripple/app/tx/impl/DID.cpp src/ripple/app/tx/impl/Escrow.cpp @@ -628,7 +626,6 @@ target_sources (rippled PRIVATE src/ripple/app/tx/impl/PayChan.cpp src/ripple/app/tx/impl/Payment.cpp src/ripple/app/tx/impl/SetAccount.cpp - src/ripple/app/tx/impl/SetOracle.cpp src/ripple/app/tx/impl/SetRegularKey.cpp src/ripple/app/tx/impl/SetSignerList.cpp src/ripple/app/tx/impl/SetTrust.cpp @@ -764,7 +761,6 @@ target_sources (rippled PRIVATE src/ripple/rpc/handlers/FetchInfo.cpp src/ripple/rpc/handlers/GatewayBalances.cpp src/ripple/rpc/handlers/GetCounts.cpp - src/ripple/rpc/handlers/GetAggregatePrice.cpp src/ripple/rpc/handlers/LedgerAccept.cpp src/ripple/rpc/handlers/LedgerCleanerHandler.cpp src/ripple/rpc/handlers/LedgerClosed.cpp @@ -878,7 +874,6 @@ if (tests) src/test/app/NFTokenDir_test.cpp src/test/app/OfferStream_test.cpp src/test/app/Offer_test.cpp - src/test/app/Oracle_test.cpp src/test/app/OversizeMeta_test.cpp src/test/app/Path_test.cpp src/test/app/PayChan_test.cpp @@ -1003,7 +998,6 @@ if (tests) src/test/jtx/impl/AMMTest.cpp src/test/jtx/impl/Env.cpp src/test/jtx/impl/JSONRPCClient.cpp - src/test/jtx/impl/Oracle.cpp src/test/jtx/impl/TestHelpers.cpp src/test/jtx/impl/WSClient.cpp src/test/jtx/impl/acctdelete.cpp @@ -1131,7 +1125,6 @@ if (tests) src/test/rpc/DeliveredAmount_test.cpp src/test/rpc/Feature_test.cpp src/test/rpc/GatewayBalances_test.cpp - src/test/rpc/GetAggregatePrice_test.cpp src/test/rpc/GetCounts_test.cpp src/test/rpc/JSONRPC_test.cpp src/test/rpc/KeyGeneration_test.cpp diff --git a/src/ripple/app/tx/impl/DeleteAccount.cpp b/src/ripple/app/tx/impl/DeleteAccount.cpp index efa38a4b74e..49b645e31d9 100644 --- a/src/ripple/app/tx/impl/DeleteAccount.cpp +++ b/src/ripple/app/tx/impl/DeleteAccount.cpp @@ -19,7 +19,6 @@ #include #include -#include #include #include #include @@ -147,18 +146,6 @@ removeDIDFromLedger( return DIDDelete::deleteSLE(view, sleDel, account, j); } -TER -removeOracleFromLedger( - Application&, - ApplyView& view, - AccountID const& account, - uint256 const&, - std::shared_ptr const& sleDel, - beast::Journal j) -{ - return DeleteOracle::deleteOracle(view, sleDel, account, j); -} - // Return nullptr if the LedgerEntryType represents an obligation that can't // be deleted. Otherwise return the pointer to the function that can delete // the non-obligation @@ -179,8 +166,6 @@ nonObligationDeleter(LedgerEntryType t) return removeNFTokenOfferFromLedger; case ltDID: return removeDIDFromLedger; - case ltORACLE: - return removeOracleFromLedger; default: return nullptr; } diff --git a/src/ripple/app/tx/impl/DeleteOracle.cpp b/src/ripple/app/tx/impl/DeleteOracle.cpp deleted file mode 100644 index dfaecc384d4..00000000000 --- a/src/ripple/app/tx/impl/DeleteOracle.cpp +++ /dev/null @@ -1,110 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2023 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#include -#include -#include -#include -#include -#include - -namespace ripple { - -NotTEC -DeleteOracle::preflight(PreflightContext const& ctx) -{ - if (!ctx.rules.enabled(featurePriceOracle)) - return temDISABLED; - - if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) - return ret; - - if (ctx.tx.getFlags() & tfUniversalMask) - { - JLOG(ctx.j.debug()) << "Oracle Delete: invalid flags."; - return temINVALID_FLAG; - } - - return preflight2(ctx); -} - -TER -DeleteOracle::preclaim(PreclaimContext const& ctx) -{ - if (!ctx.view.exists(keylet::account(ctx.tx.getAccountID(sfAccount)))) - return terNO_ACCOUNT; - - if (auto const sle = ctx.view.read(keylet::oracle( - ctx.tx.getAccountID(sfAccount), ctx.tx[sfOracleDocumentID])); - !sle) - { - JLOG(ctx.j.debug()) << "Oracle Delete: Oracle does not exist."; - return tecNO_ENTRY; - } - else if (ctx.tx.getAccountID(sfAccount) != sle->getAccountID(sfOwner)) - { - // this can't happen because of the above check - JLOG(ctx.j.debug()) << "Oracle Delete: invalid account."; - return tecINTERNAL; - } - return tesSUCCESS; -} - -TER -DeleteOracle::deleteOracle( - ApplyView& view, - std::shared_ptr const& sle, - AccountID const& account, - beast::Journal j) -{ - if (!sle) - return tesSUCCESS; - - if (!view.dirRemove( - keylet::ownerDir(account), (*sle)[sfOwnerNode], sle->key(), true)) - { - JLOG(j.fatal()) << "Unable to delete Oracle from owner."; - return tefBAD_LEDGER; - } - - auto const sleOwner = view.peek(keylet::account(account)); - if (!sleOwner) - return tecINTERNAL; - - auto const count = - sle->getFieldArray(sfPriceDataSeries).size() > 5 ? -2 : -1; - - adjustOwnerCount(view, sleOwner, count, j); - - view.erase(sle); - - return tesSUCCESS; -} - -TER -DeleteOracle::doApply() -{ - if (auto sle = ctx_.view().peek( - keylet::oracle(account_, ctx_.tx[sfOracleDocumentID]))) - return deleteOracle(ctx_.view(), sle, account_, j_); - - return tecINTERNAL; -} - -} // namespace ripple diff --git a/src/ripple/app/tx/impl/DeleteOracle.h b/src/ripple/app/tx/impl/DeleteOracle.h deleted file mode 100644 index e578adaaaf0..00000000000 --- a/src/ripple/app/tx/impl/DeleteOracle.h +++ /dev/null @@ -1,64 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2023 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#ifndef RIPPLE_TX_DELETEORACLE_H_INCLUDED -#define RIPPLE_TX_DELETEORACLE_H_INCLUDED - -#include - -namespace ripple { - -/** - Price Oracle is a system that acts as a bridge between - a blockchain network and the external world, providing off-chain price data - to decentralized applications (dApps) on the blockchain. This implementation - conforms to the requirements specified in the XLS-47d. - - The DeleteOracle transactor implements the deletion of Oracle objects. -*/ - -class DeleteOracle : public Transactor -{ -public: - static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; - - explicit DeleteOracle(ApplyContext& ctx) : Transactor(ctx) - { - } - - static NotTEC - preflight(PreflightContext const& ctx); - - static TER - preclaim(PreclaimContext const& ctx); - - TER - doApply() override; - - static TER - deleteOracle( - ApplyView& view, - std::shared_ptr const& sle, - AccountID const& account, - beast::Journal j); -}; - -} // namespace ripple - -#endif // RIPPLE_TX_DELETEORACLE_H_INCLUDED diff --git a/src/ripple/app/tx/impl/InvariantCheck.cpp b/src/ripple/app/tx/impl/InvariantCheck.cpp index c0ef5bbf0c5..c717777f88f 100644 --- a/src/ripple/app/tx/impl/InvariantCheck.cpp +++ b/src/ripple/app/tx/impl/InvariantCheck.cpp @@ -392,7 +392,6 @@ LedgerEntryTypesMatch::visitEntry( case ltXCHAIN_OWNED_CLAIM_ID: case ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID: case ltDID: - case ltORACLE: break; default: invalidTypeAdded_ = true; diff --git a/src/ripple/app/tx/impl/SetOracle.cpp b/src/ripple/app/tx/impl/SetOracle.cpp deleted file mode 100644 index 37dc6fcd212..00000000000 --- a/src/ripple/app/tx/impl/SetOracle.cpp +++ /dev/null @@ -1,312 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2023 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace ripple { - -static inline std::pair -tokenPairKey(STObject const& pair) -{ - return std::make_pair( - pair.getFieldCurrency(sfBaseAsset).currency(), - pair.getFieldCurrency(sfQuoteAsset).currency()); -} - -NotTEC -SetOracle::preflight(PreflightContext const& ctx) -{ - if (!ctx.rules.enabled(featurePriceOracle)) - return temDISABLED; - - if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) - return ret; - - if (ctx.tx.getFlags() & tfUniversalMask) - return temINVALID_FLAG; - - auto const& dataSeries = ctx.tx.getFieldArray(sfPriceDataSeries); - if (dataSeries.empty()) - return temARRAY_EMPTY; - if (dataSeries.size() > maxOracleDataSeries) - return temARRAY_TOO_LARGE; - - auto isInvalidLength = [&](auto const& sField, std::size_t length) { - return ctx.tx.isFieldPresent(sField) && - (ctx.tx[sField].length() == 0 || ctx.tx[sField].length() > length); - }; - - if (isInvalidLength(sfProvider, maxOracleProvider) || - isInvalidLength(sfURI, maxOracleURI) || - isInvalidLength(sfAssetClass, maxOracleSymbolClass)) - return temMALFORMED; - - return preflight2(ctx); -} - -TER -SetOracle::preclaim(PreclaimContext const& ctx) -{ - auto const sleSetter = - ctx.view.read(keylet::account(ctx.tx.getAccountID(sfAccount))); - if (!sleSetter) - return terNO_ACCOUNT; - - // lastUpdateTime must be within maxLastUpdateTimeDelta seconds - // of the last closed ledger - using namespace std::chrono; - std::size_t const closeTime = - duration_cast(ctx.view.info().closeTime.time_since_epoch()) - .count(); - std::size_t const lastUpdateTime = ctx.tx[sfLastUpdateTime]; - if (lastUpdateTime < epoch_offset.count()) - return tecINVALID_UPDATE_TIME; - std::size_t const lastUpdateTimeEpoch = - lastUpdateTime - epoch_offset.count(); - if (closeTime < maxLastUpdateTimeDelta) - Throw( - "Oracle: close time is less than maxLastUpdateTimeDelta"); - if (lastUpdateTimeEpoch < (closeTime - maxLastUpdateTimeDelta) || - lastUpdateTimeEpoch > (closeTime + maxLastUpdateTimeDelta)) - return tecINVALID_UPDATE_TIME; - - auto const sle = ctx.view.read(keylet::oracle( - ctx.tx.getAccountID(sfAccount), ctx.tx[sfOracleDocumentID])); - - // token pairs to add/update - hash_set> pairs; - // token pairs to delete. if a token pair doesn't include - // the price then this pair should be deleted from the object. - hash_set> pairsDel; - for (auto const& entry : ctx.tx.getFieldArray(sfPriceDataSeries)) - { - if (entry[sfBaseAsset] == entry[sfQuoteAsset]) - return temMALFORMED; - auto const key = tokenPairKey(entry); - if (pairs.contains(key) || pairsDel.contains(key)) - return temMALFORMED; - if (entry[~sfScale] > maxPriceScale) - return temMALFORMED; - if (entry.isFieldPresent(sfAssetPrice)) - pairs.emplace(key); - else if (sle) - pairsDel.emplace(key); - else - return temMALFORMED; - } - - // Lambda is used to check if the value of a field, passed - // in the transaction, is equal to the value of that field - // in the on-ledger object. - auto isConsistent = [&ctx, &sle](auto const& field) { - auto const v = ctx.tx[~field]; - return !v || *v == (*sle)[field]; - }; - - std::uint32_t adjustReserve = 0; - if (sle) - { - // update - // Account is the Owner since we can get sle - - // lastUpdateTime must be more recent than the previous one - if (ctx.tx[sfLastUpdateTime] <= (*sle)[sfLastUpdateTime]) - return tecINVALID_UPDATE_TIME; - - if (!isConsistent(sfProvider) || !isConsistent(sfAssetClass)) - return temMALFORMED; - - for (auto const& entry : sle->getFieldArray(sfPriceDataSeries)) - { - auto const key = tokenPairKey(entry); - if (!pairs.contains(key)) - { - if (pairsDel.contains(key)) - pairsDel.erase(key); - else - pairs.emplace(key); - } - } - if (!pairsDel.empty()) - return tecTOKEN_PAIR_NOT_FOUND; - - auto const oldCount = - sle->getFieldArray(sfPriceDataSeries).size() > 5 ? 2 : 1; - auto const newCount = pairs.size() > 5 ? 2 : 1; - adjustReserve = newCount - oldCount; - } - else - { - // create - - if (!ctx.tx.isFieldPresent(sfProvider) || - !ctx.tx.isFieldPresent(sfAssetClass)) - return temMALFORMED; - adjustReserve = pairs.size() > 5 ? 2 : 1; - } - - if (pairs.empty()) - return tecARRAY_EMPTY; - if (pairs.size() > maxOracleDataSeries) - return tecARRAY_TOO_LARGE; - - auto const reserve = ctx.view.fees().accountReserve( - sleSetter->getFieldU32(sfOwnerCount) + adjustReserve); - auto const& balance = sleSetter->getFieldAmount(sfBalance); - - if (balance < reserve) - return tecINSUFFICIENT_RESERVE; - - return tesSUCCESS; -} - -static bool -adjustOwnerCount(ApplyContext& ctx, int count) -{ - if (auto const sleAccount = - ctx.view().peek(keylet::account(ctx.tx[sfAccount]))) - { - adjustOwnerCount(ctx.view(), sleAccount, count, ctx.journal); - return true; - } - - return false; -} - -static void -setPriceDataInnerObjTemplate(STObject& obj) -{ - if (SOTemplate const* elements = - InnerObjectFormats::getInstance().findSOTemplateBySField( - sfPriceData)) - obj.set(*elements); -} - -TER -SetOracle::doApply() -{ - auto const oracleID = keylet::oracle(account_, ctx_.tx[sfOracleDocumentID]); - - if (auto sle = ctx_.view().peek(oracleID)) - { - // update - // the token pair that doesn't have their price updated will not - // include neither price nor scale in the updated PriceDataSeries - - hash_map, STObject> pairs; - // collect current token pairs - for (auto const& entry : sle->getFieldArray(sfPriceDataSeries)) - { - STObject priceData{sfPriceData}; - setPriceDataInnerObjTemplate(priceData); - priceData.setFieldCurrency( - sfBaseAsset, entry.getFieldCurrency(sfBaseAsset)); - priceData.setFieldCurrency( - sfQuoteAsset, entry.getFieldCurrency(sfQuoteAsset)); - pairs.emplace(tokenPairKey(entry), std::move(priceData)); - } - auto const oldCount = pairs.size() > 5 ? 2 : 1; - // update/add/delete pairs - for (auto const& entry : ctx_.tx.getFieldArray(sfPriceDataSeries)) - { - auto const key = tokenPairKey(entry); - if (!entry.isFieldPresent(sfAssetPrice)) - { - // delete token pair - pairs.erase(key); - } - else if (auto iter = pairs.find(key); iter != pairs.end()) - { - // update the price - iter->second.setFieldU64( - sfAssetPrice, entry.getFieldU64(sfAssetPrice)); - if (entry.isFieldPresent(sfScale)) - iter->second.setFieldU8(sfScale, entry.getFieldU8(sfScale)); - } - else - { - // add a token pair with the price - STObject priceData{sfPriceData}; - setPriceDataInnerObjTemplate(priceData); - priceData.setFieldCurrency( - sfBaseAsset, entry.getFieldCurrency(sfBaseAsset)); - priceData.setFieldCurrency( - sfQuoteAsset, entry.getFieldCurrency(sfQuoteAsset)); - priceData.setFieldU64( - sfAssetPrice, entry.getFieldU64(sfAssetPrice)); - if (entry.isFieldPresent(sfScale)) - priceData.setFieldU8(sfScale, entry.getFieldU8(sfScale)); - pairs.emplace(key, std::move(priceData)); - } - } - STArray updatedSeries; - for (auto const& iter : pairs) - updatedSeries.push_back(std::move(iter.second)); - sle->setFieldArray(sfPriceDataSeries, updatedSeries); - if (ctx_.tx.isFieldPresent(sfURI)) - sle->setFieldVL(sfURI, ctx_.tx[sfURI]); - sle->setFieldU32(sfLastUpdateTime, ctx_.tx[sfLastUpdateTime]); - - auto const newCount = pairs.size() > 5 ? 2 : 1; - auto const adjust = newCount - oldCount; - if (adjust != 0 && !adjustOwnerCount(ctx_, adjust)) - return tefINTERNAL; - - ctx_.view().update(sle); - } - else - { - // create - - sle = std::make_shared(oracleID); - sle->setAccountID(sfOwner, ctx_.tx.getAccountID(sfAccount)); - sle->setFieldVL(sfProvider, ctx_.tx[sfProvider]); - if (ctx_.tx.isFieldPresent(sfURI)) - sle->setFieldVL(sfURI, ctx_.tx[sfURI]); - auto const& series = ctx_.tx.getFieldArray(sfPriceDataSeries); - sle->setFieldArray(sfPriceDataSeries, series); - sle->setFieldVL(sfAssetClass, ctx_.tx[sfAssetClass]); - sle->setFieldU32(sfLastUpdateTime, ctx_.tx[sfLastUpdateTime]); - - auto page = ctx_.view().dirInsert( - keylet::ownerDir(account_), sle->key(), describeOwnerDir(account_)); - if (!page) - return tecDIR_FULL; - - (*sle)[sfOwnerNode] = *page; - - auto const count = series.size() > 5 ? 2 : 1; - if (!adjustOwnerCount(ctx_, count)) - return tefINTERNAL; - - ctx_.view().insert(sle); - } - - return tesSUCCESS; -} - -} // namespace ripple diff --git a/src/ripple/app/tx/impl/SetOracle.h b/src/ripple/app/tx/impl/SetOracle.h deleted file mode 100644 index 0ab8e603aa5..00000000000 --- a/src/ripple/app/tx/impl/SetOracle.h +++ /dev/null @@ -1,57 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2023 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#ifndef RIPPLE_TX_SETORACLE_H_INCLUDED -#define RIPPLE_TX_SETORACLE_H_INCLUDED - -#include - -namespace ripple { - -/** - Price Oracle is a system that acts as a bridge between - a blockchain network and the external world, providing off-chain price data - to decentralized applications (dApps) on the blockchain. This implementation - conforms to the requirements specified in the XLS-47d. - - The SetOracle transactor implements creating or updating Oracle objects. -*/ - -class SetOracle : public Transactor -{ -public: - static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; - - explicit SetOracle(ApplyContext& ctx) : Transactor(ctx) - { - } - - static NotTEC - preflight(PreflightContext const& ctx); - - static TER - preclaim(PreclaimContext const& ctx); - - TER - doApply() override; -}; - -} // namespace ripple - -#endif // RIPPLE_TX_SETORACLE_H_INCLUDED diff --git a/src/ripple/app/tx/impl/applySteps.cpp b/src/ripple/app/tx/impl/applySteps.cpp index 1a1fc343e3c..10e2b0c4524 100644 --- a/src/ripple/app/tx/impl/applySteps.cpp +++ b/src/ripple/app/tx/impl/applySteps.cpp @@ -35,7 +35,6 @@ #include #include #include -#include #include #include #include @@ -46,7 +45,6 @@ #include #include #include -#include #include #include #include @@ -161,10 +159,6 @@ with_txn_type(TxType txnType, F&& f) return f.template operator()(); case ttDID_DELETE: return f.template operator()(); - case ttORACLE_SET: - return f.template operator()(); - case ttORACLE_DELETE: - return f.template operator()(); default: throw UnknownTxnType(txnType); } diff --git a/src/ripple/protocol/ErrorCodes.h b/src/ripple/protocol/ErrorCodes.h index ad849f2b4ef..8319b69c8c2 100644 --- a/src/ripple/protocol/ErrorCodes.h +++ b/src/ripple/protocol/ErrorCodes.h @@ -145,11 +145,7 @@ enum error_code_i { // AMM rpcISSUE_MALFORMED = 93, - // Oracle - rpcORACLE_MALFORMED = 94, - - rpcLAST = - rpcORACLE_MALFORMED // rpcLAST should always equal the last code.= + rpcLAST = rpcISSUE_MALFORMED // rpcLAST should always equal the last code.= }; /** Codes returned in the `warnings` array of certain RPC commands. diff --git a/src/ripple/protocol/Feature.h b/src/ripple/protocol/Feature.h index b8788a74de7..cf0523fcf82 100644 --- a/src/ripple/protocol/Feature.h +++ b/src/ripple/protocol/Feature.h @@ -74,7 +74,7 @@ namespace detail { // Feature.cpp. Because it's only used to reserve storage, and determine how // large to make the FeatureBitset, it MAY be larger. It MUST NOT be less than // the actual number of amendments. A LogicError on startup will verify this. -static constexpr std::size_t numFeatures = 73; +static constexpr std::size_t numFeatures = 72; /** Amendments that this server supports and the default voting behavior. Whether they are enabled depends on the Rules defined in the validated @@ -355,7 +355,6 @@ extern uint256 const fixFillOrKill; extern uint256 const fixNFTokenReserve; extern uint256 const fixInnerObjTemplate; extern uint256 const fixAMMOverflowOffer; -extern uint256 const featurePriceOracle; extern uint256 const fixEmptyDID; extern uint256 const fixXChainRewardRounding; extern uint256 const fixPreviousTxnID; diff --git a/src/ripple/protocol/Indexes.h b/src/ripple/protocol/Indexes.h index d83599f892f..9a330b6b4f0 100644 --- a/src/ripple/protocol/Indexes.h +++ b/src/ripple/protocol/Indexes.h @@ -283,9 +283,6 @@ xChainCreateAccountClaimID(STXChainBridge const& bridge, std::uint64_t seq); Keylet did(AccountID const& account) noexcept; -Keylet -oracle(AccountID const& account, std::uint32_t const& documentID) noexcept; - } // namespace keylet // Everything below is deprecated and should be removed in favor of keylets: diff --git a/src/ripple/protocol/LedgerFormats.h b/src/ripple/protocol/LedgerFormats.h index e0ea7bf6f46..db64942790a 100644 --- a/src/ripple/protocol/LedgerFormats.h +++ b/src/ripple/protocol/LedgerFormats.h @@ -192,11 +192,6 @@ enum LedgerEntryType : std::uint16_t */ ltDID = 0x0049, - /** A ledger object which tracks Oracle - \sa keylet::oracle - */ - ltORACLE = 0x0080, - //--------------------------------------------------------------------------- /** A special type, matching any ledger entry type. diff --git a/src/ripple/protocol/Protocol.h b/src/ripple/protocol/Protocol.h index bd723627494..49642efc4cf 100644 --- a/src/ripple/protocol/Protocol.h +++ b/src/ripple/protocol/Protocol.h @@ -109,31 +109,6 @@ using TxID = uint256; */ std::uint16_t constexpr maxDeletableAMMTrustLines = 512; -/** The maximum length of a URI inside an Oracle */ -std::size_t constexpr maxOracleURI = 256; - -/** The maximum length of a Provider inside an Oracle */ -std::size_t constexpr maxOracleProvider = 256; - -/** The maximum size of a data series array inside an Oracle */ -std::size_t constexpr maxOracleDataSeries = 10; - -/** The maximum length of a SymbolClass inside an Oracle */ -std::size_t constexpr maxOracleSymbolClass = 16; - -/** The maximum allowed time difference between lastUpdateTime and the time - of the last closed ledger -*/ -std::size_t constexpr maxLastUpdateTimeDelta = 300; - -/** The maximum price scaling factor - */ -std::size_t constexpr maxPriceScale = 20; - -/** The maximum percentage of outliers to trim - */ -std::size_t constexpr maxTrim = 25; - } // namespace ripple #endif diff --git a/src/ripple/protocol/SField.h b/src/ripple/protocol/SField.h index 727d531ff40..5d7acb12383 100644 --- a/src/ripple/protocol/SField.h +++ b/src/ripple/protocol/SField.h @@ -51,7 +51,6 @@ template class STInteger; class STXChainBridge; class STVector256; -class STCurrency; #pragma push_macro("XMACRO") #undef XMACRO @@ -86,7 +85,6 @@ class STCurrency; STYPE(STI_UINT512, 23) \ STYPE(STI_ISSUE, 24) \ STYPE(STI_XCHAIN_BRIDGE, 25) \ - STYPE(STI_CURRENCY, 26) \ \ /* high-level types */ \ /* cannot be serialized inside other types */ \ @@ -348,7 +346,6 @@ using SF_UINT512 = TypedField>; using SF_ACCOUNT = TypedField; using SF_AMOUNT = TypedField; using SF_ISSUE = TypedField; -using SF_CURRENCY = TypedField; using SF_VL = TypedField; using SF_VECTOR256 = TypedField; using SF_XCHAIN_BRIDGE = TypedField; @@ -367,7 +364,6 @@ extern SF_UINT8 const sfCloseResolution; extern SF_UINT8 const sfMethod; extern SF_UINT8 const sfTransactionResult; extern SF_UINT8 const sfWasLockingChainSend; -extern SF_UINT8 const sfScale; // 8-bit integers (uncommon) extern SF_UINT8 const sfTickSize; @@ -404,7 +400,6 @@ extern SF_UINT32 const sfTransferRate; extern SF_UINT32 const sfWalletSize; extern SF_UINT32 const sfOwnerCount; extern SF_UINT32 const sfDestinationTag; -extern SF_UINT32 const sfLastUpdateTime; // 32-bit integers (uncommon) extern SF_UINT32 const sfHighQualityIn; @@ -440,7 +435,6 @@ extern SF_UINT32 const sfHookStateCount; extern SF_UINT32 const sfEmitGeneration; extern SF_UINT32 const sfVoteWeight; extern SF_UINT32 const sfFirstNFTokenSequence; -extern SF_UINT32 const sfOracleDocumentID; // 64-bit integers (common) extern SF_UINT64 const sfIndexNext; @@ -465,7 +459,6 @@ extern SF_UINT64 const sfReferenceCount; extern SF_UINT64 const sfXChainClaimID; extern SF_UINT64 const sfXChainAccountCreateCount; extern SF_UINT64 const sfXChainAccountClaimCount; -extern SF_UINT64 const sfAssetPrice; // 128-bit extern SF_UINT128 const sfEmailHash; @@ -561,8 +554,6 @@ extern SF_VL const sfMemoData; extern SF_VL const sfMemoFormat; extern SF_VL const sfDIDDocument; extern SF_VL const sfData; -extern SF_VL const sfAssetClass; -extern SF_VL const sfProvider; // variable length (uncommon) extern SF_VL const sfFulfillment; @@ -599,10 +590,6 @@ extern SF_ACCOUNT const sfIssuingChainDoor; // path set extern SField const sfPaths; -// currency -extern SF_CURRENCY const sfBaseAsset; -extern SF_CURRENCY const sfQuoteAsset; - // issue extern SF_ISSUE const sfAsset; extern SF_ISSUE const sfAsset2; @@ -636,7 +623,6 @@ extern SField const sfHook; extern SField const sfVoteEntry; extern SField const sfAuctionSlot; extern SField const sfAuthAccount; -extern SField const sfPriceData; extern SField const sfSigner; extern SField const sfMajority; @@ -665,7 +651,6 @@ extern SField const sfNFTokens; extern SField const sfHooks; extern SField const sfVoteSlots; extern SField const sfAuthAccounts; -extern SField const sfPriceDataSeries; // array of objects (uncommon) extern SField const sfMajorities; diff --git a/src/ripple/protocol/STCurrency.h b/src/ripple/protocol/STCurrency.h deleted file mode 100644 index f855c24832e..00000000000 --- a/src/ripple/protocol/STCurrency.h +++ /dev/null @@ -1,138 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2023 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#ifndef RIPPLE_PROTOCOL_STCURRENCY_H_INCLUDED -#define RIPPLE_PROTOCOL_STCURRENCY_H_INCLUDED - -#include -#include -#include -#include -#include - -namespace ripple { - -class STCurrency final : public STBase -{ -private: - Currency currency_{}; - -public: - using value_type = Currency; - - STCurrency() = default; - - explicit STCurrency(SerialIter& sit, SField const& name); - - explicit STCurrency(SField const& name, Currency const& currency); - - explicit STCurrency(SField const& name); - - Currency const& - currency() const; - - Currency const& - value() const noexcept; - - void - setCurrency(Currency const& currency); - - SerializedTypeID - getSType() const override; - - std::string - getText() const override; - - Json::Value getJson(JsonOptions) const override; - - void - add(Serializer& s) const override; - - bool - isEquivalent(const STBase& t) const override; - - bool - isDefault() const override; - -private: - static std::unique_ptr - construct(SerialIter&, SField const& name); - - STBase* - copy(std::size_t n, void* buf) const override; - STBase* - move(std::size_t n, void* buf) override; - - friend class detail::STVar; -}; - -STCurrency -currencyFromJson(SField const& name, Json::Value const& v); - -inline Currency const& -STCurrency::currency() const -{ - return currency_; -} - -inline Currency const& -STCurrency::value() const noexcept -{ - return currency_; -} - -inline void -STCurrency::setCurrency(Currency const& currency) -{ - currency_ = currency; -} - -inline bool -operator==(STCurrency const& lhs, STCurrency const& rhs) -{ - return lhs.currency() == rhs.currency(); -} - -inline bool -operator!=(STCurrency const& lhs, STCurrency const& rhs) -{ - return !operator==(lhs, rhs); -} - -inline bool -operator<(STCurrency const& lhs, STCurrency const& rhs) -{ - return lhs.currency() < rhs.currency(); -} - -inline bool -operator==(STCurrency const& lhs, Currency const& rhs) -{ - return lhs.currency() == rhs; -} - -inline bool -operator<(STCurrency const& lhs, Currency const& rhs) -{ - return lhs.currency() < rhs; -} - -} // namespace ripple - -#endif diff --git a/src/ripple/protocol/STObject.h b/src/ripple/protocol/STObject.h index 38678f67a55..5476cd01198 100644 --- a/src/ripple/protocol/STObject.h +++ b/src/ripple/protocol/STObject.h @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include @@ -242,8 +241,6 @@ class STObject : public STBase, public CountedObject getFieldV256(SField const& field) const; const STArray& getFieldArray(SField const& field) const; - const STCurrency& - getFieldCurrency(SField const& field) const; /** Get the value of a field. @param A TypedField built from an SField value representing the desired @@ -373,8 +370,6 @@ class STObject : public STBase, public CountedObject void setFieldIssue(SField const& field, STIssue const&); void - setFieldCurrency(SField const& field, STCurrency const&); - void setFieldPathSet(SField const& field, STPathSet const&); void setFieldV256(SField const& field, STVector256 const& v); diff --git a/src/ripple/protocol/TER.h b/src/ripple/protocol/TER.h index 41c23a2d6a8..d294096f4b4 100644 --- a/src/ripple/protocol/TER.h +++ b/src/ripple/protocol/TER.h @@ -136,9 +136,6 @@ enum TEMcodes : TERUnderlyingType { temXCHAIN_BRIDGE_BAD_REWARD_AMOUNT, temEMPTY_DID, - - temARRAY_EMPTY, - temARRAY_TOO_LARGE, }; //------------------------------------------------------------------------------ @@ -334,11 +331,7 @@ enum TECcodes : TERUnderlyingType { tecXCHAIN_SELF_COMMIT = 184, tecXCHAIN_BAD_PUBLIC_KEY_ACCOUNT_PAIR = 185, tecXCHAIN_CREATE_ACCOUNT_DISABLED = 186, - tecEMPTY_DID = 187, - tecINVALID_UPDATE_TIME = 188, - tecTOKEN_PAIR_NOT_FOUND = 189, - tecARRAY_EMPTY = 190, - tecARRAY_TOO_LARGE = 191 + tecEMPTY_DID = 187 }; //------------------------------------------------------------------------------ diff --git a/src/ripple/protocol/TxFormats.h b/src/ripple/protocol/TxFormats.h index b5afa470f38..b12547b0a67 100644 --- a/src/ripple/protocol/TxFormats.h +++ b/src/ripple/protocol/TxFormats.h @@ -191,12 +191,6 @@ enum TxType : std::uint16_t ttDID_DELETE = 50, - /** This transaction type creates an Oracle instance */ - ttORACLE_SET = 51, - - /** This transaction type deletes an Oracle instance */ - ttORACLE_DELETE = 52, - /** This system-generated transaction type is used to update the status of the various amendments. For details, see: https://xrpl.org/amendments.html diff --git a/src/ripple/protocol/impl/ErrorCodes.cpp b/src/ripple/protocol/impl/ErrorCodes.cpp index 3af48891c78..319bd8e28c2 100644 --- a/src/ripple/protocol/impl/ErrorCodes.cpp +++ b/src/ripple/protocol/impl/ErrorCodes.cpp @@ -109,8 +109,7 @@ constexpr static ErrorInfo unorderedErrorInfos[]{ {rpcSTREAM_MALFORMED, "malformedStream", "Stream malformed.", 400}, {rpcTOO_BUSY, "tooBusy", "The server is too busy to help you now.", 503}, {rpcTXN_NOT_FOUND, "txnNotFound", "Transaction not found.", 404}, - {rpcUNKNOWN_COMMAND, "unknownCmd", "Unknown method.", 405}, - {rpcORACLE_MALFORMED, "oracleMalformed", "Oracle request is malformed.", 400}}; + {rpcUNKNOWN_COMMAND, "unknownCmd", "Unknown method.", 405}}; // clang-format on // Sort and validate unorderedErrorInfos at compile time. Should be diff --git a/src/ripple/protocol/impl/Feature.cpp b/src/ripple/protocol/impl/Feature.cpp index 8c8ff403e3f..1089e62d2f9 100644 --- a/src/ripple/protocol/impl/Feature.cpp +++ b/src/ripple/protocol/impl/Feature.cpp @@ -462,7 +462,6 @@ REGISTER_FIX (fixFillOrKill, Supported::yes, VoteBehavior::De REGISTER_FIX (fixNFTokenReserve, Supported::yes, VoteBehavior::DefaultNo); REGISTER_FIX (fixInnerObjTemplate, Supported::yes, VoteBehavior::DefaultNo); REGISTER_FIX (fixAMMOverflowOffer, Supported::yes, VoteBehavior::DefaultYes); -REGISTER_FEATURE(PriceOracle, Supported::yes, VoteBehavior::DefaultNo); REGISTER_FIX (fixEmptyDID, Supported::yes, VoteBehavior::DefaultNo); REGISTER_FIX (fixXChainRewardRounding, Supported::yes, VoteBehavior::DefaultNo); REGISTER_FIX (fixPreviousTxnID, Supported::yes, VoteBehavior::DefaultNo); diff --git a/src/ripple/protocol/impl/Indexes.cpp b/src/ripple/protocol/impl/Indexes.cpp index 0ee52aab297..74f6b6492de 100644 --- a/src/ripple/protocol/impl/Indexes.cpp +++ b/src/ripple/protocol/impl/Indexes.cpp @@ -72,7 +72,6 @@ enum class LedgerNameSpace : std::uint16_t { XCHAIN_CLAIM_ID = 'Q', XCHAIN_CREATE_ACCOUNT_CLAIM_ID = 'K', DID = 'I', - ORACLE = 'R', // No longer used or supported. Left here to reserve the space // to avoid accidental reuse. @@ -445,12 +444,6 @@ did(AccountID const& account) noexcept return {ltDID, indexHash(LedgerNameSpace::DID, account)}; } -Keylet -oracle(AccountID const& account, std::uint32_t const& documentID) noexcept -{ - return {ltORACLE, indexHash(LedgerNameSpace::ORACLE, account, documentID)}; -} - } // namespace keylet } // namespace ripple diff --git a/src/ripple/protocol/impl/InnerObjectFormats.cpp b/src/ripple/protocol/impl/InnerObjectFormats.cpp index edebc57477e..4350ea180d2 100644 --- a/src/ripple/protocol/impl/InnerObjectFormats.cpp +++ b/src/ripple/protocol/impl/InnerObjectFormats.cpp @@ -138,15 +138,6 @@ InnerObjectFormats::InnerObjectFormats() { {sfAccount, soeREQUIRED}, }); - - add(sfPriceData.jsonName.c_str(), - sfPriceData.getCode(), - { - {sfBaseAsset, soeREQUIRED}, - {sfQuoteAsset, soeREQUIRED}, - {sfAssetPrice, soeOPTIONAL}, - {sfScale, soeDEFAULT}, - }); } InnerObjectFormats const& diff --git a/src/ripple/protocol/impl/LedgerFormats.cpp b/src/ripple/protocol/impl/LedgerFormats.cpp index 4f117a5d60d..8592466e9b7 100644 --- a/src/ripple/protocol/impl/LedgerFormats.cpp +++ b/src/ripple/protocol/impl/LedgerFormats.cpp @@ -349,22 +349,6 @@ LedgerFormats::LedgerFormats() {sfPreviousTxnLgrSeq, soeREQUIRED} }, commonFields); - - add(jss::Oracle, - ltORACLE, - { - {sfOwner, soeREQUIRED}, - {sfProvider, soeREQUIRED}, - {sfPriceDataSeries, soeREQUIRED}, - {sfAssetClass, soeREQUIRED}, - {sfLastUpdateTime, soeREQUIRED}, - {sfURI, soeOPTIONAL}, - {sfOwnerNode, soeREQUIRED}, - {sfPreviousTxnID, soeREQUIRED}, - {sfPreviousTxnLgrSeq, soeREQUIRED} - }, - commonFields); - // clang-format on } diff --git a/src/ripple/protocol/impl/SField.cpp b/src/ripple/protocol/impl/SField.cpp index 6d034db75ef..027c8ffb9c5 100644 --- a/src/ripple/protocol/impl/SField.cpp +++ b/src/ripple/protocol/impl/SField.cpp @@ -91,7 +91,6 @@ CONSTRUCT_UNTYPED_SFIELD(sfMetadata, "Metadata", METADATA CONSTRUCT_TYPED_SFIELD(sfCloseResolution, "CloseResolution", UINT8, 1); CONSTRUCT_TYPED_SFIELD(sfMethod, "Method", UINT8, 2); CONSTRUCT_TYPED_SFIELD(sfTransactionResult, "TransactionResult", UINT8, 3); -CONSTRUCT_TYPED_SFIELD(sfScale, "Scale", UINT8, 4); // 8-bit integers (uncommon) CONSTRUCT_TYPED_SFIELD(sfTickSize, "TickSize", UINT8, 16); @@ -129,7 +128,6 @@ CONSTRUCT_TYPED_SFIELD(sfTransferRate, "TransferRate", UINT32, CONSTRUCT_TYPED_SFIELD(sfWalletSize, "WalletSize", UINT32, 12); CONSTRUCT_TYPED_SFIELD(sfOwnerCount, "OwnerCount", UINT32, 13); CONSTRUCT_TYPED_SFIELD(sfDestinationTag, "DestinationTag", UINT32, 14); -CONSTRUCT_TYPED_SFIELD(sfLastUpdateTime, "LastUpdateTime", UINT32, 15); // 32-bit integers (uncommon) CONSTRUCT_TYPED_SFIELD(sfHighQualityIn, "HighQualityIn", UINT32, 16); @@ -166,7 +164,6 @@ CONSTRUCT_TYPED_SFIELD(sfEmitGeneration, "EmitGeneration", UINT32, // 47 is reserved for LockCount(Hooks) CONSTRUCT_TYPED_SFIELD(sfVoteWeight, "VoteWeight", UINT32, 48); CONSTRUCT_TYPED_SFIELD(sfFirstNFTokenSequence, "FirstNFTokenSequence", UINT32, 50); -CONSTRUCT_TYPED_SFIELD(sfOracleDocumentID, "OracleDocumentID", UINT32, 51); // 64-bit integers (common) CONSTRUCT_TYPED_SFIELD(sfIndexNext, "IndexNext", UINT64, 1); @@ -191,7 +188,6 @@ CONSTRUCT_TYPED_SFIELD(sfReferenceCount, "ReferenceCount", U CONSTRUCT_TYPED_SFIELD(sfXChainClaimID, "XChainClaimID", UINT64, 20); CONSTRUCT_TYPED_SFIELD(sfXChainAccountCreateCount, "XChainAccountCreateCount", UINT64, 21); CONSTRUCT_TYPED_SFIELD(sfXChainAccountClaimCount, "XChainAccountClaimCount", UINT64, 22); -CONSTRUCT_TYPED_SFIELD(sfAssetPrice, "AssetPrice", UINT64, 23); // 128-bit CONSTRUCT_TYPED_SFIELD(sfEmailHash, "EmailHash", UINT128, 1); @@ -304,8 +300,6 @@ CONSTRUCT_TYPED_SFIELD(sfHookParameterName, "HookParameterName", VL, CONSTRUCT_TYPED_SFIELD(sfHookParameterValue, "HookParameterValue", VL, 25); CONSTRUCT_TYPED_SFIELD(sfDIDDocument, "DIDDocument", VL, 26); CONSTRUCT_TYPED_SFIELD(sfData, "Data", VL, 27); -CONSTRUCT_TYPED_SFIELD(sfAssetClass, "AssetClass", VL, 28); -CONSTRUCT_TYPED_SFIELD(sfProvider, "Provider", VL, 29); // account CONSTRUCT_TYPED_SFIELD(sfAccount, "Account", ACCOUNT, 1); @@ -337,10 +331,6 @@ CONSTRUCT_TYPED_SFIELD(sfNFTokenOffers, "NFTokenOffers", VECTOR25 // path set CONSTRUCT_UNTYPED_SFIELD(sfPaths, "Paths", PATHSET, 1); -// currency -CONSTRUCT_TYPED_SFIELD(sfBaseAsset, "BaseAsset", CURRENCY, 1); -CONSTRUCT_TYPED_SFIELD(sfQuoteAsset, "QuoteAsset", CURRENCY, 2); - // issue CONSTRUCT_TYPED_SFIELD(sfLockingChainIssue, "LockingChainIssue", ISSUE, 1); CONSTRUCT_TYPED_SFIELD(sfIssuingChainIssue, "IssuingChainIssue", ISSUE, 2); @@ -389,7 +379,6 @@ CONSTRUCT_UNTYPED_SFIELD(sfXChainClaimAttestationCollectionElement, CONSTRUCT_UNTYPED_SFIELD(sfXChainCreateAccountAttestationCollectionElement, "XChainCreateAccountAttestationCollectionElement", OBJECT, 31); -CONSTRUCT_UNTYPED_SFIELD(sfPriceData, "PriceData", OBJECT, 32); // array of objects // ARRAY/1 is reserved for end of array @@ -417,8 +406,7 @@ CONSTRUCT_UNTYPED_SFIELD(sfXChainClaimAttestations, CONSTRUCT_UNTYPED_SFIELD(sfXChainCreateAccountAttestations, "XChainCreateAccountAttestations", ARRAY, 22); -// 23 is unused and available for use -CONSTRUCT_UNTYPED_SFIELD(sfPriceDataSeries, "PriceDataSeries", ARRAY, 24); +// 23 and 24 are unused and available for use CONSTRUCT_UNTYPED_SFIELD(sfAuthAccounts, "AuthAccounts", ARRAY, 25); // clang-format on diff --git a/src/ripple/protocol/impl/STCurrency.cpp b/src/ripple/protocol/impl/STCurrency.cpp deleted file mode 100644 index d2bc1b3bea7..00000000000 --- a/src/ripple/protocol/impl/STCurrency.cpp +++ /dev/null @@ -1,114 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2023 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#include -#include - -#include - -namespace ripple { - -STCurrency::STCurrency(SField const& name) : STBase{name} -{ -} - -STCurrency::STCurrency(SerialIter& sit, SField const& name) : STBase{name} -{ - currency_ = sit.get160(); -} - -STCurrency::STCurrency(SField const& name, Currency const& currency) - : STBase{name}, currency_{currency} -{ -} - -SerializedTypeID -STCurrency::getSType() const -{ - return STI_CURRENCY; -} - -std::string -STCurrency::getText() const -{ - return to_string(currency_); -} - -Json::Value STCurrency::getJson(JsonOptions) const -{ - return to_string(currency_); -} - -void -STCurrency::add(Serializer& s) const -{ - s.addBitString(currency_); -} - -bool -STCurrency::isEquivalent(const STBase& t) const -{ - const STCurrency* v = dynamic_cast(&t); - return v && (*v == *this); -} - -bool -STCurrency::isDefault() const -{ - return isXRP(currency_); -} - -std::unique_ptr -STCurrency::construct(SerialIter& sit, SField const& name) -{ - return std::make_unique(sit, name); -} - -STBase* -STCurrency::copy(std::size_t n, void* buf) const -{ - return emplace(n, buf, *this); -} - -STBase* -STCurrency::move(std::size_t n, void* buf) -{ - return emplace(n, buf, std::move(*this)); -} - -STCurrency -currencyFromJson(SField const& name, Json::Value const& v) -{ - if (!v.isString()) - { - Throw( - "currencyFromJson currency must be a string Json value"); - } - - auto const currency = to_currency(v.asString()); - if (currency == badCurrency() || currency == noCurrency()) - { - Throw( - "currencyFromJson currency must be a valid currency"); - } - - return STCurrency{name, currency}; -} - -} // namespace ripple diff --git a/src/ripple/protocol/impl/STObject.cpp b/src/ripple/protocol/impl/STObject.cpp index dbcb47e8794..7c546a2568e 100644 --- a/src/ripple/protocol/impl/STObject.cpp +++ b/src/ripple/protocol/impl/STObject.cpp @@ -24,7 +24,6 @@ #include #include #include -#include #include namespace ripple { @@ -643,13 +642,6 @@ STObject::getFieldArray(SField const& field) const return getFieldByConstRef(field, empty); } -STCurrency const& -STObject::getFieldCurrency(SField const& field) const -{ - static STCurrency const empty{}; - return getFieldByConstRef(field, empty); -} - void STObject::set(std::unique_ptr v) { @@ -738,12 +730,6 @@ STObject::setFieldAmount(SField const& field, STAmount const& v) setFieldUsingAssignment(field, v); } -void -STObject::setFieldCurrency(SField const& field, STCurrency const& v) -{ - setFieldUsingAssignment(field, v); -} - void STObject::setFieldIssue(SField const& field, STIssue const& v) { diff --git a/src/ripple/protocol/impl/STParsedJSON.cpp b/src/ripple/protocol/impl/STParsedJSON.cpp index 6727fe7388c..fb960e6f11e 100644 --- a/src/ripple/protocol/impl/STParsedJSON.cpp +++ b/src/ripple/protocol/impl/STParsedJSON.cpp @@ -760,19 +760,6 @@ parseLeaf( } break; - case STI_CURRENCY: - try - { - ret = detail::make_stvar( - currencyFromJson(field, value)); - } - catch (std::exception const&) - { - error = invalid_data(json_name, fieldName); - return ret; - } - break; - default: error = bad_type(json_name, fieldName); return ret; diff --git a/src/ripple/protocol/impl/STVar.cpp b/src/ripple/protocol/impl/STVar.cpp index adda165901f..2ec55ccaf03 100644 --- a/src/ripple/protocol/impl/STVar.cpp +++ b/src/ripple/protocol/impl/STVar.cpp @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -168,9 +167,6 @@ STVar::STVar(SerialIter& sit, SField const& name, int depth) case STI_XCHAIN_BRIDGE: construct(sit, name); return; - case STI_CURRENCY: - construct(sit, name); - return; default: Throw("Unknown object type"); } @@ -232,9 +228,6 @@ STVar::STVar(SerializedTypeID id, SField const& name) case STI_XCHAIN_BRIDGE: construct(name); return; - case STI_CURRENCY: - construct(name); - return; default: Throw("Unknown object type"); } diff --git a/src/ripple/protocol/impl/TER.cpp b/src/ripple/protocol/impl/TER.cpp index 93bc60a98ba..d4c47266e49 100644 --- a/src/ripple/protocol/impl/TER.cpp +++ b/src/ripple/protocol/impl/TER.cpp @@ -111,10 +111,6 @@ transResults() MAKE_ERROR(tecXCHAIN_BAD_PUBLIC_KEY_ACCOUNT_PAIR, "Bad public key account pair in an xchain transaction."), MAKE_ERROR(tecXCHAIN_CREATE_ACCOUNT_DISABLED, "This bridge does not support account creation."), MAKE_ERROR(tecEMPTY_DID, "The DID object did not have a URI or DIDDocument field."), - MAKE_ERROR(tecINVALID_UPDATE_TIME, "The Oracle object has invalid LastUpdateTime field."), - MAKE_ERROR(tecTOKEN_PAIR_NOT_FOUND, "Token pair is not found in Oracle object."), - MAKE_ERROR(tecARRAY_EMPTY, "Array is empty."), - MAKE_ERROR(tecARRAY_TOO_LARGE, "Array is too large."), MAKE_ERROR(tefALREADY, "The exact transaction was already in this ledger."), MAKE_ERROR(tefBAD_ADD_AUTH, "Not authorized to add account."), @@ -202,8 +198,6 @@ transResults() MAKE_ERROR(temXCHAIN_BRIDGE_NONDOOR_OWNER, "Malformed: Bridge owner must be one of the door accounts."), MAKE_ERROR(temXCHAIN_BRIDGE_BAD_MIN_ACCOUNT_CREATE_AMOUNT, "Malformed: Bad min account create amount."), MAKE_ERROR(temXCHAIN_BRIDGE_BAD_REWARD_AMOUNT, "Malformed: Bad reward amount."), - MAKE_ERROR(temARRAY_EMPTY, "Malformed: Array is empty."), - MAKE_ERROR(temARRAY_TOO_LARGE, "Malformed: Array is too large."), MAKE_ERROR(terRETRY, "Retry transaction."), MAKE_ERROR(terFUNDS_SPENT, "DEPRECATED."), diff --git a/src/ripple/protocol/impl/TxFormats.cpp b/src/ripple/protocol/impl/TxFormats.cpp index d2bdd4f8aa7..7be8ca741e2 100644 --- a/src/ripple/protocol/impl/TxFormats.cpp +++ b/src/ripple/protocol/impl/TxFormats.cpp @@ -483,25 +483,6 @@ TxFormats::TxFormats() commonFields); add(jss::DIDDelete, ttDID_DELETE, {}, commonFields); - - add(jss::OracleSet, - ttORACLE_SET, - { - {sfOracleDocumentID, soeREQUIRED}, - {sfProvider, soeOPTIONAL}, - {sfURI, soeOPTIONAL}, - {sfAssetClass, soeOPTIONAL}, - {sfLastUpdateTime, soeREQUIRED}, - {sfPriceDataSeries, soeREQUIRED}, - }, - commonFields); - - add(jss::OracleDelete, - ttORACLE_DELETE, - { - {sfOracleDocumentID, soeREQUIRED}, - }, - commonFields); } TxFormats const& diff --git a/src/ripple/protocol/jss.h b/src/ripple/protocol/jss.h index b5f3d1fcb5d..dbb39a58d85 100644 --- a/src/ripple/protocol/jss.h +++ b/src/ripple/protocol/jss.h @@ -60,11 +60,8 @@ JSS(Amount); // in: TransactionSign; field. JSS(Amount2); // in/out: AMM IOU/XRP pool, deposit, withdraw amount JSS(Asset); // in: AMM Asset1 JSS(Asset2); // in: AMM Asset2 -JSS(AssetClass); // in: Oracle -JSS(AssetPrice); // in: Oracle JSS(AuthAccount); // in: AMM Auction Slot JSS(AuthAccounts); // in: AMM Auction Slot -JSS(BaseAsset); // in: Oracle JSS(Bridge); // ledger type. JSS(Check); // ledger type. JSS(CheckCancel); // transaction type. @@ -92,7 +89,6 @@ JSS(Flags); // in/out: TransactionSign; field. JSS(incomplete_shards); // out: OverlayImpl, PeerImp JSS(Invalid); // JSS(LastLedgerSequence); // in: TransactionSign; field -JSS(LastUpdateTime); // field. JSS(LedgerHashes); // ledger type. JSS(LimitAmount); // field. JSS(BidMax); // in: AMM Bid @@ -112,26 +108,16 @@ JSS(Offer); // ledger type. JSS(OfferCancel); // transaction type. JSS(OfferCreate); // transaction type. JSS(OfferSequence); // field. -JSS(Oracle); // ledger type. -JSS(OracleDelete); // transaction type. -JSS(OracleDocumentID); // field -JSS(OracleSet); // transaction type. -JSS(Owner); // field JSS(Paths); // in/out: TransactionSign JSS(PayChannel); // ledger type. JSS(Payment); // transaction type. JSS(PaymentChannelClaim); // transaction type. JSS(PaymentChannelCreate); // transaction type. JSS(PaymentChannelFund); // transaction type. -JSS(PriceDataSeries); // field. -JSS(PriceData); // field. -JSS(Provider); // field. -JSS(QuoteAsset); // in: Oracle. JSS(RippleState); // ledger type. JSS(SLE_hit_rate); // out: GetCounts. JSS(SetFee); // transaction type. JSS(UNLModify); // transaction type. -JSS(Scale); // field. JSS(SettleDelay); // in: TransactionSign JSS(SendMax); // in: TransactionSign JSS(Sequence); // in/out: TransactionSign; field. @@ -149,7 +135,6 @@ JSS(TradingFee); // in/out: AMM trading fee JSS(TransactionType); // in: TransactionSign. JSS(TransferRate); // in: TransferRate. JSS(TrustSet); // transaction type. -JSS(URI); // field. JSS(VoteSlots); // out: AMM Vote JSS(XChainAddAccountCreateAttestation); // transaction type. JSS(XChainAddClaimAttestation); // transaction type. @@ -217,7 +202,6 @@ JSS(avg_bps_sent); // out: Peers JSS(balance); // out: AccountLines JSS(balances); // out: GatewayBalances JSS(base); // out: LogLevel -JSS(base_asset); // in: get_aggregate_price JSS(base_fee); // out: NetworkOPs JSS(base_fee_xrp); // out: NetworkOPs JSS(bids); // out: Subscribe @@ -315,7 +299,6 @@ JSS(enabled); // out: AmendmentTable JSS(engine_result); // out: NetworkOPs, TransactionSign, Submit JSS(engine_result_code); // out: NetworkOPs, TransactionSign, Submit JSS(engine_result_message); // out: NetworkOPs, TransactionSign, Submit -JSS(entire_set); // out: get_aggregate_price JSS(ephemeral_key); // out: ValidatorInfo // in/out: Manifest JSS(error); // out: error @@ -475,8 +458,6 @@ JSS(max_ledger); // in/out: LedgerCleaner JSS(max_queue_size); // out: TxQ JSS(max_spend_drops); // out: AccountInfo JSS(max_spend_drops_total); // out: AccountInfo -JSS(mean); // out: get_aggregate_price -JSS(median); // out: get_aggregate_price JSS(median_fee); // out: TxQ JSS(median_level); // out: TxQ JSS(message); // error. @@ -535,9 +516,6 @@ JSS(open); // out: handlers/Ledger JSS(open_ledger_cost); // out: SubmitTransaction JSS(open_ledger_fee); // out: TxQ JSS(open_ledger_level); // out: TxQ -JSS(oracle); // in: LedgerEntry -JSS(oracles); // in: get_aggregate_price -JSS(oracle_document_id); // in: get_aggregate_price JSS(owner); // in: LedgerEntry, out: NetworkOPs JSS(owner_funds); // in/out: Ledger, NetworkOPs, AcceptedLedgerTx JSS(page_index); @@ -584,7 +562,6 @@ JSS(queue); // in: AccountInfo JSS(queue_data); // out: AccountInfo JSS(queued); // out: SubmitTransaction JSS(queued_duration_us); -JSS(quote_asset); // in: get_aggregate_price JSS(random); // out: Random JSS(raw_meta); // out: AcceptedLedgerTx JSS(receive_currencies); // out: AccountCurrencies @@ -639,14 +616,12 @@ JSS(signing_keys); // out: ValidatorList JSS(signing_time); // out: NetworkOPs JSS(signer_list); // in: AccountObjects JSS(signer_lists); // in/out: AccountInfo -JSS(size); // out: get_aggregate_price JSS(snapshot); // in: Subscribe JSS(source_account); // in: PathRequest, RipplePathFind JSS(source_amount); // in: PathRequest, RipplePathFind JSS(source_currencies); // in: PathRequest, RipplePathFind JSS(source_tag); // out: AccountChannels JSS(stand_alone); // out: NetworkOPs -JSS(standard_deviation); // out: get_aggregate_price JSS(start); // in: TxHistory JSS(started); JSS(state); // out: Logic.h, ServerState, LedgerData @@ -662,7 +637,6 @@ JSS(sub_index); // in: LedgerEntry JSS(subcommand); // in: PathFind JSS(success); // rpc JSS(supported); // out: AmendmentTableImpl -JSS(sync_mode); // in: Submit JSS(system_time_offset); // out: NetworkOPs JSS(tag); // out: Peers JSS(taker); // in: Subscribe, BookOffers @@ -676,12 +650,9 @@ JSS(ticket_count); // out: AccountInfo JSS(ticket_seq); // in: LedgerEntry JSS(time); JSS(timeouts); // out: InboundLedger -JSS(time_threshold); // in/out: Oracle aggregate JSS(time_interval); // out: AMM Auction Slot JSS(track); // out: PeerImp JSS(traffic); // out: Overlay -JSS(trim); // in: get_aggregate_price -JSS(trimmed_set); // out: get_aggregate_price JSS(total); // out: counters JSS(total_bytes_recv); // out: Peers JSS(total_bytes_sent); // out: Peers diff --git a/src/ripple/rpc/handlers/GetAggregatePrice.cpp b/src/ripple/rpc/handlers/GetAggregatePrice.cpp deleted file mode 100644 index 5490cc4fcff..00000000000 --- a/src/ripple/rpc/handlers/GetAggregatePrice.cpp +++ /dev/null @@ -1,340 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2023 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -namespace ripple { - -using namespace boost::bimaps; -// sorted descending by lastUpdateTime, ascending by AssetPrice -using Prices = bimap< - multiset_of>, - multiset_of>; - -/** Calls callback "f" on the ledger-object sle and up to three previous - * metadata objects. Stops early if the callback returns true. - */ -static void -iteratePriceData( - RPC::JsonContext& context, - std::shared_ptr const& sle, - std::function&& f) -{ - using Meta = std::shared_ptr; - constexpr std::uint8_t maxHistory = 3; - bool isNew = false; - std::uint8_t history = 0; - - // `oracle` points to an object that has an `sfPriceDataSeries` field. - // When this function is called, that is a `PriceOracle` ledger object, - // but after one iteration of the loop below, it is an `sfNewFields` - // / `sfFinalFields` object in a `CreatedNode` / `ModifiedNode` object in - // a transaction's metadata. - - // `chain` points to an object that has `sfPreviousTxnID` and - // `sfPreviousTxnLgrSeq` fields. When this function is called, - // that is the `PriceOracle` ledger object pointed to by `oracle`, - // but after one iteration of the loop below, then it is a `ModifiedNode` - // / `CreatedNode` object in a transaction's metadata. - STObject const* oracle = sle.get(); - STObject const* chain = oracle; - // Use to test an unlikely scenario when CreatedNode / ModifiedNode - // for the Oracle is not found in the inner loop - STObject const* prevChain = nullptr; - - Meta meta = nullptr; - while (true) - { - if (prevChain == chain) - return; - - if (!oracle || f(*oracle) || isNew) - return; - - if (++history > maxHistory) - return; - - uint256 prevTx = chain->getFieldH256(sfPreviousTxnID); - std::uint32_t prevSeq = chain->getFieldU32(sfPreviousTxnLgrSeq); - - auto const ledger = context.ledgerMaster.getLedgerBySeq(prevSeq); - if (!ledger) - return; - - meta = ledger->txRead(prevTx).second; - - for (STObject const& node : meta->getFieldArray(sfAffectedNodes)) - { - if (node.getFieldU16(sfLedgerEntryType) != ltORACLE) - { - continue; - } - - prevChain = chain; - chain = &node; - isNew = node.isFieldPresent(sfNewFields); - // if a meta is for the new and this is the first - // look-up then it's the meta for the tx that - // created the current object; i.e. there is no - // historical data - if (isNew && history == 1) - return; - - oracle = isNew - ? &static_cast(node.peekAtField(sfNewFields)) - : &static_cast( - node.peekAtField(sfFinalFields)); - break; - } - } -} - -// Return avg, sd, data set size -static std::tuple -getStats( - Prices::right_const_iterator const& begin, - Prices::right_const_iterator const& end) -{ - STAmount avg{noIssue(), 0, 0}; - Number sd{0}; - std::uint16_t const size = std::distance(begin, end); - avg = std::accumulate( - begin, end, avg, [&](STAmount const& acc, auto const& it) { - return acc + it.first; - }); - avg = divide(avg, STAmount{noIssue(), size, 0}, noIssue()); - if (size > 1) - { - sd = std::accumulate( - begin, end, sd, [&](Number const& acc, auto const& it) { - return acc + (it.first - avg) * (it.first - avg); - }); - sd = root2(sd / (size - 1)); - } - return {avg, sd, size}; -}; - -/** - * oracles: array of {account, oracle_document_id} - * base_asset: is the asset to be priced - * quote_asset: is the denomination in which the prices are expressed - * trim : percentage of outliers to trim [optional] - * time_threshold : defines a range of prices to include based on the timestamp - * range - {most recent, most recent - time_threshold} [optional] - */ -Json::Value -doGetAggregatePrice(RPC::JsonContext& context) -{ - Json::Value result; - auto const& params(context.params); - - constexpr std::uint16_t maxOracles = 200; - if (!params.isMember(jss::oracles)) - return RPC::missing_field_error(jss::oracles); - if (!params[jss::oracles].isArray() || params[jss::oracles].size() == 0 || - params[jss::oracles].size() > maxOracles) - { - RPC::inject_error(rpcORACLE_MALFORMED, result); - return result; - } - - if (!params.isMember(jss::base_asset)) - return RPC::missing_field_error(jss::base_asset); - - if (!params.isMember(jss::quote_asset)) - return RPC::missing_field_error(jss::quote_asset); - - // Lambda to get `trim` and `time_threshold` fields. If the field - // is not included in the input then a default value is returned. - auto getField = [¶ms]( - Json::StaticString const& field, - unsigned int def = - 0) -> std::variant { - if (params.isMember(field)) - { - if (!params[field].isConvertibleTo(Json::ValueType::uintValue)) - return rpcORACLE_MALFORMED; - return params[field].asUInt(); - } - return def; - }; - - auto const trim = getField(jss::trim); - if (std::holds_alternative(trim)) - { - RPC::inject_error(std::get(trim), result); - return result; - } - if (params.isMember(jss::trim) && - (std::get(trim) == 0 || - std::get(trim) > maxTrim)) - { - RPC::inject_error(rpcINVALID_PARAMS, result); - return result; - } - - auto const timeThreshold = getField(jss::time_threshold, 0); - if (std::holds_alternative(timeThreshold)) - { - RPC::inject_error(std::get(timeThreshold), result); - return result; - } - - auto const& baseAsset = params[jss::base_asset]; - auto const& quoteAsset = params[jss::quote_asset]; - - // Collect the dataset into bimap keyed by lastUpdateTime and - // STAmount (Number is int64 and price is uint64) - Prices prices; - for (auto const& oracle : params[jss::oracles]) - { - if (!oracle.isMember(jss::oracle_document_id) || - !oracle.isMember(jss::account)) - { - RPC::inject_error(rpcORACLE_MALFORMED, result); - return result; - } - auto const documentID = oracle[jss::oracle_document_id].isConvertibleTo( - Json::ValueType::uintValue) - ? std::make_optional(oracle[jss::oracle_document_id].asUInt()) - : std::nullopt; - auto const account = - parseBase58(oracle[jss::account].asString()); - if (!account || account->isZero() || !documentID) - { - RPC::inject_error(rpcINVALID_PARAMS, result); - return result; - } - - std::shared_ptr ledger; - result = RPC::lookupLedger(ledger, context); - if (!ledger) - return result; - - auto const sle = ledger->read(keylet::oracle(*account, *documentID)); - iteratePriceData(context, sle, [&](STObject const& node) { - auto const& series = node.getFieldArray(sfPriceDataSeries); - // find the token pair entry with the price - if (auto iter = std::find_if( - series.begin(), - series.end(), - [&](STObject const& o) -> bool { - return o.getFieldCurrency(sfBaseAsset).getText() == - baseAsset && - o.getFieldCurrency(sfQuoteAsset).getText() == - quoteAsset && - o.isFieldPresent(sfAssetPrice); - }); - iter != series.end()) - { - auto const price = iter->getFieldU64(sfAssetPrice); - auto const scale = iter->isFieldPresent(sfScale) - ? -static_cast(iter->getFieldU8(sfScale)) - : 0; - prices.insert(Prices::value_type( - node.getFieldU32(sfLastUpdateTime), - STAmount{noIssue(), price, scale})); - return true; - } - return false; - }); - } - - if (prices.empty()) - { - RPC::inject_error(rpcOBJECT_NOT_FOUND, result); - return result; - } - - // erase outdated data - // sorted in descending, therefore begin is the latest, end is the oldest - auto const latestTime = prices.left.begin()->first; - if (auto const threshold = std::get(timeThreshold)) - { - // threshold defines an acceptable range {max,min} of lastUpdateTime as - // {latestTime, latestTime - threshold}, the prices with lastUpdateTime - // greater than (latestTime - threshold) are erased. - auto const oldestTime = prices.left.rbegin()->first; - auto const upperBound = - latestTime > threshold ? (latestTime - threshold) : oldestTime; - if (upperBound > oldestTime) - prices.left.erase( - prices.left.upper_bound(upperBound), prices.left.end()); - - if (prices.empty()) - { - RPC::inject_error(rpcOBJECT_NOT_FOUND, result); - return result; - } - } - result[jss::time] = latestTime; - - // calculate stats - auto const [avg, sd, size] = - getStats(prices.right.begin(), prices.right.end()); - result[jss::entire_set][jss::mean] = avg.getText(); - result[jss::entire_set][jss::size] = size; - result[jss::entire_set][jss::standard_deviation] = to_string(sd); - - auto itAdvance = [&](auto it, int distance) { - std::advance(it, distance); - return it; - }; - - auto const median = [&prices, &itAdvance, &size_ = size]() { - auto const middle = size_ / 2; - if ((size_ % 2) == 0) - { - static STAmount two{noIssue(), 2, 0}; - auto it = itAdvance(prices.right.begin(), middle - 1); - auto const& a1 = it->first; - auto const& a2 = (++it)->first; - return divide(a1 + a2, two, noIssue()); - } - return itAdvance(prices.right.begin(), middle)->first; - }(); - result[jss::median] = median.getText(); - - if (std::get(trim) != 0) - { - auto const trimCount = - prices.size() * std::get(trim) / 100; - - auto const [avg, sd, size] = getStats( - itAdvance(prices.right.begin(), trimCount), - itAdvance(prices.right.end(), -trimCount)); - result[jss::trimmed_set][jss::mean] = avg.getText(); - result[jss::trimmed_set][jss::size] = size; - result[jss::trimmed_set][jss::standard_deviation] = to_string(sd); - } - - return result; -} - -} // namespace ripple diff --git a/src/ripple/rpc/handlers/Handlers.h b/src/ripple/rpc/handlers/Handlers.h index 6c74c5c7e5c..ba93be54513 100644 --- a/src/ripple/rpc/handlers/Handlers.h +++ b/src/ripple/rpc/handlers/Handlers.h @@ -73,8 +73,6 @@ doGatewayBalances(RPC::JsonContext&); Json::Value doGetCounts(RPC::JsonContext&); Json::Value -doGetAggregatePrice(RPC::JsonContext&); -Json::Value doLedgerAccept(RPC::JsonContext&); Json::Value doLedgerCleaner(RPC::JsonContext&); diff --git a/src/ripple/rpc/handlers/LedgerEntry.cpp b/src/ripple/rpc/handlers/LedgerEntry.cpp index dfbc32e606a..28d9962e4d2 100644 --- a/src/ripple/rpc/handlers/LedgerEntry.cpp +++ b/src/ripple/rpc/handlers/LedgerEntry.cpp @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -599,51 +598,6 @@ doLedgerEntry(RPC::JsonContext& context) else uNodeIndex = keylet::did(*account).key; } - else if (context.params.isMember(jss::oracle)) - { - expectedType = ltORACLE; - if (!context.params[jss::oracle].isObject()) - { - if (!uNodeIndex.parseHex( - context.params[jss::oracle].asString())) - { - uNodeIndex = beast::zero; - jvResult[jss::error] = "malformedRequest"; - } - } - else if ( - !context.params[jss::oracle].isMember( - jss::oracle_document_id) || - !context.params[jss::oracle].isMember(jss::account)) - { - jvResult[jss::error] = "malformedRequest"; - } - else - { - uNodeIndex = beast::zero; - auto const& oracle = context.params[jss::oracle]; - auto const documentID = [&]() -> std::optional { - auto const& id = oracle[jss::oracle_document_id]; - if (id.isConvertibleTo(Json::ValueType::uintValue)) - return std::make_optional(id.asUInt()); - else if (id.isString()) - { - std::uint32_t v; - if (beast::lexicalCastChecked(v, id.asString())) - return std::make_optional(v); - } - return std::nullopt; - }(); - auto const account = - parseBase58(oracle[jss::account].asString()); - if (!account || account->isZero()) - jvResult[jss::error] = "malformedAddress"; - else if (!documentID) - jvResult[jss::error] = "malformedDocumentID"; - else - uNodeIndex = keylet::oracle(*account, *documentID).key; - } - } else { if (context.params.isMember("params") && diff --git a/src/ripple/rpc/impl/Handler.cpp b/src/ripple/rpc/impl/Handler.cpp index 1fc160dc4db..7ac8c5674f4 100644 --- a/src/ripple/rpc/impl/Handler.cpp +++ b/src/ripple/rpc/impl/Handler.cpp @@ -114,10 +114,6 @@ Handler const handlerArray[]{ {"gateway_balances", byRef(&doGatewayBalances), Role::USER, NO_CONDITION}, #endif {"get_counts", byRef(&doGetCounts), Role::ADMIN, NO_CONDITION}, - {"get_aggregate_price", - byRef(&doGetAggregatePrice), - Role::USER, - NO_CONDITION}, {"ledger_accept", byRef(&doLedgerAccept), Role::ADMIN, diff --git a/src/ripple/rpc/impl/RPCHelpers.cpp b/src/ripple/rpc/impl/RPCHelpers.cpp index a6179b04c88..35b9363ae4e 100644 --- a/src/ripple/rpc/impl/RPCHelpers.cpp +++ b/src/ripple/rpc/impl/RPCHelpers.cpp @@ -934,7 +934,7 @@ chooseLedgerEntryType(Json::Value const& params) std::pair result{RPC::Status::OK, ltANY}; if (params.isMember(jss::type)) { - static constexpr std::array, 22> + static constexpr std::array, 21> types{ {{jss::account, ltACCOUNT_ROOT}, {jss::amendments, ltAMENDMENTS}, @@ -948,7 +948,6 @@ chooseLedgerEntryType(Json::Value const& params) {jss::fee, ltFEE_SETTINGS}, {jss::hashes, ltLEDGER_HASHES}, {jss::nunl, ltNEGATIVE_UNL}, - {jss::oracle, ltORACLE}, {jss::nft_offer, ltNFTOKEN_OFFER}, {jss::nft_page, ltNFTOKEN_PAGE}, {jss::offer, ltOFFER}, diff --git a/src/test/app/Oracle_test.cpp b/src/test/app/Oracle_test.cpp deleted file mode 100644 index f5488c793a1..00000000000 --- a/src/test/app/Oracle_test.cpp +++ /dev/null @@ -1,698 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2023 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#include -#include - -namespace ripple { -namespace test { -namespace jtx { -namespace oracle { - -struct Oracle_test : public beast::unit_test::suite -{ -private: - // Helper function that returns the owner count of an account root. - static std::uint32_t - ownerCount(jtx::Env const& env, jtx::Account const& acct) - { - std::uint32_t ret{0}; - if (auto const sleAcct = env.le(acct)) - ret = sleAcct->at(sfOwnerCount); - return ret; - } - - void - testInvalidSet() - { - testcase("Invalid Set"); - - using namespace jtx; - Account const owner("owner"); - - { - // Invalid account - Env env(*this); - Account const bad("bad"); - env.memoize(bad); - Oracle oracle( - env, {.owner = bad, .seq = seq(1), .err = ter(terNO_ACCOUNT)}); - } - - // Insufficient reserve - { - Env env(*this); - env.fund(env.current()->fees().accountReserve(0), owner); - Oracle oracle( - env, {.owner = owner, .err = ter(tecINSUFFICIENT_RESERVE)}); - } - // Insufficient reserve if the data series extends to greater than 5 - { - Env env(*this); - env.fund( - env.current()->fees().accountReserve(1) + - env.current()->fees().base * 2, - owner); - Oracle oracle(env, {.owner = owner}); - BEAST_EXPECT(oracle.exists()); - oracle.set(UpdateArg{ - .series = - { - {"XRP", "EUR", 740, 1}, - {"XRP", "GBP", 740, 1}, - {"XRP", "CNY", 740, 1}, - {"XRP", "CAD", 740, 1}, - {"XRP", "AUD", 740, 1}, - }, - .err = ter(tecINSUFFICIENT_RESERVE)}); - } - - { - Env env(*this); - env.fund(XRP(1'000), owner); - Oracle oracle(env, {.owner = owner}, false); - - // Invalid flag - oracle.set( - CreateArg{.flags = tfSellNFToken, .err = ter(temINVALID_FLAG)}); - - // Duplicate token pair - oracle.set(CreateArg{ - .series = {{"XRP", "USD", 740, 1}, {"XRP", "USD", 750, 1}}, - .err = ter(temMALFORMED)}); - - // Price is not included - oracle.set(CreateArg{ - .series = - {{"XRP", "USD", 740, 1}, {"XRP", "EUR", std::nullopt, 1}}, - .err = ter(temMALFORMED)}); - - // Token pair is in update and delete - oracle.set(CreateArg{ - .series = - {{"XRP", "USD", 740, 1}, {"XRP", "USD", std::nullopt, 1}}, - .err = ter(temMALFORMED)}); - // Token pair is in add and delete - oracle.set(CreateArg{ - .series = - {{"XRP", "EUR", 740, 1}, {"XRP", "EUR", std::nullopt, 1}}, - .err = ter(temMALFORMED)}); - - // Array of token pair is 0 or exceeds 10 - oracle.set(CreateArg{ - .series = - {{"XRP", "US1", 740, 1}, - {"XRP", "US2", 750, 1}, - {"XRP", "US3", 740, 1}, - {"XRP", "US4", 750, 1}, - {"XRP", "US5", 740, 1}, - {"XRP", "US6", 750, 1}, - {"XRP", "US7", 740, 1}, - {"XRP", "US8", 750, 1}, - {"XRP", "US9", 740, 1}, - {"XRP", "U10", 750, 1}, - {"XRP", "U11", 740, 1}}, - .err = ter(temARRAY_TOO_LARGE)}); - oracle.set(CreateArg{.series = {}, .err = ter(temARRAY_EMPTY)}); - } - - // Array of token pair exceeds 10 after update - { - Env env{*this}; - env.fund(XRP(1'000), owner); - - Oracle oracle( - env, - CreateArg{ - .owner = owner, .series = {{{"XRP", "USD", 740, 1}}}}); - oracle.set(UpdateArg{ - .series = - { - {"XRP", "US1", 740, 1}, - {"XRP", "US2", 750, 1}, - {"XRP", "US3", 740, 1}, - {"XRP", "US4", 750, 1}, - {"XRP", "US5", 740, 1}, - {"XRP", "US6", 750, 1}, - {"XRP", "US7", 740, 1}, - {"XRP", "US8", 750, 1}, - {"XRP", "US9", 740, 1}, - {"XRP", "U10", 750, 1}, - }, - .err = ter(tecARRAY_TOO_LARGE)}); - } - - { - Env env(*this); - env.fund(XRP(1'000), owner); - Oracle oracle(env, {.owner = owner}, false); - - // Symbol class or provider not included on create - oracle.set(CreateArg{ - .assetClass = std::nullopt, - .provider = "provider", - .err = ter(temMALFORMED)}); - oracle.set(CreateArg{ - .assetClass = "currency", - .provider = std::nullopt, - .uri = "URI", - .err = ter(temMALFORMED)}); - - // Symbol class or provider are included on update - // and don't match the current values - oracle.set(CreateArg{}); - BEAST_EXPECT(oracle.exists()); - oracle.set(UpdateArg{ - .series = {{"XRP", "USD", 740, 1}}, - .provider = "provider1", - .err = ter(temMALFORMED)}); - oracle.set(UpdateArg{ - .series = {{"XRP", "USD", 740, 1}}, - .assetClass = "currency1", - .err = ter(temMALFORMED)}); - } - - { - Env env(*this); - env.fund(XRP(1'000), owner); - Oracle oracle(env, {.owner = owner}, false); - - // Fields too long - // Symbol class - std::string assetClass(17, '0'); - oracle.set( - CreateArg{.assetClass = assetClass, .err = ter(temMALFORMED)}); - // provider - std::string const large(257, '0'); - oracle.set(CreateArg{.provider = large, .err = ter(temMALFORMED)}); - // URI - oracle.set(CreateArg{.uri = large, .err = ter(temMALFORMED)}); - } - - { - // Different owner creates a new object and fails because - // of missing fields currency/provider - Env env(*this); - Account const some("some"); - env.fund(XRP(1'000), owner); - env.fund(XRP(1'000), some); - Oracle oracle(env, {.owner = owner}); - BEAST_EXPECT(oracle.exists()); - oracle.set(UpdateArg{ - .owner = some, - .series = {{"XRP", "USD", 740, 1}}, - .err = ter(temMALFORMED)}); - } - - { - // Invalid update time - using namespace std::chrono; - Env env(*this); - env.fund(XRP(1'000), owner); - Oracle oracle(env, {.owner = owner}); - BEAST_EXPECT(oracle.exists()); - env.close(seconds(400)); - // Less than the last close time - 300s - oracle.set(UpdateArg{ - .series = {{"XRP", "USD", 740, 1}}, - .lastUpdateTime = testStartTime.count() + 400 - 301, - .err = ter(tecINVALID_UPDATE_TIME)}); - // Greater than last close time + 300s - oracle.set(UpdateArg{ - .series = {{"XRP", "USD", 740, 1}}, - .lastUpdateTime = testStartTime.count() + 400 + 301, - .err = ter(tecINVALID_UPDATE_TIME)}); - oracle.set(UpdateArg{.series = {{"XRP", "USD", 740, 1}}}); - BEAST_EXPECT( - oracle.expectLastUpdateTime(testStartTime.count() + 450)); - // Less than the previous lastUpdateTime - oracle.set(UpdateArg{ - .series = {{"XRP", "USD", 740, 1}}, - .lastUpdateTime = testStartTime.count() + 449, - .err = ter(tecINVALID_UPDATE_TIME)}); - } - - { - // delete token pair that doesn't exist - Env env(*this); - env.fund(XRP(1'000), owner); - Oracle oracle(env, {.owner = owner}); - BEAST_EXPECT(oracle.exists()); - oracle.set(UpdateArg{ - .series = {{"XRP", "EUR", std::nullopt, std::nullopt}}, - .err = ter(tecTOKEN_PAIR_NOT_FOUND)}); - // delete all token pairs - oracle.set(UpdateArg{ - .series = {{"XRP", "USD", std::nullopt, std::nullopt}}, - .err = ter(tecARRAY_EMPTY)}); - } - - { - // same BaseAsset and QuoteAsset - Env env(*this); - env.fund(XRP(1'000), owner); - Oracle oracle( - env, - {.owner = owner, - .series = {{"USD", "USD", 740, 1}}, - .err = ter(temMALFORMED)}); - } - - { - // Scale is greater than maxPriceScale - Env env(*this); - env.fund(XRP(1'000), owner); - Oracle oracle( - env, - {.owner = owner, - .series = {{"USD", "BTC", 740, maxPriceScale + 1}}, - .err = ter(temMALFORMED)}); - } - } - - void - testCreate() - { - testcase("Create"); - using namespace jtx; - Account const owner("owner"); - - auto test = [&](Env& env, DataSeries const& series, std::uint16_t adj) { - env.fund(XRP(1'000), owner); - auto const count = ownerCount(env, owner); - Oracle oracle(env, {.owner = owner, .series = series}); - BEAST_EXPECT(oracle.exists()); - BEAST_EXPECT(ownerCount(env, owner) == (count + adj)); - BEAST_EXPECT(oracle.expectLastUpdateTime(946694810)); - }; - - { - // owner count is adjusted by 1 - Env env(*this); - test(env, {{"XRP", "USD", 740, 1}}, 1); - } - - { - // owner count is adjusted by 2 - Env env(*this); - test( - env, - {{"XRP", "USD", 740, 1}, - {"BTC", "USD", 740, 1}, - {"ETH", "USD", 740, 1}, - {"CAN", "USD", 740, 1}, - {"YAN", "USD", 740, 1}, - {"GBP", "USD", 740, 1}}, - 2); - } - - { - // Different owner creates a new object - Env env(*this); - Account const some("some"); - env.fund(XRP(1'000), owner); - env.fund(XRP(1'000), some); - Oracle oracle(env, {.owner = owner}); - BEAST_EXPECT(oracle.exists()); - oracle.set(CreateArg{ - .owner = some, .series = {{"912810RR9", "USD", 740, 1}}}); - BEAST_EXPECT(Oracle::exists(env, some, oracle.documentID())); - } - } - - void - testInvalidDelete() - { - testcase("Invalid Delete"); - - using namespace jtx; - Env env(*this); - Account const owner("owner"); - env.fund(XRP(1'000), owner); - Oracle oracle(env, {.owner = owner}); - BEAST_EXPECT(oracle.exists()); - - { - // Invalid account - Account const bad("bad"); - env.memoize(bad); - oracle.remove( - {.owner = bad, .seq = seq(1), .err = ter(terNO_ACCOUNT)}); - } - - // Invalid Sequence - oracle.remove({.documentID = 2, .err = ter(tecNO_ENTRY)}); - - // Invalid owner - Account const invalid("invalid"); - env.fund(XRP(1'000), invalid); - oracle.remove({.owner = invalid, .err = ter(tecNO_ENTRY)}); - } - - void - testDelete() - { - testcase("Delete"); - using namespace jtx; - Account const owner("owner"); - - auto test = [&](Env& env, DataSeries const& series, std::uint16_t adj) { - env.fund(XRP(1'000), owner); - Oracle oracle(env, {.owner = owner, .series = series}); - auto const count = ownerCount(env, owner); - BEAST_EXPECT(oracle.exists()); - oracle.remove({}); - BEAST_EXPECT(!oracle.exists()); - BEAST_EXPECT(ownerCount(env, owner) == (count - adj)); - }; - - { - // owner count is adjusted by 1 - Env env(*this); - test(env, {{"XRP", "USD", 740, 1}}, 1); - } - - { - // owner count is adjusted by 2 - Env env(*this); - test( - env, - { - {"XRP", "USD", 740, 1}, - {"BTC", "USD", 740, 1}, - {"ETH", "USD", 740, 1}, - {"CAN", "USD", 740, 1}, - {"YAN", "USD", 740, 1}, - {"GBP", "USD", 740, 1}, - }, - 2); - } - - { - // deleting the account deletes the oracles - Env env(*this); - auto const alice = Account("alice"); - auto const acctDelFee{drops(env.current()->fees().increment)}; - env.fund(XRP(1'000), owner); - env.fund(XRP(1'000), alice); - Oracle oracle( - env, {.owner = owner, .series = {{"XRP", "USD", 740, 1}}}); - Oracle oracle1( - env, - {.owner = owner, - .documentID = 2, - .series = {{"XRP", "EUR", 740, 1}}}); - BEAST_EXPECT(ownerCount(env, owner) == 2); - BEAST_EXPECT(oracle.exists()); - BEAST_EXPECT(oracle1.exists()); - auto const index = env.closed()->seq(); - auto const hash = env.closed()->info().hash; - for (int i = 0; i < 256; ++i) - env.close(); - env(acctdelete(owner, alice), fee(acctDelFee)); - env.close(); - BEAST_EXPECT(!oracle.exists()); - BEAST_EXPECT(!oracle1.exists()); - - // can still get the oracles via the ledger index or hash - auto verifyLedgerData = [&](auto const& field, auto const& value) { - Json::Value jvParams; - jvParams[field] = value; - jvParams[jss::binary] = false; - jvParams[jss::type] = jss::oracle; - Json::Value jrr = env.rpc( - "json", - "ledger_data", - boost::lexical_cast(jvParams)); - BEAST_EXPECT(jrr[jss::result][jss::state].size() == 2); - }; - verifyLedgerData(jss::ledger_index, index); - verifyLedgerData(jss::ledger_hash, to_string(hash)); - } - } - - void - testUpdate() - { - testcase("Update"); - using namespace jtx; - Account const owner("owner"); - - { - Env env(*this); - env.fund(XRP(1'000), owner); - auto count = ownerCount(env, owner); - Oracle oracle(env, {.owner = owner}); - BEAST_EXPECT(oracle.exists()); - - // update existing pair - oracle.set(UpdateArg{.series = {{"XRP", "USD", 740, 2}}}); - BEAST_EXPECT(oracle.expectPrice({{"XRP", "USD", 740, 2}})); - // owner count is increased by 1 since the oracle object is added - // with one token pair - count += 1; - BEAST_EXPECT(ownerCount(env, owner) == count); - - // add new pairs, not-included pair is reset - oracle.set(UpdateArg{.series = {{"XRP", "EUR", 700, 2}}}); - BEAST_EXPECT(oracle.expectPrice( - {{"XRP", "USD", 0, 0}, {"XRP", "EUR", 700, 2}})); - // owner count is not changed since the number of pairs is 2 - BEAST_EXPECT(ownerCount(env, owner) == count); - - // update both pairs - oracle.set(UpdateArg{ - .series = {{"XRP", "USD", 741, 2}, {"XRP", "EUR", 710, 2}}}); - BEAST_EXPECT(oracle.expectPrice( - {{"XRP", "USD", 741, 2}, {"XRP", "EUR", 710, 2}})); - // owner count is not changed since the number of pairs is 2 - BEAST_EXPECT(ownerCount(env, owner) == count); - - // owner count is increased by 1 since the number of pairs is 6 - oracle.set(UpdateArg{ - .series = { - {"BTC", "USD", 741, 2}, - {"ETH", "EUR", 710, 2}, - {"YAN", "EUR", 710, 2}, - {"CAN", "EUR", 710, 2}, - }}); - count += 1; - BEAST_EXPECT(ownerCount(env, owner) == count); - - // update two pairs and delete four - oracle.set(UpdateArg{ - .series = {{"BTC", "USD", std::nullopt, std::nullopt}}}); - oracle.set(UpdateArg{ - .series = { - {"XRP", "USD", 742, 2}, - {"XRP", "EUR", 711, 2}, - {"ETH", "EUR", std::nullopt, std::nullopt}, - {"YAN", "EUR", std::nullopt, std::nullopt}, - {"CAN", "EUR", std::nullopt, std::nullopt}}}); - BEAST_EXPECT(oracle.expectPrice( - {{"XRP", "USD", 742, 2}, {"XRP", "EUR", 711, 2}})); - // owner count is decreased by 1 since the number of pairs is 2 - count -= 1; - BEAST_EXPECT(ownerCount(env, owner) == count); - } - - // Min reserve to create and update - { - Env env(*this); - env.fund( - env.current()->fees().accountReserve(1) + - env.current()->fees().base * 2, - owner); - Oracle oracle(env, {.owner = owner}); - oracle.set(UpdateArg{.series = {{"XRP", "USD", 742, 2}}}); - } - } - - void - testMultisig(FeatureBitset features) - { - testcase("Multisig"); - using namespace jtx; - Oracle::setFee(100'000); - - Env env(*this, features); - Account const alice{"alice", KeyType::secp256k1}; - Account const bogie{"bogie", KeyType::secp256k1}; - Account const ed{"ed", KeyType::secp256k1}; - Account const becky{"becky", KeyType::ed25519}; - Account const zelda{"zelda", KeyType::secp256k1}; - Account const bob{"bob", KeyType::secp256k1}; - env.fund(XRP(10'000), alice, becky, zelda, ed, bob); - - // alice uses a regular key with the master disabled. - Account const alie{"alie", KeyType::secp256k1}; - env(regkey(alice, alie)); - env(fset(alice, asfDisableMaster), sig(alice)); - - // Attach signers to alice. - env(signers(alice, 2, {{becky, 1}, {bogie, 1}, {ed, 2}}), sig(alie)); - env.close(); - // if multiSignReserve disabled then its 2 + 1 per signer - int const signerListOwners{features[featureMultiSignReserve] ? 1 : 5}; - env.require(owners(alice, signerListOwners)); - - // Create - // Force close (true) and time advancement because the close time - // is no longer 0. - Oracle oracle(env, CreateArg{.owner = alice, .close = true}, false); - oracle.set(CreateArg{.msig = msig(becky), .err = ter(tefBAD_QUORUM)}); - oracle.set( - CreateArg{.msig = msig(zelda), .err = ter(tefBAD_SIGNATURE)}); - oracle.set(CreateArg{.msig = msig(becky, bogie)}); - BEAST_EXPECT(oracle.exists()); - - // Update - oracle.set(UpdateArg{ - .series = {{"XRP", "USD", 740, 1}}, - .msig = msig(becky), - .err = ter(tefBAD_QUORUM)}); - oracle.set(UpdateArg{ - .series = {{"XRP", "USD", 740, 1}}, - .msig = msig(zelda), - .err = ter(tefBAD_SIGNATURE)}); - oracle.set(UpdateArg{ - .series = {{"XRP", "USD", 741, 1}}, .msig = msig(becky, bogie)}); - BEAST_EXPECT(oracle.expectPrice({{"XRP", "USD", 741, 1}})); - // remove the signer list - env(signers(alice, jtx::none), sig(alie)); - env.close(); - env.require(owners(alice, 1)); - // create new signer list - env(signers(alice, 2, {{zelda, 1}, {bob, 1}, {ed, 2}}), sig(alie)); - env.close(); - // old list fails - oracle.set(UpdateArg{ - .series = {{"XRP", "USD", 740, 1}}, - .msig = msig(becky, bogie), - .err = ter(tefBAD_SIGNATURE)}); - // updated list succeeds - oracle.set(UpdateArg{ - .series = {{"XRP", "USD", 7412, 2}}, .msig = msig(zelda, bob)}); - BEAST_EXPECT(oracle.expectPrice({{"XRP", "USD", 7412, 2}})); - oracle.set( - UpdateArg{.series = {{"XRP", "USD", 74245, 3}}, .msig = msig(ed)}); - BEAST_EXPECT(oracle.expectPrice({{"XRP", "USD", 74245, 3}})); - - // Remove - oracle.remove({.msig = msig(bob), .err = ter(tefBAD_QUORUM)}); - oracle.remove({.msig = msig(becky), .err = ter(tefBAD_SIGNATURE)}); - oracle.remove({.msig = msig(ed)}); - BEAST_EXPECT(!oracle.exists()); - } - - void - testAmendment() - { - testcase("Amendment"); - using namespace jtx; - - auto const features = supported_amendments() - featurePriceOracle; - Account const owner("owner"); - Env env(*this, features); - - env.fund(XRP(1'000), owner); - { - Oracle oracle(env, {.owner = owner, .err = ter(temDISABLED)}); - } - - { - Oracle oracle(env, {.owner = owner}, false); - oracle.remove({.err = ter(temDISABLED)}); - } - } - - void - testLedgerEntry() - { - testcase("Ledger Entry"); - using namespace jtx; - - Env env(*this); - std::vector accounts; - std::vector oracles; - for (int i = 0; i < 10; ++i) - { - Account const owner(std::string("owner") + std::to_string(i)); - env.fund(XRP(1'000), owner); - // different accounts can have the same asset pair - Oracle oracle(env, {.owner = owner, .documentID = i}); - accounts.push_back(owner.id()); - oracles.push_back(oracle.documentID()); - // same account can have different asset pair - Oracle oracle1(env, {.owner = owner, .documentID = i + 10}); - accounts.push_back(owner.id()); - oracles.push_back(oracle1.documentID()); - } - for (int i = 0; i < accounts.size(); ++i) - { - auto const jv = [&]() { - // document id is uint32 - if (i % 2) - return Oracle::ledgerEntry(env, accounts[i], oracles[i]); - // document id is string - return Oracle::ledgerEntry( - env, accounts[i], std::to_string(oracles[i])); - }(); - try - { - BEAST_EXPECT( - jv[jss::node][jss::Owner] == to_string(accounts[i])); - } - catch (...) - { - fail(); - } - } - } - -public: - void - run() override - { - using namespace jtx; - auto const all = supported_amendments(); - testInvalidSet(); - testInvalidDelete(); - testCreate(); - testDelete(); - testUpdate(); - testAmendment(); - for (auto const& features : - {all, - all - featureMultiSignReserve - featureExpandedSignerList, - all - featureExpandedSignerList}) - testMultisig(features); - testLedgerEntry(); - } -}; - -BEAST_DEFINE_TESTSUITE(Oracle, app, ripple); - -} // namespace oracle - -} // namespace jtx - -} // namespace test - -} // namespace ripple diff --git a/src/test/jtx/Oracle.h b/src/test/jtx/Oracle.h deleted file mode 100644 index f6fdbbff34a..00000000000 --- a/src/test/jtx/Oracle.h +++ /dev/null @@ -1,186 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2012, 2013 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#ifndef RIPPLE_TEST_JTX_ORACLE_H_INCLUDED -#define RIPPLE_TEST_JTX_ORACLE_H_INCLUDED - -#include -#include - -namespace ripple { -namespace test { -namespace jtx { -namespace oracle { - -// base asset, quote asset, price, scale -using DataSeries = std::vector, - std::optional>>; - -// Typical defaults for Create -struct CreateArg -{ - std::optional owner = std::nullopt; - std::optional documentID = 1; - DataSeries series = {{"XRP", "USD", 740, 1}}; - std::optional assetClass = "currency"; - std::optional provider = "provider"; - std::optional uri = "URI"; - std::optional lastUpdateTime = std::nullopt; - std::uint32_t flags = 0; - std::optional msig = std::nullopt; - std::optional seq = std::nullopt; - std::uint32_t fee = 10; - std::optional err = std::nullopt; - bool close = false; -}; - -// Typical defaults for Update -struct UpdateArg -{ - std::optional owner = std::nullopt; - std::optional documentID = std::nullopt; - DataSeries series = {}; - std::optional assetClass = std::nullopt; - std::optional provider = std::nullopt; - std::optional uri = "URI"; - std::optional lastUpdateTime = std::nullopt; - std::uint32_t flags = 0; - std::optional msig = std::nullopt; - std::optional seq = std::nullopt; - std::uint32_t fee = 10; - std::optional err = std::nullopt; -}; - -struct RemoveArg -{ - std::optional const& owner = std::nullopt; - std::optional const& documentID = std::nullopt; - std::optional const& msig = std::nullopt; - std::optional seq = std::nullopt; - std::uint32_t fee = 10; - std::optional const& err = std::nullopt; -}; - -// Simulate testStartTime as 10'000s from Ripple epoch time to make -// LastUpdateTime validation to work and to make unit-test consistent. -// The value doesn't matter much, it has to be greater -// than maxLastUpdateTimeDelta in order to pass LastUpdateTime -// validation {close-maxLastUpdateTimeDelta,close+maxLastUpdateTimeDelta}. -constexpr static std::chrono::seconds testStartTime = - epoch_offset + std::chrono::seconds(10'000); - -/** Oracle class facilitates unit-testing of the Price Oracle feature. - * It defines functions to create, update, and delete the Oracle object, - * to query for various states, and to call APIs. - */ -class Oracle -{ -private: - // Global fee if not 0 - static inline std::uint32_t fee = 0; - Env& env_; - AccountID owner_; - std::uint32_t documentID_; - -private: - void - submit( - Json::Value const& jv, - std::optional const& msig, - std::optional const& seq, - std::optional const& err); - -public: - Oracle(Env& env, CreateArg const& arg, bool submit = true); - - void - remove(RemoveArg const& arg); - - void - set(CreateArg const& arg); - void - set(UpdateArg const& arg); - - static Json::Value - aggregatePrice( - Env& env, - std::optional const& baseAsset, - std::optional const& quoteAsset, - std::optional>> const& - oracles = std::nullopt, - std::optional const& trim = std::nullopt, - std::optional const& timeTreshold = std::nullopt); - - std::uint32_t - documentID() const - { - return documentID_; - } - - [[nodiscard]] bool - exists() const - { - return exists(env_, owner_, documentID_); - } - - [[nodiscard]] static bool - exists(Env& env, AccountID const& account, std::uint32_t documentID); - - [[nodiscard]] bool - expectPrice(DataSeries const& pricess) const; - - [[nodiscard]] bool - expectLastUpdateTime(std::uint32_t lastUpdateTime) const; - - static Json::Value - ledgerEntry( - Env& env, - AccountID const& account, - std::variant const& documentID, - std::optional const& index = std::nullopt); - - Json::Value - ledgerEntry(std::optional const& index = std::nullopt) const - { - return Oracle::ledgerEntry(env_, owner_, documentID_, index); - } - - static void - setFee(std::uint32_t f) - { - fee = f; - } - - friend std::ostream& - operator<<(std::ostream& strm, Oracle const& oracle) - { - strm << oracle.ledgerEntry().toStyledString(); - return strm; - } -}; - -} // namespace oracle -} // namespace jtx -} // namespace test -} // namespace ripple - -#endif // RIPPLE_TEST_JTX_ORACLE_H_INCLUDED diff --git a/src/test/jtx/impl/Oracle.cpp b/src/test/jtx/impl/Oracle.cpp deleted file mode 100644 index 95da59952a0..00000000000 --- a/src/test/jtx/impl/Oracle.cpp +++ /dev/null @@ -1,292 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2012, 2013 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#include -#include -#include - -#include - -#include - -namespace ripple { -namespace test { -namespace jtx { -namespace oracle { - -Oracle::Oracle(Env& env, CreateArg const& arg, bool submit) - : env_(env), owner_{}, documentID_{} -{ - // LastUpdateTime is checked to be in range - // {close-maxLastUpdateTimeDelta, close+maxLastUpdateTimeDelta}. - // To make the validation work and to make the clock consistent - // for tests running at different time, simulate Unix time starting - // on testStartTime since Ripple epoch. - auto const now = env_.timeKeeper().now(); - if (now.time_since_epoch().count() == 0 || arg.close) - env_.close(now + testStartTime - epoch_offset); - if (arg.owner) - owner_ = *arg.owner; - if (arg.documentID) - documentID_ = *arg.documentID; - if (submit) - set(arg); -} - -void -Oracle::remove(RemoveArg const& arg) -{ - Json::Value jv; - jv[jss::TransactionType] = jss::OracleDelete; - jv[jss::Account] = to_string(arg.owner.value_or(owner_)); - jv[jss::OracleDocumentID] = arg.documentID.value_or(documentID_); - if (Oracle::fee != 0) - jv[jss::Fee] = std::to_string(Oracle::fee); - else if (arg.fee != 0) - jv[jss::Fee] = std::to_string(arg.fee); - else - jv[jss::Fee] = std::to_string(env_.current()->fees().increment.drops()); - submit(jv, arg.msig, arg.seq, arg.err); -} - -void -Oracle::submit( - Json::Value const& jv, - std::optional const& msig, - std::optional const& seq, - std::optional const& err) -{ - if (msig) - { - if (seq && err) - env_(jv, *msig, *seq, *err); - else if (seq) - env_(jv, *msig, *seq); - else if (err) - env_(jv, *msig, *err); - else - env_(jv, *msig); - } - else if (seq && err) - env_(jv, *seq, *err); - else if (seq) - env_(jv, *seq); - else if (err) - env_(jv, *err); - else - env_(jv); - env_.close(); -} - -bool -Oracle::exists(Env& env, AccountID const& account, std::uint32_t documentID) -{ - assert(account.isNonZero()); - return env.le(keylet::oracle(account, documentID)) != nullptr; -} - -bool -Oracle::expectPrice(DataSeries const& series) const -{ - if (auto const sle = env_.le(keylet::oracle(owner_, documentID_))) - { - auto const& leSeries = sle->getFieldArray(sfPriceDataSeries); - if (leSeries.size() == 0 || leSeries.size() != series.size()) - return false; - for (auto const& data : series) - { - if (std::find_if( - leSeries.begin(), - leSeries.end(), - [&](STObject const& o) -> bool { - auto const& baseAsset = o.getFieldCurrency(sfBaseAsset); - auto const& quoteAsset = - o.getFieldCurrency(sfQuoteAsset); - auto const& price = o.getFieldU64(sfAssetPrice); - auto const& scale = o.getFieldU8(sfScale); - return baseAsset.getText() == std::get<0>(data) && - quoteAsset.getText() == std::get<1>(data) && - price == std::get<2>(data) && - scale == std::get<3>(data); - }) == leSeries.end()) - return false; - } - return true; - } - return false; -} - -bool -Oracle::expectLastUpdateTime(std::uint32_t lastUpdateTime) const -{ - auto const sle = env_.le(keylet::oracle(owner_, documentID_)); - return sle && (*sle)[sfLastUpdateTime] == lastUpdateTime; -} - -Json::Value -Oracle::aggregatePrice( - Env& env, - std::optional const& baseAsset, - std::optional const& quoteAsset, - std::optional>> const& - oracles, - std::optional const& trim, - std::optional const& timeThreshold) -{ - Json::Value jv; - Json::Value jvOracles(Json::arrayValue); - if (oracles) - { - for (auto const& id : *oracles) - { - Json::Value oracle; - oracle[jss::account] = to_string(id.first.id()); - oracle[jss::oracle_document_id] = id.second; - jvOracles.append(oracle); - } - jv[jss::oracles] = jvOracles; - } - if (trim) - jv[jss::trim] = *trim; - if (baseAsset) - jv[jss::base_asset] = *baseAsset; - if (quoteAsset) - jv[jss::quote_asset] = *quoteAsset; - if (timeThreshold) - jv[jss::time_threshold] = *timeThreshold; - - auto jr = env.rpc("json", "get_aggregate_price", to_string(jv)); - - if (jr.isObject() && jr.isMember(jss::result) && - jr[jss::result].isMember(jss::status)) - return jr[jss::result]; - return Json::nullValue; -} - -void -Oracle::set(UpdateArg const& arg) -{ - using namespace std::chrono; - Json::Value jv; - if (arg.owner) - owner_ = *arg.owner; - if (arg.documentID) - documentID_ = *arg.documentID; - jv[jss::TransactionType] = jss::OracleSet; - jv[jss::Account] = to_string(owner_); - jv[jss::OracleDocumentID] = documentID_; - if (arg.assetClass) - jv[jss::AssetClass] = strHex(*arg.assetClass); - if (arg.provider) - jv[jss::Provider] = strHex(*arg.provider); - if (arg.uri) - jv[jss::URI] = strHex(*arg.uri); - if (arg.flags != 0) - jv[jss::Flags] = arg.flags; - if (Oracle::fee != 0) - jv[jss::Fee] = std::to_string(Oracle::fee); - else if (arg.fee != 0) - jv[jss::Fee] = std::to_string(arg.fee); - else - jv[jss::Fee] = std::to_string(env_.current()->fees().increment.drops()); - // lastUpdateTime if provided is offset from testStartTime - if (arg.lastUpdateTime) - jv[jss::LastUpdateTime] = - to_string(testStartTime.count() + *arg.lastUpdateTime); - else - jv[jss::LastUpdateTime] = to_string( - duration_cast( - env_.current()->info().closeTime.time_since_epoch()) - .count() + - epoch_offset.count()); - Json::Value dataSeries(Json::arrayValue); - auto assetToStr = [](std::string const& s) { - // assume standard currency - if (s.size() == 3) - return s; - assert(s.size() <= 20); - // anything else must be 160-bit hex string - std::string h = strHex(s); - return strHex(s).append(40 - s.size() * 2, '0'); - }; - for (auto const& data : arg.series) - { - Json::Value priceData; - Json::Value price; - price[jss::BaseAsset] = assetToStr(std::get<0>(data)); - price[jss::QuoteAsset] = assetToStr(std::get<1>(data)); - if (std::get<2>(data)) - price[jss::AssetPrice] = *std::get<2>(data); - if (std::get<3>(data)) - price[jss::Scale] = *std::get<3>(data); - priceData[jss::PriceData] = price; - dataSeries.append(priceData); - } - jv[jss::PriceDataSeries] = dataSeries; - submit(jv, arg.msig, arg.seq, arg.err); -} - -void -Oracle::set(CreateArg const& arg) -{ - set(UpdateArg{ - .owner = arg.owner, - .documentID = arg.documentID, - .series = arg.series, - .assetClass = arg.assetClass, - .provider = arg.provider, - .uri = arg.uri, - .lastUpdateTime = arg.lastUpdateTime, - .flags = arg.flags, - .msig = arg.msig, - .seq = arg.seq, - .fee = arg.fee, - .err = arg.err}); -} - -Json::Value -Oracle::ledgerEntry( - Env& env, - AccountID const& account, - std::variant const& documentID, - std::optional const& index) -{ - Json::Value jvParams; - jvParams[jss::oracle][jss::account] = to_string(account); - if (std::holds_alternative(documentID)) - jvParams[jss::oracle][jss::oracle_document_id] = - std::get(documentID); - else - jvParams[jss::oracle][jss::oracle_document_id] = - std::get(documentID); - if (index) - { - std::uint32_t i; - if (boost::conversion::try_lexical_convert(*index, i)) - jvParams[jss::oracle][jss::ledger_index] = i; - else - jvParams[jss::oracle][jss::ledger_index] = *index; - } - return env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; -} - -} // namespace oracle -} // namespace jtx -} // namespace test -} // namespace ripple \ No newline at end of file diff --git a/src/test/ledger/Directory_test.cpp b/src/test/ledger/Directory_test.cpp index 42dcdab9bd1..ce32e1c5c80 100644 --- a/src/test/ledger/Directory_test.cpp +++ b/src/test/ledger/Directory_test.cpp @@ -463,7 +463,7 @@ struct Directory_test : public beast::unit_test::suite BEAST_EXPECT( directory["LedgerEntryType"] == jss::DirectoryNode); // sanity check - if (directory[jss::Owner] == gw.human()) + if (directory[sfOwner.jsonName] == gw.human()) { // gw's directory did not get touched, so it // should not have those fields populated diff --git a/src/test/rpc/GetAggregatePrice_test.cpp b/src/test/rpc/GetAggregatePrice_test.cpp deleted file mode 100644 index 4c45b7b9d2b..00000000000 --- a/src/test/rpc/GetAggregatePrice_test.cpp +++ /dev/null @@ -1,260 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2023 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#include -#include -#include -#include - -namespace ripple { -namespace test { -namespace jtx { -namespace oracle { - -class GetAggregatePrice_test : public beast::unit_test::suite -{ -public: - void - testErrors() - { - testcase("Errors"); - using namespace jtx; - using Oracles = std::vector>; - Account const owner{"owner"}; - Account const some{"some"}; - static Oracles oracles = {{owner, 1}}; - - { - Env env(*this); - // missing base_asset - auto ret = - Oracle::aggregatePrice(env, std::nullopt, "USD", oracles); - BEAST_EXPECT( - ret[jss::error_message].asString() == - "Missing field 'base_asset'."); - - // missing quote_asset - ret = Oracle::aggregatePrice(env, "XRP", std::nullopt, oracles); - BEAST_EXPECT( - ret[jss::error_message].asString() == - "Missing field 'quote_asset'."); - - // missing oracles array - ret = Oracle::aggregatePrice(env, "XRP", "USD"); - BEAST_EXPECT( - ret[jss::error_message].asString() == - "Missing field 'oracles'."); - - // empty oracles array - ret = Oracle::aggregatePrice(env, "XRP", "USD", Oracles{}); - BEAST_EXPECT(ret[jss::error].asString() == "oracleMalformed"); - - // invalid oracle document id - ret = Oracle::aggregatePrice(env, "XRP", "USD", {{{owner, 2}}}); - BEAST_EXPECT(ret[jss::error].asString() == "objectNotFound"); - - // invalid owner - ret = Oracle::aggregatePrice(env, "XRP", "USD", {{{some, 1}}}); - BEAST_EXPECT(ret[jss::error].asString() == "objectNotFound"); - - // oracles have wrong asset pair - env.fund(XRP(1'000), owner); - Oracle oracle( - env, {.owner = owner, .series = {{"XRP", "EUR", 740, 1}}}); - ret = Oracle::aggregatePrice( - env, "XRP", "USD", {{{owner, oracle.documentID()}}}); - BEAST_EXPECT(ret[jss::error].asString() == "objectNotFound"); - - // invalid trim value - ret = Oracle::aggregatePrice( - env, "XRP", "USD", {{{owner, oracle.documentID()}}}, 0); - BEAST_EXPECT(ret[jss::error].asString() == "invalidParams"); - ret = Oracle::aggregatePrice( - env, "XRP", "USD", {{{owner, oracle.documentID()}}}, 26); - BEAST_EXPECT(ret[jss::error].asString() == "invalidParams"); - } - - // too many oracles - { - Env env(*this); - std::vector> oracles; - for (int i = 0; i < 201; ++i) - { - Account const owner(std::to_string(i)); - env.fund(XRP(1'000), owner); - Oracle oracle(env, {.owner = owner, .documentID = i}); - oracles.emplace_back(owner, oracle.documentID()); - } - auto const ret = Oracle::aggregatePrice(env, "XRP", "USD", oracles); - BEAST_EXPECT(ret[jss::error].asString() == "oracleMalformed"); - } - } - - void - testRpc() - { - testcase("RPC"); - using namespace jtx; - - auto prep = [&](Env& env, auto& oracles) { - oracles.reserve(10); - for (int i = 0; i < 10; ++i) - { - Account const owner{std::to_string(i)}; - env.fund(XRP(1'000), owner); - Oracle oracle( - env, - {.owner = owner, - .documentID = rand(), - .series = { - {"XRP", "USD", 740 + i, 1}, {"XRP", "EUR", 740, 1}}}); - oracles.emplace_back(owner, oracle.documentID()); - } - }; - - // Aggregate data set includes all price oracle instances, no trimming - // or time threshold - { - Env env(*this); - std::vector> oracles; - prep(env, oracles); - // entire and trimmed stats - auto ret = Oracle::aggregatePrice(env, "XRP", "USD", oracles); - BEAST_EXPECT(ret[jss::entire_set][jss::mean] == "74.45"); - BEAST_EXPECT(ret[jss::entire_set][jss::size].asUInt() == 10); - BEAST_EXPECT( - ret[jss::entire_set][jss::standard_deviation] == - "0.3027650354097492"); - BEAST_EXPECT(ret[jss::median] == "74.45"); - BEAST_EXPECT(ret[jss::time] == 946694900); - } - - // Aggregate data set includes all price oracle instances - { - Env env(*this); - std::vector> oracles; - prep(env, oracles); - // entire and trimmed stats - auto ret = - Oracle::aggregatePrice(env, "XRP", "USD", oracles, 20, 100); - BEAST_EXPECT(ret[jss::entire_set][jss::mean] == "74.45"); - BEAST_EXPECT(ret[jss::entire_set][jss::size].asUInt() == 10); - BEAST_EXPECT( - ret[jss::entire_set][jss::standard_deviation] == - "0.3027650354097492"); - BEAST_EXPECT(ret[jss::median] == "74.45"); - BEAST_EXPECT(ret[jss::trimmed_set][jss::mean] == "74.45"); - BEAST_EXPECT(ret[jss::trimmed_set][jss::size].asUInt() == 6); - BEAST_EXPECT( - ret[jss::trimmed_set][jss::standard_deviation] == - "0.187082869338697"); - BEAST_EXPECT(ret[jss::time] == 946694900); - } - - // A reduced dataset, as some price oracles have data beyond three - // updated ledgers - { - Env env(*this); - std::vector> oracles; - prep(env, oracles); - for (int i = 0; i < 3; ++i) - { - Oracle oracle( - env, - {.owner = oracles[i].first, - .documentID = oracles[i].second}, - false); - // push XRP/USD by more than three ledgers, so this price - // oracle is not included in the dataset - oracle.set(UpdateArg{.series = {{"XRP", "EUR", 740, 1}}}); - oracle.set(UpdateArg{.series = {{"XRP", "EUR", 740, 1}}}); - oracle.set(UpdateArg{.series = {{"XRP", "EUR", 740, 1}}}); - } - for (int i = 3; i < 6; ++i) - { - Oracle oracle( - env, - {.owner = oracles[i].first, - .documentID = oracles[i].second}, - false); - // push XRP/USD by two ledgers, so this price - // is included in the dataset - oracle.set(UpdateArg{.series = {{"XRP", "EUR", 740, 1}}}); - oracle.set(UpdateArg{.series = {{"XRP", "EUR", 740, 1}}}); - } - - // entire and trimmed stats - auto ret = - Oracle::aggregatePrice(env, "XRP", "USD", oracles, 20, 200); - BEAST_EXPECT(ret[jss::entire_set][jss::mean] == "74.6"); - BEAST_EXPECT(ret[jss::entire_set][jss::size].asUInt() == 7); - BEAST_EXPECT( - ret[jss::entire_set][jss::standard_deviation] == - "0.2160246899469287"); - BEAST_EXPECT(ret[jss::median] == "74.6"); - BEAST_EXPECT(ret[jss::trimmed_set][jss::mean] == "74.6"); - BEAST_EXPECT(ret[jss::trimmed_set][jss::size].asUInt() == 5); - BEAST_EXPECT( - ret[jss::trimmed_set][jss::standard_deviation] == - "0.158113883008419"); - BEAST_EXPECT(ret[jss::time] == 946694900); - } - - // Reduced data set because of the time threshold - { - Env env(*this); - std::vector> oracles; - prep(env, oracles); - for (int i = 0; i < oracles.size(); ++i) - { - Oracle oracle( - env, - {.owner = oracles[i].first, - .documentID = oracles[i].second}, - false); - // push XRP/USD by two ledgers, so this price - // is included in the dataset - oracle.set(UpdateArg{.series = {{"XRP", "USD", 740, 1}}}); - } - - // entire stats only, limit lastUpdateTime to {200, 125} - auto ret = Oracle::aggregatePrice( - env, "XRP", "USD", oracles, std::nullopt, 75); - BEAST_EXPECT(ret[jss::entire_set][jss::mean] == "74"); - BEAST_EXPECT(ret[jss::entire_set][jss::size].asUInt() == 8); - BEAST_EXPECT(ret[jss::entire_set][jss::standard_deviation] == "0"); - BEAST_EXPECT(ret[jss::median] == "74"); - BEAST_EXPECT(ret[jss::time] == 946695000); - } - } - - void - run() override - { - testErrors(); - testRpc(); - } -}; - -BEAST_DEFINE_TESTSUITE(GetAggregatePrice, app, ripple); - -} // namespace oracle -} // namespace jtx -} // namespace test -} // namespace ripple