Demonstrate how strongly typed ledger objects work

Signed-off-by: JCW <a1q123456@users.noreply.github.com>

# Conflicts:
#	include/xrpl/protocol/Keylet.h
#	src/libxrpl/protocol/Indexes.cpp
#	src/xrpld/rpc/handlers/VaultInfo.cpp
This commit is contained in:
JCW
2025-08-04 21:43:22 +01:00
parent 4f5fb6b298
commit ae95aff7aa
14 changed files with 49 additions and 36 deletions

View File

@@ -157,7 +157,7 @@ public:
return false;
SeqProxy const acctSeq =
SeqProxy::sequence(sleAcct->getFieldU32(sfSequence));
SeqProxy::sequence(sleAcct.fsfSequence());
SeqProxy const seqProx = txn.getSeqProxy();
if (seqProx.isSeq())

View File

@@ -245,7 +245,7 @@ authorizedDepositPreauth(
return tefINTERNAL; // LCOV_EXCL_LINE
auto [it, ins] =
sorted.emplace((*sleCred)[sfIssuer], (*sleCred)[sfCredentialType]);
sorted.emplace(sleCred.fsfIssuer(), sleCred.fsfCredentialType());
if (!ins)
return tefINTERNAL; // LCOV_EXCL_LINE
lifeExtender.push_back(std::move(sleCred));

View File

@@ -34,10 +34,10 @@ accountInDomain(
return false;
// domain owner is in the domain
if (sleDomain->getAccountID(sfOwner) == account)
if (sleDomain.fsfOwner() == account)
return true;
auto const& credentials = sleDomain->getFieldArray(sfAcceptedCredentials);
auto const& credentials = sleDomain.fsfAcceptedCredentials();
bool const inDomain = std::any_of(
credentials.begin(), credentials.end(), [&](auto const& credential) {
@@ -67,20 +67,20 @@ offerInDomain(
// have any of the following wrong behavior
if (!sleOffer)
return false; // LCOV_EXCL_LINE
if (!sleOffer->isFieldPresent(sfDomainID))
if (!sleOffer.fsfDomainID().has_value())
return false; // LCOV_EXCL_LINE
if (sleOffer->getFieldH256(sfDomainID) != domainID)
if (sleOffer.fsfDomainID() != domainID)
return false; // LCOV_EXCL_LINE
if (sleOffer->isFlag(lsfHybrid) &&
!sleOffer->isFieldPresent(sfAdditionalBooks))
!sleOffer.fsfAdditionalBooks())
{
JLOG(j.error()) << "Hybrid offer " << offerID
<< " missing AdditionalBooks field";
return false; // LCOV_EXCL_LINE
}
return accountInDomain(view, sleOffer->getAccountID(sfAccount), domainID);
return accountInDomain(view, sleOffer.fsfAccount(), domainID);
}
} // namespace permissioned_dex

View File

@@ -215,14 +215,14 @@ ammAccountHolds(
if (isXRP(issue))
{
if (auto const sle = view.read(keylet::account(ammAccountID)))
return (*sle)[sfBalance];
return sle.fsfBalance();
}
else if (auto const sle = view.read(
keylet::line(ammAccountID, issue.account, issue.currency));
sle &&
!isFrozen(view, ammAccountID, issue.currency, issue.account))
{
auto amount = (*sle)[sfBalance];
auto amount = sle.fsfBalance();
if (ammAccountID > issue.account)
amount.negate();
amount.setIssuer(issue.account);

View File

@@ -104,7 +104,7 @@ public:
ammSle && ammSle->getFieldAmount(sfLPTokenBalance) != beast::zero)
ammLiquidity_.emplace(
ctx.view,
(*ammSle)[sfAccount],
ammSle.fsfAccount(),
getTradingFee(ctx.view, *ammSle, ctx.ammContext.account()),
in,
out,
@@ -1392,7 +1392,7 @@ BookStep<TIn, TOut, TDerived>::check(StrandContext const& ctx) const
auto sle = view.read(keylet::line(*prev, cur, book_.in.currency));
if (!sle)
return terNO_LINE;
if ((*sle)[sfFlags] &
if ((*sle.getObject())[sfFlags] &
((cur > *prev) ? lsfHighNoRipple : lsfLowNoRipple))
return terNO_RIPPLE;
}

View File

@@ -421,7 +421,7 @@ DirectIPaymentStep::check(
if (((*sleSrc)[sfFlags] & lsfRequireAuth) &&
!((*sleLine)[sfFlags] & authField) &&
(*sleLine)[sfBalance] == beast::zero)
sleLine.fsfBalance() == beast::zero)
{
JLOG(j_.warn())
<< "DirectStepI: can't receive IOUs from issuer without auth."

View File

@@ -65,17 +65,17 @@ checkFreeze(
if (view.rules().enabled(fixFrozenLPTokenTransfer))
{
if (auto const sleDst = view.read(keylet::account(dst));
sleDst && sleDst->isFieldPresent(sfAMMID))
sleDst && sleDst.fsfAMMID().has_value())
{
auto const sleAmm = view.read(keylet::amm((*sleDst)[sfAMMID]));
auto const sleAmm = view.read(keylet::amm(sleDst.fsfAMMID().value()));
if (!sleAmm)
return tecINTERNAL; // LCOV_EXCL_LINE
if (isLPTokenFrozen(
view,
src,
(*sleAmm)[sfAsset].get<Issue>(),
(*sleAmm)[sfAsset2].get<Issue>()))
sleAmm.fsfAsset().get<Issue>(),
sleAmm.fsfAsset2().get<Issue>()))
{
return terNO_LINE;
}

View File

@@ -109,7 +109,7 @@ AMMBid::preclaim(PreclaimContext const& ctx)
return terNO_AMM;
}
auto const lpTokensBalance = (*ammSle)[sfLPTokenBalance];
auto const lpTokensBalance = ammSle.fsfLPTokenBalance();
if (lpTokensBalance == beast::zero)
return tecAMM_EMPTY;

View File

@@ -56,7 +56,7 @@ AMMDelete::preclaim(PreclaimContext const& ctx)
return terNO_AMM;
}
auto const lpTokensBalance = (*ammSle)[sfLPTokenBalance];
auto const lpTokensBalance = ammSle.fsfLPTokenBalance();
if (lpTokensBalance != beast::zero)
return tecAMM_NOT_EMPTY;

View File

@@ -220,7 +220,7 @@ AMMDeposit::preclaim(PreclaimContext const& ctx)
auto balance = [&](auto const& deposit) -> TER {
if (isXRP(deposit))
{
auto const lpIssue = (*ammSle)[sfLPTokenBalance].issue();
auto const lpIssue = ammSle.fsfLPTokenBalance().issue();
// Adjust the reserve if LP doesn't have LPToken trustline
auto const sle = ctx.view.read(
keylet::line(accountID, lpIssue.account, lpIssue.currency));

View File

@@ -61,7 +61,7 @@ CancelCheck::preclaim(PreclaimContext const& ctx)
using duration = NetClock::duration;
using timepoint = NetClock::time_point;
auto const optExpiry = (*sleCheck)[~sfExpiration];
auto const optExpiry = sleCheck.fsfExpiration();
// Expiration is defined in terms of the close time of the parent
// ledger, because we definitively know the time that it closed but
@@ -73,8 +73,8 @@ CancelCheck::preclaim(PreclaimContext const& ctx)
// If the check is not yet expired, then only the creator or the
// destination may cancel the check.
AccountID const acctId{ctx.tx[sfAccount]};
if (acctId != (*sleCheck)[sfAccount] &&
acctId != (*sleCheck)[sfDestination])
if (acctId != sleCheck.fsfAccount() &&
acctId != sleCheck.fsfDestination())
{
JLOG(ctx.j.warn()) << "Check is not expired and canceler is "
"neither check source nor destination.";

View File

@@ -61,7 +61,7 @@ CancelOffer::preclaim(PreclaimContext const& ctx)
if (!sle)
return terNO_ACCOUNT;
if ((*sle)[sfSequence] <= offerSequence)
if (sle.fsfSequence() <= offerSequence)
{
JLOG(ctx.j.trace()) << "Malformed transaction: "
<< "Sequence " << offerSequence << " is invalid.";

View File

@@ -33,6 +33,7 @@
#include <xrpl/protocol/STAmount.h>
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/TypedLedgerEntries.h>
#include <cstdint>
#include <optional>
@@ -170,6 +171,13 @@ public:
virtual std::shared_ptr<SLE const>
read(Keylet const& k) const = 0;
template<LedgerEntryType Type>
ConstLedgerObjectType<Type>
read(TypedKeylet<Type> const& k) const
{
return ConstLedgerObjectType<Type>::fromObject(read(k));
}
// Accounts in a payment are not allowed to use assets acquired during that
// payment. The PaymentSandbox tracks the debits, credits, and owner count
// changes that accounts make during a payment. `balanceHook` adjusts

View File

@@ -36,6 +36,7 @@
#include <xrpl/protocol/TxFlags.h>
#include <xrpl/protocol/digest.h>
#include <xrpl/protocol/st.h>
#include <xrpl/protocol/TypedLedgerEntries.h>
#include <type_traits>
#include <variant>
@@ -260,9 +261,9 @@ isFrozen(
if (issuer != account)
{
// Check if the issuer froze the line
sle = view.read(keylet::line(account, issuer, currency));
if (sle &&
sle->isFlag((issuer > account) ? lsfHighFreeze : lsfLowFreeze))
auto sleLine = view.read(keylet::line(account, issuer, currency));
if (sleLine &&
sleLine->isFlag((issuer > account) ? lsfHighFreeze : lsfLowFreeze))
return true;
}
return false;
@@ -319,30 +320,34 @@ isVaultPseudoAccountFrozen(
return true; // LCOV_EXCL_LINE
auto const mptIssuance =
view.read(keylet::mptIssuance(mptShare.getMptID()));
if (mptIssuance == nullptr)
ConstLedgerObjectType<ltMPTOKEN_ISSUANCE>::fromObject(
view.read(keylet::mptIssuance(mptShare.getMptID())));
if (mptIssuance.isValid())
return false; // zero MPToken won't block deletion of MPTokenIssuance
auto const issuer = mptIssuance->getAccountID(sfIssuer);
auto const mptIssuer = view.read(keylet::account(issuer));
if (mptIssuer == nullptr)
auto const issuer = mptIssuance.fsfIssuer();
auto const mptIssuer =
ConstLedgerObjectType<ltACCOUNT_ROOT>::fromObject(
view.read(keylet::account(issuer)));
if (mptIssuer.isValid())
{ // LCOV_EXCL_START
UNREACHABLE("ripple::isVaultPseudoAccountFrozen : null MPToken issuer");
return false;
} // LCOV_EXCL_STOP
if (!mptIssuer->isFieldPresent(sfVaultID))
if (!mptIssuer.fsfVaultID().has_value())
return false; // not a Vault pseudo-account, common case
auto const vault =
view.read(keylet::vault(mptIssuer->getFieldH256(sfVaultID)));
if (vault == nullptr)
ConstLedgerObjectType<ltVAULT>::fromObject(
view.read(keylet::vault(*mptIssuer.fsfVaultID())));
if (vault.isValid())
{ // LCOV_EXCL_START
UNREACHABLE("ripple::isVaultPseudoAccountFrozen : null vault");
return false;
} // LCOV_EXCL_STOP
return isAnyFrozen(view, {issuer, account}, vault->at(sfAsset), depth + 1);
return isAnyFrozen(view, {issuer, account}, vault.fsfAsset(), depth + 1);
}
bool