Compare commits

...

7 Commits

Author SHA1 Message Date
Mayukha Vadari
1d8f73794f refactor: Move valid's per-credential check onto CredentialEntry
Move the subject-match and accepted-flag checks out of
credentials::valid() and onto CredentialEntry as a const member
isValid(AccountID const&). valid() keeps iterating the transaction's
sfCredentialIDs array and looking up each credential, since that part
needs the raw credential ID for its own diagnostics, but now delegates
the per-credential check to the wrapper.
2026-10-05 14:40:28 +01:00
Mayukha Vadari
0872ef04f3 Include Keylet.h in the SLEBase test
(cherry picked from commit 3cca9d3266)
2026-10-05 14:40:25 +01:00
Mayukha Vadari
39390ad40e test: Drop extern template declarations from SLEBase test
An explicit instantiation declaration stops clang from emitting the
inline constructors of the header-only entries at -O0, so the debug
builds fail to link xrpl_tests (undefined AccountRootEntry constructors).
Naming each class in a static_assert already instantiates its definition,
which is all the exhaustiveness check needs.

(cherry picked from commit 611415e1f9)
2026-10-05 14:40:25 +01:00
Mayukha Vadari
66a4f59c9e refactor: Move deleteSLE onto CredentialEntry
Move the credentials::deleteSLE free function onto CredentialEntry as
a writable member removeFromLedger(); the owner and subject are read
from the SLE's own fields instead of being passed in. Body goes to a
new src/libxrpl/ledger/entries/CredentialEntry.cpp (explicit
instantiation for ReadView/ApplyView). Updates all call sites
(CredentialHelpers.cpp, Transactor.cpp's removeExpiredCredentials,
AccountDelete.cpp's removeCredentialFromLedger adapter,
CredentialDelete/CredentialAccept transactors, and the Credentials_test
null-SLE gtest).
2026-10-05 13:49:32 +01:00
Mayukha Vadari
63ffb130ec test: Declare, not define, entry instantiations in SLEBase test
The exhaustive per-entry-type check in the SLEBase gtest used explicit
instantiation definitions. Once an entry class gets out-of-line member
bodies in its own .cpp (with the explicit instantiation definition
there), the test would instantiate the class a second time, and MSVC
rejects it with C4661 because those member bodies are not visible in
the test translation unit. Explicit instantiation declarations still
instantiate the class definition, which is all the check needs.

(cherry picked from commit 8b07abc3cd)
2026-10-05 13:49:31 +01:00
Mayukha Vadari
000596c262 refactor: Move checkExpired onto CredentialEntry
Move the credentials::checkExpired free function onto CredentialEntry
as a const member isExpired(), part of the Wrapped SLEs migration.
Updates all call sites (CredentialHelpers, DepositAuthorized RPC
handler, PermissionedDEXHelpers, CredentialDelete/Accept transactors,
and the Credentials_test gtest) to call the member instead.
2026-10-05 13:45:03 +01:00
Mayukha Vadari
b1a1dde2a6 refactor: Use CredentialEntry everywhere
Replace raw Credential SLE pointers with CredentialEntryR and
CredentialEntryW in the ledger helpers and transactors.
credentials::checkExpired now takes CredentialEntryR const& and
credentials::deleteSLE takes CredentialEntryW&; both stay free
functions. The RPC handler and Credentials_test get the minimal edits
that the new signatures force.

Pure refactor: no amendment and no behavior change.
2026-10-05 13:39:52 +01:00
13 changed files with 188 additions and 136 deletions

View File

@@ -1,7 +1,9 @@
#pragma once
#include <xrpl/basics/Log.h>
#include <xrpl/basics/Slice.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/chrono.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
@@ -9,6 +11,11 @@
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/TER.h>
#include <cstdint>
#include <limits>
namespace xrpl {
@@ -39,6 +46,44 @@ public:
: Base(keylet::credential(credentialID), view, j)
{
}
// Check if the credential's sfExpiration field has passed the given
// ledger close time.
[[nodiscard]] bool
isExpired(NetClock::time_point const& closed) const
{
std::uint32_t const exp =
Base::operator*()[~sfExpiration].value_or(std::numeric_limits<std::uint32_t>::max());
std::uint32_t const now = closed.time_since_epoch().count();
return now > exp;
}
// Remove this credential's SLE from the ledger: delete it from the
// issuer's (and, unless it is the same account, the subject's) owner
// directory, adjust owner counts, and erase the SLE.
[[nodiscard]] TER
removeFromLedger()
requires Base::kIsWritable;
// Check whether this credential belongs to src and is accepted.
// Expiration is checked separately by isExpired().
[[nodiscard]] TER
isValid(AccountID const& src) const
{
if (Base::operator->()->getAccountID(sfSubject) != src)
{
JLOG(this->journal().trace()) << "Credential doesn't belong to the source account.";
return tecBAD_CREDENTIALS;
}
if (!Base::operator->()->isFlag(lsfAccepted))
{
JLOG(this->journal().trace()) << "Credential isn't accepted.";
return tecBAD_CREDENTIALS;
}
return tesSUCCESS;
}
};
using CredentialEntryR = CredentialEntry<ReadView>;

View File

@@ -2,7 +2,6 @@
#include <xrpl/basics/Slice.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/chrono.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
@@ -25,14 +24,6 @@ namespace credentials {
// (and any future pre-authorization modes) as part of authorization (all the
// transfer funds transactions)
// Check if credential sfExpiration field has passed ledger's parentCloseTime
bool
checkExpired(SLE const& sleCredential, NetClock::time_point const& closed);
// Actually remove a credentials object from the ledger
[[nodiscard]] TER
deleteSLE(ApplyView& view, SLE::Ref sleCredential, beast::Journal j);
// Amendment and parameters checks for sfCredentialIDs field
NotTEC
checkFields(STTx const& tx, Rules const& rules, beast::Journal j);

View File

@@ -0,0 +1,77 @@
#include <xrpl/ledger/entries/CredentialEntry.h>
#include <xrpl/basics/Log.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/TER.h>
#include <cstdint>
namespace xrpl {
template <typename ViewT>
TER
CredentialEntry<ViewT>::removeFromLedger()
requires Base::kIsWritable
{
if (!this->exists())
return tecNO_ENTRY;
auto delSLE = [this](AccountID const& account, SField const& node, bool isOwner) -> TER {
auto const sleAccount = this->applyView().peek(keylet::account(account));
if (!sleAccount)
{
// LCOV_EXCL_START
JLOG(this->journal().fatal()) << "Internal error: can't retrieve Owner account.";
return tecINTERNAL;
// LCOV_EXCL_STOP
}
std::uint64_t const page = Base::operator->()->getFieldU64(node);
if (!this->applyView().dirRemove(keylet::ownerDir(account), page, this->key(), false))
{
// LCOV_EXCL_START
JLOG(this->journal().fatal()) << "Unable to delete Credential from owner.";
return tefBAD_LEDGER;
// LCOV_EXCL_STOP
}
if (isOwner)
{
decreaseOwnerCountForObject(
this->applyView(), sleAccount, this->mutableRawSle(), 1, this->journal());
}
return tesSUCCESS;
};
auto const issuer = Base::operator->()->getAccountID(sfIssuer);
auto const subject = Base::operator->()->getAccountID(sfSubject);
bool const accepted = Base::operator->()->isFlag(lsfAccepted);
auto err = delSLE(issuer, sfIssuerNode, !accepted || (subject == issuer));
if (!isTesSuccess(err))
return err;
if (subject != issuer)
{
err = delSLE(subject, sfSubjectNode, accepted);
if (!isTesSuccess(err))
return err;
}
// Remove object from ledger
this->erase();
return tesSUCCESS;
}
template class CredentialEntry<ReadView>;
template class CredentialEntry<ApplyView>;
} // namespace xrpl

View File

@@ -3,11 +3,10 @@
#include <xrpl/basics/Log.h>
#include <xrpl/basics/Slice.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/chrono.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/entries/CredentialEntry.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
@@ -23,9 +22,7 @@
#include <xrpl/protocol/digest.h>
#include <algorithm>
#include <cstdint>
#include <expected>
#include <limits>
#include <set>
#include <unordered_set>
#include <utility>
@@ -34,15 +31,6 @@
namespace xrpl {
namespace credentials {
bool
checkExpired(SLE const& sleCredential, NetClock::time_point const& closed)
{
std::uint32_t const exp =
sleCredential[~sfExpiration].value_or(std::numeric_limits<std::uint32_t>::max());
std::uint32_t const now = closed.time_since_epoch().count();
return now > exp;
}
[[nodiscard]]
static std::expected<bool, TER>
removeExpired(ApplyView& view, STVector256 const& arr, beast::Journal const j)
@@ -56,14 +44,13 @@ removeExpired(ApplyView& view, STVector256 const& arr, beast::Journal const j)
if (view.rules().enabled(fixCleanup3_4_0) && h.isZero())
return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE
auto const k = keylet::credential(h);
auto const sleCred = view.peek(k);
CredentialEntryW sleCred(h, view, j);
if (sleCred && checkExpired(*sleCred, closeTime))
if (sleCred && sleCred.isExpired(closeTime))
{
JLOG(j.trace()) << "Credentials are expired. Cred: " << sleCred->getText();
// delete expired credentials even if the transaction failed
auto const err = deleteSLE(view, sleCred, j);
auto const err = sleCred.removeFromLedger();
if (view.rules().enabled(fixCleanup3_1_3) && !isTesSuccess(err))
return std::unexpected(err);
foundExpired = true;
@@ -73,60 +60,6 @@ removeExpired(ApplyView& view, STVector256 const& arr, beast::Journal const j)
return foundExpired;
}
TER
deleteSLE(ApplyView& view, SLE::Ref sleCredential, beast::Journal j)
{
if (!sleCredential)
return tecNO_ENTRY;
auto delSLE = [&view, &sleCredential, j](
AccountID const& account, SField const& node, bool isOwner) -> TER {
auto const sleAccount = view.peek(keylet::account(account));
if (!sleAccount)
{
// LCOV_EXCL_START
JLOG(j.fatal()) << "Internal error: can't retrieve Owner account.";
return tecINTERNAL;
// LCOV_EXCL_STOP
}
// Remove object from owner directory
std::uint64_t const page = sleCredential->getFieldU64(node);
if (!view.dirRemove(keylet::ownerDir(account), page, sleCredential->key(), false))
{
// LCOV_EXCL_START
JLOG(j.fatal()) << "Unable to delete Credential from owner.";
return tefBAD_LEDGER;
// LCOV_EXCL_STOP
}
if (isOwner)
decreaseOwnerCountForObject(view, sleAccount, sleCredential, 1, j);
return tesSUCCESS;
};
auto const issuer = sleCredential->getAccountID(sfIssuer);
auto const subject = sleCredential->getAccountID(sfSubject);
bool const accepted = sleCredential->isFlag(lsfAccepted);
auto err = delSLE(issuer, sfIssuerNode, !accepted || (subject == issuer));
if (!isTesSuccess(err))
return err;
if (subject != issuer)
{
err = delSLE(subject, sfSubjectNode, accepted);
if (!isTesSuccess(err))
return err;
}
// Remove object from ledger
view.erase(sleCredential);
return tesSUCCESS;
}
NotTEC
checkFields(STTx const& tx, Rules const& rules, beast::Journal j)
{
@@ -179,24 +112,15 @@ valid(STTx const& tx, ReadView const& view, AccountID const& src, beast::Journal
// LCOV_EXCL_STOP
}
auto const sleCred = view.read(keylet::credential(h));
CredentialEntryR const sleCred(h, view, j);
if (!sleCred)
{
JLOG(j.trace()) << "Credential doesn't exist. Cred: " << h;
return tecBAD_CREDENTIALS;
}
if (sleCred->getAccountID(sfSubject) != src)
{
JLOG(j.trace()) << "Credential doesn't belong to the source account. Cred: " << h;
return tecBAD_CREDENTIALS;
}
if (!sleCred->isFlag(lsfAccepted))
{
JLOG(j.trace()) << "Credential isn't accepted. Cred: " << h;
return tecBAD_CREDENTIALS;
}
if (auto const err = sleCred.isValid(src); !isTesSuccess(err))
return err;
// Expiration checks are in doApply
}
@@ -218,8 +142,7 @@ validDomain(ReadView const& view, UInt256 domainID, AccountID const& subject)
{
auto const issuer = h.getAccountID(sfIssuer);
auto const type = h.getFieldVL(sfCredentialType);
auto const keyletCredential = keylet::credential(subject, issuer, makeSlice(type));
auto const sleCredential = view.read(keyletCredential);
CredentialEntryR const sleCredential(subject, issuer, makeSlice(type), view);
// We cannot delete expired credentials, that would require ApplyView&
// However we can check if credentials are expired. Expected transaction
@@ -228,7 +151,7 @@ validDomain(ReadView const& view, UInt256 domainID, AccountID const& subject)
// allows expired credentials to be deleted by any transaction.
if (sleCredential)
{
if (checkExpired(*sleCredential, closeTime))
if (sleCredential.isExpired(closeTime))
{
foundExpired = true;
continue;
@@ -249,14 +172,14 @@ TER
authorizedDepositPreauth(ReadView const& view, STVector256 const& credIDs, AccountID const& dst)
{
std::set<std::pair<AccountID, Slice>> sorted;
std::vector<SLE::const_pointer> lifeExtender;
std::vector<CredentialEntryR> lifeExtender;
lifeExtender.reserve(credIDs.size());
for (auto const& h : credIDs)
{
if (view.rules().enabled(fixCleanup3_4_0) && h.isZero())
return tefINTERNAL; // LCOV_EXCL_LINE
auto sleCred = view.read(keylet::credential(h));
CredentialEntryR sleCred(h, view);
if (!sleCred) // already checked in preclaim
return tefINTERNAL; // LCOV_EXCL_LINE
@@ -356,7 +279,7 @@ verifyValidDomain(ApplyView& view, AccountID const& account, UInt256 domainID, b
for (auto const& h : credentials)
{
auto sleCredential = view.read(keylet::credential(h));
CredentialEntryR const sleCredential(h, view);
if (!sleCredential)
continue; // expired, i.e. deleted in credentials::removeExpired

View File

@@ -6,12 +6,13 @@
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/entries/CredentialEntry.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STArray.h>
#include <xrpl/protocol/UintTypes.h>
#include <algorithm>
@@ -42,12 +43,12 @@ accountInDomain(ReadView const& view, AccountID const& account, Domain const& do
auto const& credentials = sleDomain->getFieldArray(sfAcceptedCredentials);
bool const inDomain = std::ranges::any_of(credentials, [&](auto const& credential) {
auto const sleCred = view.read(
keylet::credential(account, credential[sfIssuer], credential[sfCredentialType]));
CredentialEntryR const sleCred(
account, credential[sfIssuer], credential[sfCredentialType], view);
if (!sleCred || !sleCred->isFlag(lsfAccepted))
return false;
return !credentials::checkExpired(*sleCred, view.header().parentCloseTime);
return !sleCred.isExpired(view.header().parentCloseTime);
});
return inDomain;

View File

@@ -11,8 +11,8 @@
#include <xrpl/json/to_string.h> // IWYU pragma: keep
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/CredentialEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/DelegateHelpers.h>
#include <xrpl/ledger/helpers/NFTokenHelpers.h>
#include <xrpl/ledger/helpers/OfferHelpers.h>
@@ -1250,9 +1250,9 @@ removeExpiredCredentials(ApplyView& view, std::vector<UInt256> const& creds, bea
{
for (auto const& index : creds)
{
if (auto const sle = view.peek(keylet::credential(index)))
if (auto sle = CredentialEntryW(index, view, viewJ))
{
if (auto const ter = credentials::deleteSLE(view, sle, viewJ); !isTesSuccess(ter))
if (auto const ter = sle.removeFromLedger(); !isTesSuccess(ter))
{
JLOG(viewJ.error())
<< "removeExpiredCredentials: failed to delete expired credential. Err: "

View File

@@ -9,6 +9,7 @@
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/CredentialEntry.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/NFTokenHelpers.h>
@@ -166,11 +167,12 @@ removeCredentialFromLedger(
ServiceRegistry&,
ApplyView& view,
AccountID const&,
UInt256 const&,
SLE::Ref sleDel,
UInt256 const& delIndex,
SLE::Ref,
beast::Journal j)
{
return credentials::deleteSLE(view, sleDel, j);
CredentialEntryW sleDel(delIndex, view, j);
return sleDel.removeFromLedger();
}
TER

View File

@@ -2,8 +2,8 @@
#include <xrpl/basics/Log.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/entries/CredentialEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/SponsorHelpers.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Feature.h>
@@ -22,8 +22,6 @@
#include <cstdint>
namespace xrpl {
using namespace credentials;
std::uint32_t
CredentialAccept::getFlagsMask(PreflightContext const& ctx)
{
@@ -63,7 +61,7 @@ CredentialAccept::preclaim(PreclaimContext const& ctx)
return tecNO_ISSUER;
}
auto const sleCred = ctx.view.read(keylet::credential(subject, issuer, credType));
CredentialEntryR const sleCred(subject, issuer, credType, ctx.view);
if (!sleCred)
{
JLOG(ctx.j.warn()) << "No credential: " << to_string(subject) << ", " << to_string(issuer)
@@ -108,16 +106,15 @@ CredentialAccept::doApply()
return ret;
auto const credType(ctx_.tx[sfCredentialType]);
Keylet const credentialKey = keylet::credential(accountID_, issuer, credType);
auto const sleCred = view().peek(credentialKey); // Checked in preclaim()
CredentialEntryW sleCred(accountID_, issuer, credType, view(), j_); // Checked in preclaim()
if (!sleCred)
return tefINTERNAL; // LCOV_EXCL_LINE
if (checkExpired(*sleCred, view().header().parentCloseTime))
if (sleCred.isExpired(view().header().parentCloseTime))
{
JLOG(j_.trace()) << "Credential is expired: " << sleCred->getText();
// delete expired credentials even if the transaction failed
auto const err = credentials::deleteSLE(view(), sleCred, j_);
auto const err = sleCred.removeFromLedger();
return isTesSuccess(err) ? tecEXPIRED : err;
}
@@ -126,12 +123,12 @@ CredentialAccept::doApply()
// Release the original creation sponsor from the credential (it covered
// the issuer's reserve), then assign the accept tx's sponsor (if any) so
// the credential reflects whoever is now covering the subject's reserve.
decreaseOwnerCountForObject(view(), sleIssuer, sleCred, 1, j_);
removeSponsorFromLedgerEntry(sleCred);
decreaseOwnerCountForObject(view(), sleIssuer, sleCred.mutableRawSle(), 1, j_);
removeSponsorFromLedgerEntry(sleCred.mutableRawSle());
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), sleCred);
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), sleCred.mutableRawSle());
increaseOwnerCount(ctx_.getApplyViewContext(), sleSubject, 1, j_);
view().update(sleCred);
sleCred.update();
return tesSUCCESS;
}

View File

@@ -4,6 +4,7 @@
#include <xrpl/basics/base_uint.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/entries/CredentialEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h> // IWYU pragma: keep
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
@@ -114,7 +115,8 @@ CredentialCreate::doApply()
auto const credType(ctx_.tx[sfCredentialType]);
Keylet const credentialKey = keylet::credential(subject, accountID_, credType);
auto const sleCred = std::make_shared<SLE>(credentialKey);
CredentialEntryW sleCred(credentialKey, view(), j_);
sleCred.newSLE();
if (!sleCred)
return tefINTERNAL; // LCOV_EXCL_LINE
@@ -160,7 +162,7 @@ CredentialCreate::doApply()
sleCred->setFieldU64(sfIssuerNode, *page);
increaseOwnerCount(ctx_.getApplyViewContext(), sleIssuer, 1, j_);
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), sleCred);
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), sleCred.mutableRawSle());
}
if (subject == accountID_)
@@ -180,7 +182,7 @@ CredentialCreate::doApply()
sleCred->setFieldU64(sfSubjectNode, *page);
}
view().insert(sleCred);
sleCred.insert();
return tesSUCCESS;
}

View File

@@ -2,7 +2,7 @@
#include <xrpl/basics/Log.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/entries/CredentialEntry.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
@@ -18,8 +18,6 @@
#include <cstdint>
namespace xrpl {
using namespace credentials;
std::uint32_t
CredentialDelete::getFlagsMask(PreflightContext const& ctx)
{
@@ -80,18 +78,18 @@ CredentialDelete::doApply()
auto const issuer = ctx_.tx[~sfIssuer].value_or(accountID_);
auto const credType(ctx_.tx[sfCredentialType]);
auto const sleCred = view().peek(keylet::credential(subject, issuer, credType));
CredentialEntryW sleCred(subject, issuer, credType, view(), j_);
if (!sleCred)
return tefINTERNAL; // LCOV_EXCL_LINE
if ((subject != accountID_) && (issuer != accountID_) &&
!checkExpired(*sleCred, ctx_.view().header().parentCloseTime))
!sleCred.isExpired(ctx_.view().header().parentCloseTime))
{
JLOG(j_.trace()) << "Can't delete non-expired credential.";
return tecNO_PERMISSION;
}
return deleteSLE(view(), sleCred, j_);
return sleCred.removeFromLedger();
}
void

View File

@@ -16,11 +16,14 @@
#include <test/jtx/txflags.h>
#include <test/jtx/vault.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/strHex.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/json/to_string.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ApplyViewImpl.h>
#include <xrpl/ledger/entries/CredentialEntry.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Feature.h>
@@ -903,7 +906,8 @@ struct Credentials_test : public beast::unit_test::Suite
testcase("deleteSLE fail, bad SLE.");
auto view =
std::make_shared<ApplyViewImpl>(env.current().get(), ApplyFlags::TapNone);
auto ter = xrpl::credentials::deleteSLE(*view, {}, env.journal);
CredentialEntryW sleCred(UInt256{}, *view);
auto ter = sleCred.removeFromLedger();
BEAST_EXPECT(ter == tecNO_ENTRY);
}
}
@@ -1119,7 +1123,8 @@ struct Credentials_test : public beast::unit_test::Suite
auto const sleCred = env.current()->read(credKeylet);
BEAST_EXPECT(
sleCred &&
xrpl::credentials::checkExpired(*sleCred, env.current()->header().parentCloseTime));
CredentialEntryR(sleCred, *env.current())
.isExpired(env.current()->header().parentCloseTime));
}
// Create an ApplyViewImpl on top of the current closed ledger

View File

@@ -38,6 +38,7 @@
#include <xrpl/ledger/entries/XChainOwnedClaimIDEntry.h> // IWYU pragma: keep
#include <xrpl/ledger/entries/XChainOwnedCreateAccountClaimIDEntry.h> // IWYU pragma: keep
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Keylet.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STLedgerEntry.h>
@@ -76,12 +77,22 @@ template class SLEBase<ApplyView>;
#pragma push_macro("LEDGER_ENTRY")
#undef LEDGER_ENTRY
// No explicit instantiation here, neither a definition nor a declaration.
// A definition would instantiate an entry a second time once its member
// bodies live in its own .cpp (MSVC rejects that with C4661 under /WX), and a
// declaration (`extern template`) stops clang from emitting the inline
// constructors of the header-only entries at -O0, which leaves them undefined
// at link time in the debug builds. Naming the class in a static_assert is
// enough to instantiate its definition, which is all this exhaustiveness
// check needs; the per-type suites exercise the members.
#define LEDGER_ENTRY(tag, value, name, ...) \
template class name##Entry<ReadView>; \
template class name##Entry<ApplyView>; \
static_assert( \
name##Entry<ReadView>::kEntryType == tag && name##Entry<ApplyView>::kEntryType == tag, \
#name "Entry must be bound to " #tag);
#name "Entry must be bound to " #tag); \
static_assert( \
std::is_constructible_v<name##Entry<ReadView>, Keylet const&, ReadView const&> && \
std::is_constructible_v<name##Entry<ApplyView>, Keylet const&, ApplyView&>, \
#name "Entry must be constructible from a Keylet and a view");
#include <xrpl/protocol/detail/ledger_entries.macro>

View File

@@ -6,7 +6,7 @@
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/json/json_value.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/entries/CredentialEntry.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/ErrorCodes.h>
#include <xrpl/protocol/Indexes.h>
@@ -141,7 +141,7 @@ doDepositAuthorized(rpc::JsonContext& context)
return result;
}
if (credentials::checkExpired(*sleCred, ledger->header().parentCloseTime))
if (CredentialEntryR(sleCred, *ledger).isExpired(ledger->header().parentCloseTime))
{
rpc::injectError(RpcBadCredentials, "credentials are expired", result);
return result;