add Sponsor Oracle

This commit is contained in:
tequ
2025-09-19 21:06:34 +09:00
parent 89af81745b
commit 453fd23512
4 changed files with 314 additions and 12 deletions

View File

@@ -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();

View File

@@ -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>

View File

@@ -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);
}

View File

@@ -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);
}