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; return false;
SeqProxy const acctSeq = SeqProxy const acctSeq =
SeqProxy::sequence(sleAcct->getFieldU32(sfSequence)); SeqProxy::sequence(sleAcct.fsfSequence());
SeqProxy const seqProx = txn.getSeqProxy(); SeqProxy const seqProx = txn.getSeqProxy();
if (seqProx.isSeq()) if (seqProx.isSeq())

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -65,17 +65,17 @@ checkFreeze(
if (view.rules().enabled(fixFrozenLPTokenTransfer)) if (view.rules().enabled(fixFrozenLPTokenTransfer))
{ {
if (auto const sleDst = view.read(keylet::account(dst)); 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) if (!sleAmm)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
if (isLPTokenFrozen( if (isLPTokenFrozen(
view, view,
src, src,
(*sleAmm)[sfAsset].get<Issue>(), sleAmm.fsfAsset().get<Issue>(),
(*sleAmm)[sfAsset2].get<Issue>())) sleAmm.fsfAsset2().get<Issue>()))
{ {
return terNO_LINE; return terNO_LINE;
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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