address comments (#7768)

This commit is contained in:
yinyiqian1
2026-07-09 13:07:34 -04:00
committed by GitHub
parent c74d394e8f
commit e0c9cf8b97
11 changed files with 170 additions and 180 deletions

View File

@@ -5,11 +5,11 @@
namespace xrpl {
constexpr uint32_t kMinOracleReserveCount = 1;
constexpr uint32_t kMaxOracleReserveCount = 2;
constexpr std::uint32_t kMinOracleReserveCount = 1;
constexpr std::uint32_t kMaxOracleReserveCount = 2;
constexpr std::size_t kOracleReserveCountThreshold = 5;
inline uint32_t
inline std::uint32_t
calculateOracleReserve(std::size_t priceDataSeriesCount)
{
return priceDataSeriesCount > kOracleReserveCountThreshold ? kMaxOracleReserveCount

View File

@@ -1,12 +1,8 @@
#pragma once
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/OracleHelpers.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STTx.h>
@@ -64,7 +60,7 @@ isReserveSponsored(STTx const& tx)
}
std::optional<AccountID>
getTxReserveSponsorAccountID(STTx const& tx);
getTxReserveSponsorID(STTx const& tx);
std::expected<SLE::pointer, TER>
getTxReserveSponsor(ApplyViewContext ctx);
@@ -89,7 +85,7 @@ getTxReserveSponsor(ReadView const& view, STTx const& tx);
getEffectiveTxReserveSponsor(ApplyViewContext ctx, SLE::const_ref accountSle);
std::optional<AccountID>
getLedgerEntryReserveSponsorAccountID(SLE::const_ref sle, SF_ACCOUNT const& field = sfSponsor);
getLedgerEntryReserveSponsorID(SLE::const_ref sle, SF_ACCOUNT const& field = sfSponsor);
SLE::pointer
getLedgerEntryReserveSponsor(
@@ -123,130 +119,16 @@ addSponsorToLedgerEntry(ApplyViewContext ctx, SLE::ref sle, SF_ACCOUNT const& fi
void
removeSponsorFromLedgerEntry(SLE::ref sle, SF_ACCOUNT const& field = sfSponsor);
template <typename T>
inline std::optional<AccountID>
getLedgerEntryOwner(ReadView const& view, T const& sle, AccountID const& account)
{
switch (sle->getType())
{
case ltCHECK:
case ltESCROW:
case ltPAYCHAN:
case ltMPTOKEN:
case ltDELEGATE:
case ltDEPOSIT_PREAUTH:
return sle->getAccountID(sfAccount);
case ltMPTOKEN_ISSUANCE:
return sle->getAccountID(sfIssuer);
case ltSIGNER_LIST: {
auto const signerList = view.read(keylet::signerList(account));
if (!signerList)
return std::nullopt;
if (signerList->key() == sle->key())
return account;
return std::nullopt;
}
case ltCREDENTIAL: {
if (sle->isFlag(lsfAccepted))
return sle->getAccountID(sfSubject);
return sle->getAccountID(sfIssuer);
}
case ltRIPPLE_STATE: {
if (sle->isFlag(lsfHighReserve))
{
auto const highAccount = sle->getFieldAmount(sfHighLimit).getIssuer();
if (highAccount == account)
return highAccount;
}
if (sle->isFlag(lsfLowReserve))
{
auto const lowAccount = sle->getFieldAmount(sfLowLimit).getIssuer();
if (lowAccount == account)
return lowAccount;
}
return std::nullopt;
}
default:
UNREACHABLE("Object is not supported by sponsorship.");
return std::nullopt;
};
}
std::optional<AccountID>
getLedgerEntryOwner(ReadView const& view, SLE const& sle, AccountID const& account);
template <typename T>
inline bool
isLedgerEntrySupportedBySponsorship(T const& sle)
{
switch (sle->getType())
{
case ltCHECK:
case ltESCROW:
case ltPAYCHAN:
case ltMPTOKEN:
case ltDELEGATE:
case ltDEPOSIT_PREAUTH:
case ltMPTOKEN_ISSUANCE:
case ltSIGNER_LIST:
case ltCREDENTIAL:
case ltRIPPLE_STATE:
return true;
default:
return false;
};
}
bool
isLedgerEntrySupportedBySponsorship(SLE const& sle);
template <typename T>
inline std::uint32_t
getLedgerEntryOwnerCount(T const& sle)
{
switch (sle->getType())
{
case ltORACLE: {
return calculateOracleReserve(sle->getFieldArray(sfPriceDataSeries).size());
}
// Vaults require 2 owner counts (the vault and a pseudo-account)
case ltVAULT:
return 2;
case ltSIGNER_LIST: {
// Mirror SignerListSet's owner-count accounting so that create and
// delete agree. Modern lists (post-MultiSignReserve) carry the
// lsfOneOwnerCount flag and cost a single owner count. Legacy
// pre-MultiSignReserve lists cost 2 + signer_count owner counts
if (sle->isFlag(lsfOneOwnerCount))
return 1;
return 2 + static_cast<std::uint32_t>(sle->getFieldArray(sfSignerEntries).size());
}
default:
return 1;
}
};
std::uint32_t
getLedgerEntryOwnerCount(SLE const& sle);
template <typename T>
inline SF_ACCOUNT const&
getLedgerEntrySponsorField(T const& sle, AccountID const& owner)
{
switch (sle->getType())
{
case ltRIPPLE_STATE: {
if (sle->isFlag(lsfHighReserve))
{
auto const highAccount = sle->getFieldAmount(sfHighLimit).getIssuer();
if (highAccount == owner)
return sfHighSponsor;
}
if (sle->isFlag(lsfLowReserve))
{
auto const lowAccount = sle->getFieldAmount(sfLowLimit).getIssuer();
if (lowAccount == owner)
return sfLowSponsor;
}
// LCOV_EXCL_START
UNREACHABLE("Should not happen. Owner should be checked before calling this function.");
return sfSponsor;
// LCOV_EXCL_STOP
}
default:
return sfSponsor;
}
};
SF_ACCOUNT const&
getLedgerEntrySponsorField(SLE const& sle, AccountID const& owner);
} // namespace xrpl

View File

@@ -8,7 +8,6 @@
#include <xrpl/protocol/XRPAmount.h>
#include <cstdint>
#include <memory>
namespace xrpl {
@@ -29,7 +28,7 @@ class SponsorshipOwnerCountsMatch
public:
void
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
visitEntry(bool, SLE::const_ref, SLE::const_ref);
[[nodiscard]] bool
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const;
@@ -50,7 +49,7 @@ class SponsorshipAccountCountMatchesField
public:
void
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
visitEntry(bool, SLE::const_ref, SLE::const_ref);
[[nodiscard]] bool
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const;

View File

@@ -369,6 +369,9 @@ checkReserve(
return tefINTERNAL; // LCOV_EXCL_LINE
if (sponsorSle)
{
if (sponsorSle->getType() != ltACCOUNT_ROOT)
return tefINTERNAL; // LCOV_EXCL_LINE
auto const sle = ctx.view.read(
keylet::sponsorship(
sponsorSle->getAccountID(sfAccount), accSle->getAccountID(sfAccount)));

View File

@@ -142,7 +142,7 @@ addEmptyHolding(
if (accountID == mptIssue.getIssuer())
return tesSUCCESS;
return authorizeMPToken(ctx, priorBalance, mptID, accountID, journal, 0, std::nullopt);
return authorizeMPToken(ctx, priorBalance, mptID, accountID, journal);
}
[[nodiscard]] TER

View File

@@ -4,21 +4,24 @@
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/OracleHelpers.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STArray.h>
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/TER.h>
#include <cstdint>
#include <expected>
#include <optional>
namespace xrpl {
std::optional<AccountID>
getTxReserveSponsorAccountID(STTx const& tx)
getTxReserveSponsorID(STTx const& tx)
{
if (tx.isFieldPresent(sfSponsor) && isReserveSponsored(tx))
{
@@ -30,7 +33,7 @@ getTxReserveSponsorAccountID(STTx const& tx)
std::expected<SLE::pointer, TER>
getTxReserveSponsor(ApplyViewContext ctx)
{
auto const sponsorID = getTxReserveSponsorAccountID(ctx.tx);
auto const sponsorID = getTxReserveSponsorID(ctx.tx);
if (sponsorID)
{
auto sle = ctx.view.peek(keylet::account(*sponsorID));
@@ -46,7 +49,7 @@ getTxReserveSponsor(ApplyViewContext ctx)
std::expected<SLE::const_pointer, TER>
getTxReserveSponsor(ReadView const& view, STTx const& tx)
{
auto const sponsorID = getTxReserveSponsorAccountID(tx);
auto const sponsorID = getTxReserveSponsorID(tx);
if (sponsorID)
{
auto sle = view.read(keylet::account(*sponsorID));
@@ -69,7 +72,7 @@ getEffectiveTxReserveSponsor(ApplyViewContext ctx, SLE::const_ref accountSle)
}
std::optional<AccountID>
getLedgerEntryReserveSponsorAccountID(SLE::const_ref sle, SF_ACCOUNT const& field)
getLedgerEntryReserveSponsorID(SLE::const_ref sle, SF_ACCOUNT const& field)
{
if (sle->isFieldPresent(field))
return sle->getAccountID(field);
@@ -79,7 +82,7 @@ getLedgerEntryReserveSponsorAccountID(SLE::const_ref sle, SF_ACCOUNT const& fiel
SLE::pointer
getLedgerEntryReserveSponsor(ApplyView& view, SLE::const_ref sle, SF_ACCOUNT const& field)
{
auto const sponsorID = getLedgerEntryReserveSponsorAccountID(sle, field);
auto const sponsorID = getLedgerEntryReserveSponsorID(sle, field);
if (sponsorID)
return view.peek(keylet::account(*sponsorID));
return {};
@@ -88,7 +91,7 @@ getLedgerEntryReserveSponsor(ApplyView& view, SLE::const_ref sle, SF_ACCOUNT con
SLE::const_pointer
getLedgerEntryReserveSponsor(ReadView const& view, SLE::const_ref sle, SF_ACCOUNT const& field)
{
auto const sponsorID = getLedgerEntryReserveSponsorAccountID(sle, field);
auto const sponsorID = getLedgerEntryReserveSponsorID(sle, field);
if (sponsorID)
return view.read(keylet::account(*sponsorID));
return {};
@@ -127,4 +130,126 @@ removeSponsorFromLedgerEntry(SLE::ref sle, SF_ACCOUNT const& field)
sle->makeFieldAbsent(field);
}
std::optional<AccountID>
getLedgerEntryOwner(ReadView const& view, SLE const& sle, AccountID const& account)
{
switch (sle.getType())
{
case ltCHECK:
case ltESCROW:
case ltPAYCHAN:
case ltMPTOKEN:
case ltDELEGATE:
case ltDEPOSIT_PREAUTH:
return sle.getAccountID(sfAccount);
case ltMPTOKEN_ISSUANCE:
return sle.getAccountID(sfIssuer);
case ltSIGNER_LIST: {
auto const signerList = view.read(keylet::signerList(account));
if (!signerList)
return std::nullopt;
if (signerList->key() == sle.key())
return account;
return std::nullopt;
}
case ltCREDENTIAL: {
if (sle.isFlag(lsfAccepted))
return sle.getAccountID(sfSubject);
return sle.getAccountID(sfIssuer);
}
case ltRIPPLE_STATE: {
if (sle.isFlag(lsfHighReserve))
{
auto const highAccount = sle.getFieldAmount(sfHighLimit).getIssuer();
if (highAccount == account)
return highAccount;
}
if (sle.isFlag(lsfLowReserve))
{
auto const lowAccount = sle.getFieldAmount(sfLowLimit).getIssuer();
if (lowAccount == account)
return lowAccount;
}
return std::nullopt;
}
default:
UNREACHABLE("xrpl::getLedgerEntryOwner : object is not supported by sponsorship.");
return std::nullopt;
};
}
bool
isLedgerEntrySupportedBySponsorship(SLE const& sle)
{
switch (sle.getType())
{
case ltCHECK:
case ltESCROW:
case ltPAYCHAN:
case ltMPTOKEN:
case ltDELEGATE:
case ltDEPOSIT_PREAUTH:
case ltMPTOKEN_ISSUANCE:
case ltSIGNER_LIST:
case ltCREDENTIAL:
case ltRIPPLE_STATE:
return true;
default:
return false;
};
}
std::uint32_t
getLedgerEntryOwnerCount(SLE const& sle)
{
switch (sle.getType())
{
case ltORACLE: {
return calculateOracleReserve(sle.getFieldArray(sfPriceDataSeries).size());
}
// Vaults require 2 owner counts (the vault and a pseudo-account)
case ltVAULT:
return 2;
case ltSIGNER_LIST: {
// Mirror SignerListSet's owner-count accounting so that create and
// delete agree. Modern lists (post-MultiSignReserve) carry the
// lsfOneOwnerCount flag and cost a single owner count. Legacy
// pre-MultiSignReserve lists cost 2 + signer_count owner counts
if (sle.isFlag(lsfOneOwnerCount))
return 1;
return 2 + static_cast<std::uint32_t>(sle.getFieldArray(sfSignerEntries).size());
}
default:
return 1;
}
}
SF_ACCOUNT const&
getLedgerEntrySponsorField(SLE const& sle, AccountID const& owner)
{
switch (sle.getType())
{
case ltRIPPLE_STATE: {
if (sle.isFlag(lsfHighReserve))
{
auto const highAccount = sle.getFieldAmount(sfHighLimit).getIssuer();
if (highAccount == owner)
return sfHighSponsor;
}
if (sle.isFlag(lsfLowReserve))
{
auto const lowAccount = sle.getFieldAmount(sfLowLimit).getIssuer();
if (lowAccount == owner)
return sfLowSponsor;
}
// LCOV_EXCL_START
UNREACHABLE("xrpl::getLedgerEntrySponsorField : unknown owner for RippleState");
return sfSponsor;
// LCOV_EXCL_STOP
}
default:
return sfSponsor;
}
}
} // namespace xrpl

View File

@@ -13,36 +13,31 @@
#include <xrpl/protocol/XRPAmount.h>
#include <cstdint>
#include <memory>
namespace xrpl {
// Add new sponsorship-related invariants implementations
void
SponsorshipOwnerCountsMatch::visitEntry(
bool isDelete,
std::shared_ptr<SLE const> const& before,
std::shared_ptr<SLE const> const& after)
SponsorshipOwnerCountsMatch::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after)
{
auto getSponsored = [](std::shared_ptr<SLE const> const& sle) -> std::uint32_t {
auto getSponsored = [](SLE::const_ref sle) -> std::uint32_t {
if (sle && sle->getType() == ltACCOUNT_ROOT)
return sle->getFieldU32(sfSponsoredOwnerCount);
return 0;
};
auto getSponsoring = [](std::shared_ptr<SLE const> const& sle) -> std::uint32_t {
auto getSponsoring = [](SLE::const_ref sle) -> std::uint32_t {
if (sle && sle->getType() == ltACCOUNT_ROOT)
return sle->getFieldU32(sfSponsoringOwnerCount);
return 0;
};
auto getOwnerCount = [](std::shared_ptr<SLE const> const& sle) -> std::uint32_t {
auto getOwnerCount = [](SLE::const_ref sle) -> std::uint32_t {
if (sle && sle->getType() == ltACCOUNT_ROOT)
return sle->getFieldU32(sfOwnerCount);
return 0;
};
auto getSponsoredObjectOwnerCount =
[&](std::shared_ptr<SLE const> const& sle) -> std::uint32_t {
auto getSponsoredObjectOwnerCount = [&](SLE::const_ref sle) -> std::uint32_t {
if (!sle)
return 0;
switch (sle->getType())
@@ -138,18 +133,15 @@ SponsorshipOwnerCountsMatch::finalize(
}
void
SponsorshipAccountCountMatchesField::visitEntry(
bool,
std::shared_ptr<SLE const> const& before,
std::shared_ptr<SLE const> const& after)
SponsorshipAccountCountMatchesField::visitEntry(bool, SLE::const_ref before, SLE::const_ref after)
{
auto getSponsoringAccountCount = [](std::shared_ptr<SLE const> const& sle) -> std::uint32_t {
auto getSponsoringAccountCount = [](SLE::const_ref sle) -> std::uint32_t {
if (sle && sle->getType() == ltACCOUNT_ROOT)
return sle->getFieldU32(sfSponsoringAccountCount);
return 0;
};
auto hasSponsorField = [](std::shared_ptr<SLE const> const& sle) -> bool {
auto hasSponsorField = [](SLE::const_ref sle) -> bool {
return sle && sle->getType() == ltACCOUNT_ROOT && sle->isFieldPresent(sfSponsor);
};

View File

@@ -460,10 +460,7 @@ EscrowCreate::doApply()
// source only owes reserve for its current owners.
// - unsponsored: 1 — source owes reserve including the new increment.
auto const sourceReserve = accountReserve(
ctx_.view(),
sle,
j_,
{.ownerCountDelta = getTxReserveSponsorAccountID(ctx_.tx) ? 0 : 1});
ctx_.view(), sle, j_, {.ownerCountDelta = getTxReserveSponsorID(ctx_.tx) ? 0 : 1});
if (balance - STAmount(amount).xrp() < sourceReserve)
return tecUNFUNDED;
}

View File

@@ -158,10 +158,7 @@ PaymentChannelCreate::doApply()
// the source only owes reserve for its current owners.
// - unsponsored: 1 — source owes reserve including the new increment.
auto const sourceReserve = accountReserve(
ctx_.view(),
sle,
j_,
{.ownerCountDelta = getTxReserveSponsorAccountID(ctx_.tx) ? 0 : 1});
ctx_.view(), sle, j_, {.ownerCountDelta = getTxReserveSponsorID(ctx_.tx) ? 0 : 1});
if (preFeeBalance_ - ctx_.tx[sfAmount].xrp() < sourceReserve)
return tecUNFUNDED;
}

View File

@@ -370,10 +370,7 @@ SponsorshipSet::doApply()
}
void
SponsorshipSet::visitInvariantEntry(
bool,
std::shared_ptr<SLE const> const&,
std::shared_ptr<SLE const> const&)
SponsorshipSet::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref)
{
}

View File

@@ -195,7 +195,7 @@ SponsorshipTransfer::preflight(PreflightContext const& ctx)
// sponsorship may use pre-funded reserve sponsorship instead.
bool const isCreateOrReassign =
ctx.tx.isFlag(tfSponsorshipCreate) || ctx.tx.isFlag(tfSponsorshipReassign);
auto const reserveSponsor = getTxReserveSponsorAccountID(ctx.tx);
auto const reserveSponsor = getTxReserveSponsorID(ctx.tx);
bool const isAccountReserveSponsorship =
isCreateOrReassign && reserveSponsor && !ctx.tx.isFieldPresent(sfObjectID);
@@ -244,10 +244,10 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx)
if (!objectSle)
return tecNO_ENTRY;
if (!isLedgerEntrySupportedBySponsorship(objectSle))
if (!isLedgerEntrySupportedBySponsorship(*objectSle))
return tecNO_PERMISSION;
auto const owner = getLedgerEntryOwner(ctx.view, objectSle, sponseeID);
auto const owner = getLedgerEntryOwner(ctx.view, *objectSle, sponseeID);
if (!owner.has_value() || owner.value() != sponseeID)
return tecNO_PERMISSION;
@@ -255,7 +255,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx)
// depends on the object type, a RippleState stores the sponsor in
// sfHighSponsor/sfLowSponsor, while other object type uses sfSponsor.
targetSle = objectSle;
sponsorField = &getLedgerEntrySponsorField(objectSle, owner.value());
sponsorField = &getLedgerEntrySponsorField(*objectSle, owner.value());
}
bool const isSponsored = targetSle->isFieldPresent(*sponsorField);
@@ -315,7 +315,7 @@ SponsorshipTransfer::doApply()
if (!objectSle)
return tefINTERNAL; // LCOV_EXCL_LINE
auto const ownerID = getLedgerEntryOwner(view(), objectSle, sponseeID);
auto const ownerID = getLedgerEntryOwner(view(), *objectSle, sponseeID);
if (!ownerID)
return tefINTERNAL; // LCOV_EXCL_LINE
@@ -323,8 +323,9 @@ SponsorshipTransfer::doApply()
if (!ownerSle)
return tefINTERNAL; // LCOV_EXCL_LINE
auto const ownerCountDelta = static_cast<std::int32_t>(getLedgerEntryOwnerCount(objectSle));
auto const& sponsorField = getLedgerEntrySponsorField(objectSle, *ownerID);
auto const ownerCountDelta =
static_cast<std::int32_t>(getLedgerEntryOwnerCount(*objectSle));
auto const& sponsorField = getLedgerEntrySponsorField(*objectSle, *ownerID);
if (isCreate || isReassign)
{
@@ -526,10 +527,7 @@ SponsorshipTransfer::doApply()
}
void
SponsorshipTransfer::visitInvariantEntry(
bool,
std::shared_ptr<SLE const> const&,
std::shared_ptr<SLE const> const&)
SponsorshipTransfer::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref)
{
}