mirror of
https://github.com/XRPLF/rippled.git
synced 2026-06-04 09:16:47 +00:00
add Sponsor Oracle
This commit is contained in:
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <test/jtx.h>
|
||||
|
||||
#include <xrpl/basics/strHex.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
|
||||
namespace ripple {
|
||||
@@ -1861,6 +1862,255 @@ public:
|
||||
void
|
||||
testOracle()
|
||||
{
|
||||
testcase("Oracle");
|
||||
using namespace test::jtx;
|
||||
using namespace std::chrono;
|
||||
using DataSeries = std::vector<
|
||||
std::tuple<std::string, std::string, std::uint32_t, std::uint8_t>>;
|
||||
|
||||
Env env{*this, testable_amendments()};
|
||||
Account const alice("alice");
|
||||
Account const sponsor("sponsor");
|
||||
Account const sponsor2("sponsor2");
|
||||
|
||||
env.fund(XRP(1000000), alice, sponsor, sponsor2);
|
||||
env.close();
|
||||
|
||||
auto const oracleSet =
|
||||
[&env](Account const& account, uint8_t dataSeriesSize) {
|
||||
auto const now = env.timeKeeper().now();
|
||||
env.close(now + oracle::testStartTime - epoch_offset);
|
||||
Json::Value jv;
|
||||
jv[jss::TransactionType] = jss::OracleSet;
|
||||
jv[jss::Account] = to_string(account);
|
||||
jv[jss::OracleDocumentID] = 1;
|
||||
jv[jss::LastUpdateTime] = to_string(
|
||||
duration_cast<seconds>(
|
||||
env.current()->info().closeTime.time_since_epoch())
|
||||
.count() +
|
||||
epoch_offset.count() + 100);
|
||||
jv[jss::PriceDataSeries] = Json::arrayValue;
|
||||
jv[jss::Provider] = strHex(std::string{"provider"});
|
||||
jv[jss::AssetClass] = strHex(std::string{"currency"});
|
||||
|
||||
DataSeries const 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},
|
||||
};
|
||||
|
||||
DataSeries actualSeries(
|
||||
series.begin(), series.begin() + dataSeriesSize);
|
||||
|
||||
Json::Value dataSeries(Json::arrayValue);
|
||||
for (auto const& data : actualSeries)
|
||||
{
|
||||
Json::Value priceData;
|
||||
Json::Value price;
|
||||
price[jss::BaseAsset] = std::get<0>(data);
|
||||
price[jss::QuoteAsset] = std::get<1>(data);
|
||||
price[jss::AssetPrice] = std::get<2>(data);
|
||||
price[jss::Scale] = std::get<3>(data);
|
||||
priceData[jss::PriceData] = price;
|
||||
dataSeries.append(priceData);
|
||||
}
|
||||
jv[jss::PriceDataSeries] = dataSeries;
|
||||
return jv;
|
||||
};
|
||||
|
||||
auto const oracleDelete = [&](Account const& account) {
|
||||
Json::Value jv;
|
||||
jv[jss::TransactionType] = jss::OracleDelete;
|
||||
jv[jss::Account] = to_string(account);
|
||||
jv[jss::OracleDocumentID] = 1;
|
||||
return jv;
|
||||
};
|
||||
|
||||
{
|
||||
// OracleSet (reserve 1)
|
||||
env(oracleSet(alice, 5),
|
||||
sponsor::as(sponsor, tfSponsorReserve),
|
||||
sponsor::sig(sponsor));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 1);
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1);
|
||||
|
||||
// transfer sponsor
|
||||
env(sponsor::transfer(alice, keylet::oracle(alice, 1).key),
|
||||
sponsor::as(sponsor2, tfSponsorReserve),
|
||||
sponsor::sig(sponsor2));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 1);
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1);
|
||||
|
||||
// OracleDelete
|
||||
env(oracleDelete(alice));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 0);
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0);
|
||||
}
|
||||
{
|
||||
// OracleSet (reserve 2)
|
||||
env(oracleSet(alice, 6),
|
||||
sponsor::as(sponsor, tfSponsorReserve),
|
||||
sponsor::sig(sponsor));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 2);
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 2);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 2);
|
||||
|
||||
// transfer sponsor
|
||||
env(sponsor::transfer(alice, keylet::oracle(alice, 1).key),
|
||||
sponsor::as(sponsor2, tfSponsorReserve),
|
||||
sponsor::sig(sponsor2));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 2);
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 2);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 2);
|
||||
|
||||
// OracleDelete
|
||||
env(oracleDelete(alice));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 0);
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0);
|
||||
}
|
||||
{
|
||||
// OracleSet (reserve 1->2, sponsor1 -> no-sponsor)
|
||||
env(oracleSet(alice, 5),
|
||||
sponsor::as(sponsor, tfSponsorReserve),
|
||||
sponsor::sig(sponsor));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 1);
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1);
|
||||
|
||||
// reserve 1->2
|
||||
env(oracleSet(alice, 6));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 2);
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0);
|
||||
|
||||
// OracleDelete
|
||||
env(oracleDelete(alice));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 0);
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0);
|
||||
}
|
||||
{
|
||||
// OracleSet (reserve 1->2, sponsor1 -> sponsor2)
|
||||
env(oracleSet(alice, 5),
|
||||
sponsor::as(sponsor, tfSponsorReserve),
|
||||
sponsor::sig(sponsor));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 1);
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1);
|
||||
|
||||
// reserve 1->2
|
||||
env(oracleSet(alice, 6),
|
||||
sponsor::as(sponsor2, tfSponsorReserve),
|
||||
sponsor::sig(sponsor2));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 2);
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 2);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 2);
|
||||
|
||||
// OracleDelete
|
||||
env(oracleDelete(alice));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 0);
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0);
|
||||
}
|
||||
{
|
||||
// OracleSet (reserve 1->2, non-sponsor -> sponsor1)
|
||||
env(oracleSet(alice, 5));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 1);
|
||||
|
||||
// reserve 1->2
|
||||
env(oracleSet(alice, 6),
|
||||
sponsor::as(sponsor, tfSponsorReserve),
|
||||
sponsor::sig(sponsor));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 2);
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 2);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 2);
|
||||
|
||||
// OracleDelete
|
||||
env(oracleDelete(alice));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 0);
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0);
|
||||
}
|
||||
for (bool isTwoOwnerCount : {false, true})
|
||||
{
|
||||
// test sponsor transfer
|
||||
auto const dataSeriesSize = isTwoOwnerCount ? 6 : 5;
|
||||
auto const ocount = isTwoOwnerCount ? 2 : 1;
|
||||
env(oracleSet(alice, dataSeriesSize),
|
||||
sponsor::as(sponsor, tfSponsorReserve),
|
||||
sponsor::sig(sponsor));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, alice) == ocount);
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == ocount);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == ocount);
|
||||
|
||||
// transfer sponsor
|
||||
env(sponsor::transfer(alice, keylet::oracle(alice, 1).key),
|
||||
sponsor::as(sponsor2, tfSponsorReserve),
|
||||
sponsor::sig(sponsor2));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, alice) == ocount);
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == ocount);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == ocount);
|
||||
|
||||
// disolve sponsor
|
||||
env(sponsor::transfer(alice, keylet::oracle(alice, 1).key));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, alice) == ocount);
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -2189,7 +2439,7 @@ public:
|
||||
testNFTokenOffer();
|
||||
testPayChan();
|
||||
testPermissionedDomain();
|
||||
// testOracle();
|
||||
testOracle();
|
||||
testSignerList();
|
||||
testTrustSet();
|
||||
// testVault();
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <test/jtx/Env.h>
|
||||
#include <test/jtx/Env_ss.h>
|
||||
#include <test/jtx/JTx.h>
|
||||
#include <test/jtx/Oracle.h>
|
||||
#include <test/jtx/TestHelpers.h>
|
||||
#include <test/jtx/account_txn_id.h>
|
||||
#include <test/jtx/acctdelete.h>
|
||||
|
||||
@@ -183,13 +183,15 @@ SetOracle::preclaim(PreclaimContext const& ctx)
|
||||
}
|
||||
|
||||
static bool
|
||||
adjustOwnerCount(ApplyContext& ctx, int count)
|
||||
adjustOwnerCount(
|
||||
ApplyContext& ctx,
|
||||
std::optional<std::shared_ptr<SLE>> const& sponsor,
|
||||
int count)
|
||||
{
|
||||
if (auto const sleAccount =
|
||||
ctx.view().peek(keylet::account(ctx.tx[sfAccount])))
|
||||
{
|
||||
adjustOwnerCount(
|
||||
ctx.view(), sleAccount, std::nullopt, count, ctx.journal);
|
||||
adjustOwnerCount(ctx.view(), sleAccount, sponsor, count, ctx.journal);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -275,8 +277,34 @@ SetOracle::doApply()
|
||||
|
||||
auto const newCount = pairs.size() > 5 ? 2 : 1;
|
||||
auto const adjust = newCount - oldCount;
|
||||
if (adjust != 0 && !adjustOwnerCount(ctx_, adjust))
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
if (adjust > 0)
|
||||
{
|
||||
// To continue receiving sponsorship from the same account after the
|
||||
// OwnerCount increases from 1 to 2, it is necessary to sign with
|
||||
// the sponsor decrease current sponsored owner count.
|
||||
// Otherwise, the sponsorship will be deleted.
|
||||
|
||||
auto const currentsponsor =
|
||||
getLedgerEntryReserveSponsor(ctx_.view(), sle);
|
||||
auto const newSponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx);
|
||||
|
||||
// decrease current sponsored owner count
|
||||
if (!adjustOwnerCount(ctx_, currentsponsor, -oldCount))
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
removeSponsorFromLedgerEntry(sle);
|
||||
// increase new owner count
|
||||
if (!adjustOwnerCount(ctx_, newSponsor, newCount))
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
addSponsorToLedgerEntry(sle, newSponsor);
|
||||
}
|
||||
else if (adjust < 0)
|
||||
{
|
||||
// decrease owner count
|
||||
auto const sponsor = getLedgerEntryReserveSponsor(ctx_.view(), sle);
|
||||
if (!adjustOwnerCount(ctx_, sponsor, adjust))
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
ctx_.view().update(sle);
|
||||
}
|
||||
@@ -321,9 +349,12 @@ SetOracle::doApply()
|
||||
(*sle)[sfOwnerNode] = *page;
|
||||
|
||||
auto const count = series.size() > 5 ? 2 : 1;
|
||||
if (!adjustOwnerCount(ctx_, count))
|
||||
auto const sponsor = getTxReserveSponsor(ctx_.view(), ctx_.tx);
|
||||
if (!adjustOwnerCount(ctx_, sponsor, count))
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
addSponsorToLedgerEntry(sle, sponsor);
|
||||
|
||||
ctx_.view().insert(sle);
|
||||
}
|
||||
|
||||
|
||||
@@ -112,6 +112,20 @@ getLedgerEntryOwner(
|
||||
};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline std::uint32_t
|
||||
getLedgerEntryOwnerCount(T const& sle)
|
||||
{
|
||||
switch (sle->getType())
|
||||
{
|
||||
case ltORACLE: {
|
||||
return sle->getFieldArray(sfPriceDataSeries).size() > 5 ? 2 : 1;
|
||||
}
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
TER
|
||||
SponsorshipTransfer::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
@@ -225,6 +239,8 @@ SponsorshipTransfer::doApply()
|
||||
if (!ownerSle)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const ownerCountDelta = getLedgerEntryOwnerCount(objSle);
|
||||
|
||||
if (tx.isFieldPresent(sfSponsor))
|
||||
{
|
||||
auto const sponsorObj = tx.getFieldObject(sfSponsor);
|
||||
@@ -235,7 +251,8 @@ SponsorshipTransfer::doApply()
|
||||
view().peek(keylet::account(oldSponsor)))
|
||||
{
|
||||
auto const newCount =
|
||||
oldSponsorSle->getFieldU32(sfSponsoringOwnerCount) - 1;
|
||||
oldSponsorSle->getFieldU32(sfSponsoringOwnerCount) -
|
||||
ownerCountDelta;
|
||||
if (newCount == 0)
|
||||
oldSponsorSle->makeFieldAbsent(sfSponsoringOwnerCount);
|
||||
else
|
||||
@@ -249,14 +266,16 @@ SponsorshipTransfer::doApply()
|
||||
// update owner's sponsored count
|
||||
ownerSle->setFieldU32(
|
||||
sfSponsoredOwnerCount,
|
||||
ownerSle->getFieldU32(sfSponsoredOwnerCount) + 1);
|
||||
ownerSle->getFieldU32(sfSponsoredOwnerCount) +
|
||||
ownerCountDelta);
|
||||
view().update(ownerSle);
|
||||
}
|
||||
// increment new sponsoring count
|
||||
auto const newSponsorSle = view().peek(keylet::account(newSponsor));
|
||||
newSponsorSle->setFieldU32(
|
||||
sfSponsoringOwnerCount,
|
||||
newSponsorSle->getFieldU32(sfSponsoringOwnerCount) + 1);
|
||||
newSponsorSle->getFieldU32(sfSponsoringOwnerCount) +
|
||||
ownerCountDelta);
|
||||
view().update(newSponsorSle);
|
||||
|
||||
objSle->setAccountID(sfSponsorAccount, newSponsor);
|
||||
@@ -268,7 +287,7 @@ SponsorshipTransfer::doApply()
|
||||
auto const oldSponsor = objSle->getAccountID(sfSponsorAccount);
|
||||
// decrement sponsored count
|
||||
auto const newCount =
|
||||
accSle->getFieldU32(sfSponsoredOwnerCount) - 1;
|
||||
accSle->getFieldU32(sfSponsoredOwnerCount) - ownerCountDelta;
|
||||
if (newCount == 0)
|
||||
accSle->makeFieldAbsent(sfSponsoredOwnerCount);
|
||||
else
|
||||
@@ -281,7 +300,8 @@ SponsorshipTransfer::doApply()
|
||||
{
|
||||
oldSponsorSle->setFieldU32(
|
||||
sfSponsoringOwnerCount,
|
||||
oldSponsorSle->getFieldU32(sfSponsoringOwnerCount) - 1);
|
||||
oldSponsorSle->getFieldU32(sfSponsoringOwnerCount) -
|
||||
ownerCountDelta);
|
||||
view().update(oldSponsorSle);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user