Skip to content

Commit e718378

Browse files
authored
Price Oracle (XLS-47d): (#4789) (#4789)
Implement native support for Price Oracles. A Price Oracle is used to bring real-world data, such as market prices, onto the blockchain, enabling dApps to access and utilize information that resides outside the blockchain. Add Price Oracle functionality: - OracleSet: create or update the Oracle object - OracleDelete: delete the Oracle object To support this functionality add: - New RPC method, `get_aggregate_price`, to calculate aggregate price for a token pair of the specified oracles - `ltOracle` object The `ltOracle` object maintains: - Oracle Owner's account - Oracle's metadata - Up to ten token pairs with the scaled price - The last update time the token pairs were updated Add Oracle unit-tests
1 parent d7d15a9 commit e718378

40 files changed

+2865
-7
lines changed

Builds/CMake/RippledCore.cmake

+7
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ target_sources (xrpl_core PRIVATE
9898
src/ripple/protocol/impl/STArray.cpp
9999
src/ripple/protocol/impl/STBase.cpp
100100
src/ripple/protocol/impl/STBlob.cpp
101+
src/ripple/protocol/impl/STCurrency.cpp
101102
src/ripple/protocol/impl/STInteger.cpp
102103
src/ripple/protocol/impl/STLedgerEntry.cpp
103104
src/ripple/protocol/impl/STObject.cpp
@@ -553,6 +554,7 @@ target_sources (rippled PRIVATE
553554
src/ripple/app/tx/impl/CreateOffer.cpp
554555
src/ripple/app/tx/impl/CreateTicket.cpp
555556
src/ripple/app/tx/impl/DeleteAccount.cpp
557+
src/ripple/app/tx/impl/DeleteOracle.cpp
556558
src/ripple/app/tx/impl/DepositPreauth.cpp
557559
src/ripple/app/tx/impl/DID.cpp
558560
src/ripple/app/tx/impl/Escrow.cpp
@@ -566,6 +568,7 @@ target_sources (rippled PRIVATE
566568
src/ripple/app/tx/impl/PayChan.cpp
567569
src/ripple/app/tx/impl/Payment.cpp
568570
src/ripple/app/tx/impl/SetAccount.cpp
571+
src/ripple/app/tx/impl/SetOracle.cpp
569572
src/ripple/app/tx/impl/SetRegularKey.cpp
570573
src/ripple/app/tx/impl/SetSignerList.cpp
571574
src/ripple/app/tx/impl/SetTrust.cpp
@@ -721,6 +724,7 @@ target_sources (rippled PRIVATE
721724
src/ripple/rpc/handlers/FetchInfo.cpp
722725
src/ripple/rpc/handlers/GatewayBalances.cpp
723726
src/ripple/rpc/handlers/GetCounts.cpp
727+
src/ripple/rpc/handlers/GetAggregatePrice.cpp
724728
src/ripple/rpc/handlers/LedgerAccept.cpp
725729
src/ripple/rpc/handlers/LedgerCleanerHandler.cpp
726730
src/ripple/rpc/handlers/LedgerClosed.cpp
@@ -840,6 +844,7 @@ if (tests)
840844
src/test/app/NFTokenDir_test.cpp
841845
src/test/app/OfferStream_test.cpp
842846
src/test/app/Offer_test.cpp
847+
src/test/app/Oracle_test.cpp
843848
src/test/app/OversizeMeta_test.cpp
844849
src/test/app/Path_test.cpp
845850
src/test/app/PayChan_test.cpp
@@ -964,6 +969,7 @@ if (tests)
964969
src/test/jtx/impl/AMMTest.cpp
965970
src/test/jtx/impl/Env.cpp
966971
src/test/jtx/impl/JSONRPCClient.cpp
972+
src/test/jtx/impl/Oracle.cpp
967973
src/test/jtx/impl/TestHelpers.cpp
968974
src/test/jtx/impl/WSClient.cpp
969975
src/test/jtx/impl/acctdelete.cpp
@@ -1089,6 +1095,7 @@ if (tests)
10891095
src/test/rpc/DeliveredAmount_test.cpp
10901096
src/test/rpc/Feature_test.cpp
10911097
src/test/rpc/GatewayBalances_test.cpp
1098+
src/test/rpc/GetAggregatePrice_test.cpp
10921099
src/test/rpc/GetCounts_test.cpp
10931100
src/test/rpc/JSONRPC_test.cpp
10941101
src/test/rpc/KeyGeneration_test.cpp

src/ripple/app/tx/impl/DeleteAccount.cpp

+15
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#include <ripple/app/tx/impl/DID.h>
2121
#include <ripple/app/tx/impl/DeleteAccount.h>
22+
#include <ripple/app/tx/impl/DeleteOracle.h>
2223
#include <ripple/app/tx/impl/DepositPreauth.h>
2324
#include <ripple/app/tx/impl/SetSignerList.h>
2425
#include <ripple/app/tx/impl/details/NFTokenUtils.h>
@@ -146,6 +147,18 @@ removeDIDFromLedger(
146147
return DIDDelete::deleteSLE(view, sleDel, account, j);
147148
}
148149

150+
TER
151+
removeOracleFromLedger(
152+
Application&,
153+
ApplyView& view,
154+
AccountID const& account,
155+
uint256 const&,
156+
std::shared_ptr<SLE> const& sleDel,
157+
beast::Journal j)
158+
{
159+
return DeleteOracle::deleteOracle(view, sleDel, account, j);
160+
}
161+
149162
// Return nullptr if the LedgerEntryType represents an obligation that can't
150163
// be deleted. Otherwise return the pointer to the function that can delete
151164
// the non-obligation
@@ -166,6 +179,8 @@ nonObligationDeleter(LedgerEntryType t)
166179
return removeNFTokenOfferFromLedger;
167180
case ltDID:
168181
return removeDIDFromLedger;
182+
case ltORACLE:
183+
return removeOracleFromLedger;
169184
default:
170185
return nullptr;
171186
}
+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
//------------------------------------------------------------------------------
2+
/*
3+
This file is part of rippled: https://github.com/ripple/rippled
4+
Copyright (c) 2023 Ripple Labs Inc.
5+
6+
Permission to use, copy, modify, and/or distribute this software for any
7+
purpose with or without fee is hereby granted, provided that the above
8+
copyright notice and this permission notice appear in all copies.
9+
10+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13+
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17+
*/
18+
//==============================================================================
19+
20+
#include <ripple/app/tx/impl/DeleteOracle.h>
21+
#include <ripple/ledger/Sandbox.h>
22+
#include <ripple/ledger/View.h>
23+
#include <ripple/protocol/Feature.h>
24+
#include <ripple/protocol/Rules.h>
25+
#include <ripple/protocol/TxFlags.h>
26+
27+
namespace ripple {
28+
29+
NotTEC
30+
DeleteOracle::preflight(PreflightContext const& ctx)
31+
{
32+
if (!ctx.rules.enabled(featurePriceOracle))
33+
return temDISABLED;
34+
35+
if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
36+
return ret;
37+
38+
if (ctx.tx.getFlags() & tfUniversalMask)
39+
{
40+
JLOG(ctx.j.debug()) << "Oracle Delete: invalid flags.";
41+
return temINVALID_FLAG;
42+
}
43+
44+
return preflight2(ctx);
45+
}
46+
47+
TER
48+
DeleteOracle::preclaim(PreclaimContext const& ctx)
49+
{
50+
if (!ctx.view.exists(keylet::account(ctx.tx.getAccountID(sfAccount))))
51+
return terNO_ACCOUNT;
52+
53+
if (auto const sle = ctx.view.read(keylet::oracle(
54+
ctx.tx.getAccountID(sfAccount), ctx.tx[sfOracleDocumentID]));
55+
!sle)
56+
{
57+
JLOG(ctx.j.debug()) << "Oracle Delete: Oracle does not exist.";
58+
return tecNO_ENTRY;
59+
}
60+
else if (ctx.tx.getAccountID(sfAccount) != sle->getAccountID(sfOwner))
61+
{
62+
// this can't happen because of the above check
63+
JLOG(ctx.j.debug()) << "Oracle Delete: invalid account.";
64+
return tecINTERNAL;
65+
}
66+
return tesSUCCESS;
67+
}
68+
69+
TER
70+
DeleteOracle::deleteOracle(
71+
ApplyView& view,
72+
std::shared_ptr<SLE> const& sle,
73+
AccountID const& account,
74+
beast::Journal j)
75+
{
76+
if (!sle)
77+
return tesSUCCESS;
78+
79+
if (!view.dirRemove(
80+
keylet::ownerDir(account), (*sle)[sfOwnerNode], sle->key(), true))
81+
{
82+
JLOG(j.fatal()) << "Unable to delete Oracle from owner.";
83+
return tefBAD_LEDGER;
84+
}
85+
86+
auto const sleOwner = view.peek(keylet::account(account));
87+
if (!sleOwner)
88+
return tecINTERNAL;
89+
90+
auto const count =
91+
sle->getFieldArray(sfPriceDataSeries).size() > 5 ? -2 : -1;
92+
93+
adjustOwnerCount(view, sleOwner, count, j);
94+
95+
view.erase(sle);
96+
97+
return tesSUCCESS;
98+
}
99+
100+
TER
101+
DeleteOracle::doApply()
102+
{
103+
if (auto sle = ctx_.view().peek(
104+
keylet::oracle(account_, ctx_.tx[sfOracleDocumentID])))
105+
return deleteOracle(ctx_.view(), sle, account_, j_);
106+
107+
return tecINTERNAL;
108+
}
109+
110+
} // namespace ripple

src/ripple/app/tx/impl/DeleteOracle.h

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
//------------------------------------------------------------------------------
2+
/*
3+
This file is part of rippled: https://github.com/ripple/rippled
4+
Copyright (c) 2023 Ripple Labs Inc.
5+
6+
Permission to use, copy, modify, and/or distribute this software for any
7+
purpose with or without fee is hereby granted, provided that the above
8+
copyright notice and this permission notice appear in all copies.
9+
10+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13+
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17+
*/
18+
//==============================================================================
19+
20+
#ifndef RIPPLE_TX_DELETEORACLE_H_INCLUDED
21+
#define RIPPLE_TX_DELETEORACLE_H_INCLUDED
22+
23+
#include <ripple/app/tx/impl/Transactor.h>
24+
25+
namespace ripple {
26+
27+
/**
28+
Price Oracle is a system that acts as a bridge between
29+
a blockchain network and the external world, providing off-chain price data
30+
to decentralized applications (dApps) on the blockchain. This implementation
31+
conforms to the requirements specified in the XLS-47d.
32+
33+
The DeleteOracle transactor implements the deletion of Oracle objects.
34+
*/
35+
36+
class DeleteOracle : public Transactor
37+
{
38+
public:
39+
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
40+
41+
explicit DeleteOracle(ApplyContext& ctx) : Transactor(ctx)
42+
{
43+
}
44+
45+
static NotTEC
46+
preflight(PreflightContext const& ctx);
47+
48+
static TER
49+
preclaim(PreclaimContext const& ctx);
50+
51+
TER
52+
doApply() override;
53+
54+
static TER
55+
deleteOracle(
56+
ApplyView& view,
57+
std::shared_ptr<SLE> const& sle,
58+
AccountID const& account,
59+
beast::Journal j);
60+
};
61+
62+
} // namespace ripple
63+
64+
#endif // RIPPLE_TX_DELETEORACLE_H_INCLUDED

src/ripple/app/tx/impl/InvariantCheck.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,7 @@ LedgerEntryTypesMatch::visitEntry(
392392
case ltXCHAIN_OWNED_CLAIM_ID:
393393
case ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID:
394394
case ltDID:
395+
case ltORACLE:
395396
break;
396397
default:
397398
invalidTypeAdded_ = true;

0 commit comments

Comments
 (0)