Compare commits

...

4 Commits

Author SHA1 Message Date
Mayukha Vadari
efe43738fb Include Keylet.h in the SLEBase test
(cherry picked from commit 3cca9d3266)
2026-10-05 14:36:49 +01:00
Mayukha Vadari
70e648aa39 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:36:49 +01:00
Mayukha Vadari
7095c25f47 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:40:17 +01:00
Mayukha Vadari
1c589f96a7 refactor: Use DIDEntry everywhere
Replace raw DID SLE pointers with DIDEntryW in DIDSet, DIDDelete and
AccountDelete, and move the DID helpers onto the wrapper:

- addSLE() in DIDSet.cpp becomes DIDEntry::addToLedger().
- DIDDelete::deleteSLE() (both overloads) becomes
  DIDEntry::removeFromLedger(). AccountDelete's removeDIDFromLedger()
  called the same body, so both callers now use the one member.

The member bodies live in src/libxrpl/ledger/entries/DIDEntry.cpp with
explicit instantiation. No behavior change.
2026-10-05 13:40:17 +01:00
7 changed files with 138 additions and 101 deletions

View File

@@ -7,6 +7,7 @@
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/TER.h>
namespace xrpl {
@@ -27,6 +28,30 @@ public:
: Base(keylet::did(account), view, j)
{
}
/**
* Inserts a new DID into the ledger, links it into the owner's directory
* and increments the owner count.
*
* Call newSLE() and set the DID fields first.
*
* @param owner The account that owns the DID
* @return tesSUCCESS, or tecINSUFFICIENT_RESERVE / tecDIR_FULL on failure
*/
[[nodiscard]] TER
addToLedger(AccountID const& owner)
requires Base::kIsWritable;
/**
* Removes the DID from the owner's directory, decrements the owner count
* and erases the DID from the ledger.
*
* @param owner The account that owns the DID
* @return tesSUCCESS, or tefBAD_LEDGER / tecINTERNAL on failure
*/
[[nodiscard]] TER
removeFromLedger(AccountID const& owner)
requires Base::kIsWritable;
};
using DIDEntryR = DIDEntry<ReadView>;

View File

@@ -2,10 +2,7 @@
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Keylet.h>
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/XRPAmount.h>
@@ -26,12 +23,6 @@ public:
static NotTEC
preflight(PreflightContext const& ctx);
static TER
deleteSLE(ApplyContext& ctx, Keylet sleKeylet, AccountID const owner);
static TER
deleteSLE(ApplyView& view, SLE::pointer sle, AccountID const owner, beast::Journal j);
TER
doApply() override;

View File

@@ -0,0 +1,84 @@
#include <xrpl/ledger/entries/DIDEntry.h>
#include <xrpl/basics/Log.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STAmount.h>
#include <xrpl/protocol/TER.h>
namespace xrpl {
template <typename ViewT>
TER
DIDEntry<ViewT>::addToLedger(AccountID const& owner)
requires Base::kIsWritable
{
auto& view = this->applyView();
auto const j = this->journal();
auto const sleAccount = view.peek(keylet::account(owner));
if (!sleAccount)
return tefINTERNAL; // LCOV_EXCL_LINE
// Check reserve availability for new object creation
{
auto const balance = STAmount((*sleAccount)[sfBalance]).xrp();
auto const reserve = accountReserve(view, sleAccount, j, {.ownerCountDelta = 1});
if (balance < reserve)
return tecINSUFFICIENT_RESERVE;
}
// Add ledger object to ledger
this->insert();
// Add ledger object to owner's page
{
auto page = view.dirInsert(keylet::ownerDir(owner), this->key(), describeOwnerDir(owner));
if (!page)
return tecDIR_FULL; // LCOV_EXCL_LINE
(**this)[sfOwnerNode] = *page;
}
increaseOwnerCount(view, sleAccount, {}, 1, j);
view.update(sleAccount);
return tesSUCCESS;
}
template <typename ViewT>
TER
DIDEntry<ViewT>::removeFromLedger(AccountID const& owner)
requires Base::kIsWritable
{
auto& view = this->applyView();
auto const j = this->journal();
// Remove object from owner directory
if (!view.dirRemove(keylet::ownerDir(owner), (**this)[sfOwnerNode], this->key(), true))
{
// LCOV_EXCL_START
JLOG(j.fatal()) << "Unable to delete DID from owner.";
return tefBAD_LEDGER;
// LCOV_EXCL_STOP
}
auto const sleOwner = view.peek(keylet::account(owner));
if (!sleOwner)
return tecINTERNAL; // LCOV_EXCL_LINE
decreaseOwnerCountForObject(view, sleOwner, this->mutableRawSle(), 1, j);
// Remove object from ledger
this->erase();
return tesSUCCESS;
}
template class DIDEntry<ReadView>;
template class DIDEntry<ApplyView>;
} // namespace xrpl

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/DIDEntry.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/NFTokenHelpers.h>
@@ -27,7 +28,6 @@
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/transactors/account/SignerListSet.h>
#include <xrpl/tx/transactors/delegate/DelegateSet.h>
#include <xrpl/tx/transactors/did/DIDDelete.h>
#include <xrpl/tx/transactors/oracle/OracleDelete.h>
#include <xrpl/tx/transactors/payment/DepositPreauth.h>
@@ -143,10 +143,10 @@ removeDIDFromLedger(
ApplyView& view,
AccountID const& account,
UInt256 const& delIndex,
SLE::Ref sleDel,
SLE::Ref,
beast::Journal j)
{
return DIDDelete::deleteSLE(view, sleDel, account, j);
return DIDEntryW(Keylet(ltDID, delIndex), view, j).removeFromLedger(account);
}
TER

View File

@@ -1,14 +1,8 @@
#include <xrpl/tx/transactors/did/DIDDelete.h>
#include <xrpl/basics/Log.h>
#include <xrpl/core/ServiceRegistry.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/Keylet.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/ledger/entries/DIDEntry.h>
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/TER.h>
@@ -24,43 +18,14 @@ DIDDelete::preflight(PreflightContext const& ctx)
return tesSUCCESS;
}
TER
DIDDelete::deleteSLE(ApplyContext& ctx, Keylet sleKeylet, AccountID const owner)
{
auto const sle = ctx.view().peek(sleKeylet);
if (!sle)
return tecNO_ENTRY;
return DIDDelete::deleteSLE(ctx.view(), sle, owner, ctx.journal);
}
TER
DIDDelete::deleteSLE(ApplyView& view, SLE::pointer sle, AccountID const owner, beast::Journal j)
{
// Remove object from owner directory
if (!view.dirRemove(keylet::ownerDir(owner), (*sle)[sfOwnerNode], sle->key(), true))
{
// LCOV_EXCL_START
JLOG(j.fatal()) << "Unable to delete DID from owner.";
return tefBAD_LEDGER;
// LCOV_EXCL_STOP
}
auto const sleOwner = view.peek(keylet::account(owner));
if (!sleOwner)
return tecINTERNAL; // LCOV_EXCL_LINE
decreaseOwnerCountForObject(view, sleOwner, sle, 1, j);
// Remove object from ledger
view.erase(sle);
return tesSUCCESS;
}
TER
DIDDelete::doApply()
{
return deleteSLE(ctx_, keylet::did(accountID_), accountID_);
DIDEntryW sleDID(accountID_, ctx_.view(), ctx_.journal);
if (!sleDID)
return tecNO_ENTRY;
return sleDID.removeFromLedger(accountID_);
}
void

View File

@@ -3,12 +3,8 @@
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/ledger/entries/DIDEntry.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Keylet.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STLedgerEntry.h>
@@ -19,7 +15,6 @@
#include <xrpl/tx/Transactor.h>
#include <cstddef>
#include <memory>
namespace xrpl {
@@ -62,46 +57,12 @@ DIDSet::preflight(PreflightContext const& ctx)
return tesSUCCESS;
}
static TER
addSLE(ApplyContext& ctx, SLE::Ref sle, AccountID const& owner)
{
auto const sleAccount = ctx.view().peek(keylet::account(owner));
if (!sleAccount)
return tefINTERNAL; // LCOV_EXCL_LINE
// Check reserve availability for new object creation
{
auto const balance = STAmount((*sleAccount)[sfBalance]).xrp();
auto const reserve =
accountReserve(ctx.view(), sleAccount, ctx.journal, {.ownerCountDelta = 1});
if (balance < reserve)
return tecINSUFFICIENT_RESERVE;
}
// Add ledger object to ledger
ctx.view().insert(sle);
// Add ledger object to owner's page
{
auto page =
ctx.view().dirInsert(keylet::ownerDir(owner), sle->key(), describeOwnerDir(owner));
if (!page)
return tecDIR_FULL; // LCOV_EXCL_LINE
(*sle)[sfOwnerNode] = *page;
}
increaseOwnerCount(ctx.view(), sleAccount, {}, 1, ctx.journal);
ctx.view().update(sleAccount);
return tesSUCCESS;
}
TER
DIDSet::doApply()
{
// Edit ledger object if it already exists
Keylet const didKeylet = keylet::did(accountID_);
if (auto const sleDID = ctx_.view().peek(didKeylet))
DIDEntryW sleDID(accountID_, ctx_.view(), ctx_.journal);
if (sleDID)
{
auto update = [&](auto const& sField) {
if (auto const field = ctx_.tx[~sField])
@@ -125,12 +86,12 @@ DIDSet::doApply()
{
return tecEMPTY_DID;
}
ctx_.view().update(sleDID);
sleDID.update();
return tesSUCCESS;
}
// Create new ledger object otherwise
auto const sleDID = std::make_shared<SLE>(didKeylet);
sleDID.newSLE();
(*sleDID)[sfAccount] = accountID_;
auto set = [&](auto const& sField) {
@@ -147,7 +108,7 @@ DIDSet::doApply()
return tecEMPTY_DID;
}
return addSLE(ctx_, sleDID, accountID_);
return sleDID.addToLedger(accountID_);
}
void

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>