From 2bc17c3e735e2c46d27bddb01518ee756299e41b Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Mon, 21 Sep 2026 19:47:23 +0000 Subject: [PATCH] refactor: Add initial wrapper classes for all SLEs (#7886) --- .cspell.config.yaml | 1 + include/xrpl/ledger/entries/AMMEntry.h | 45 ++ .../xrpl/ledger/entries/AccountRootEntry.h | 35 ++ include/xrpl/ledger/entries/AmendmentsEntry.h | 33 ++ include/xrpl/ledger/entries/BridgeEntry.h | 36 ++ include/xrpl/ledger/entries/CheckEntry.h | 46 ++ include/xrpl/ledger/entries/CredentialEntry.h | 47 ++ include/xrpl/ledger/entries/DIDEntry.h | 35 ++ include/xrpl/ledger/entries/DelegateEntry.h | 36 ++ .../xrpl/ledger/entries/DepositPreauthEntry.h | 58 ++ .../xrpl/ledger/entries/DirectoryNodeEntry.h | 50 ++ include/xrpl/ledger/entries/EscrowEntry.h | 37 ++ .../xrpl/ledger/entries/FeeSettingsEntry.h | 33 ++ .../xrpl/ledger/entries/LedgerHashesEntry.h | 33 ++ include/xrpl/ledger/entries/LoanBrokerEntry.h | 46 ++ include/xrpl/ledger/entries/LoanEntry.h | 45 ++ include/xrpl/ledger/entries/MPTokenEntry.h | 55 ++ .../ledger/entries/MPTokenIssuanceEntry.h | 56 ++ .../xrpl/ledger/entries/NFTokenOfferEntry.h | 46 ++ .../xrpl/ledger/entries/NFTokenPageEntry.h | 37 ++ .../xrpl/ledger/entries/NegativeUNLEntry.h | 33 ++ include/xrpl/ledger/entries/OfferEntry.h | 46 ++ include/xrpl/ledger/entries/OracleEntry.h | 38 ++ include/xrpl/ledger/entries/PayChannelEntry.h | 38 ++ .../ledger/entries/PermissionedDomainEntry.h | 46 ++ .../xrpl/ledger/entries/RippleStateEntry.h | 48 ++ include/xrpl/ledger/entries/SLEBase.h | 503 ++++++++++++++++++ include/xrpl/ledger/entries/SignerListEntry.h | 35 ++ .../xrpl/ledger/entries/SponsorshipEntry.h | 36 ++ include/xrpl/ledger/entries/TicketEntry.h | 46 ++ include/xrpl/ledger/entries/VaultEntry.h | 46 ++ .../ledger/entries/XChainOwnedClaimIDEntry.h | 38 ++ .../XChainOwnedCreateAccountClaimIDEntry.h | 39 ++ src/tests/libxrpl/CMakeLists.txt | 1 + src/tests/libxrpl/ledger/AMMEntry.cpp | 25 + src/tests/libxrpl/ledger/AccountRootEntry.cpp | 21 + src/tests/libxrpl/ledger/AmendmentsEntry.cpp | 17 + src/tests/libxrpl/ledger/BridgeEntry.cpp | 41 ++ src/tests/libxrpl/ledger/CheckEntry.cpp | 23 + src/tests/libxrpl/ledger/CredentialEntry.cpp | 39 ++ src/tests/libxrpl/ledger/DIDEntry.cpp | 17 + src/tests/libxrpl/ledger/DelegateEntry.cpp | 29 + .../libxrpl/ledger/DepositPreauthEntry.cpp | 54 ++ .../libxrpl/ledger/DirectoryNodeEntry.cpp | 28 + src/tests/libxrpl/ledger/EntryTestHelpers.h | 123 +++++ src/tests/libxrpl/ledger/EscrowEntry.cpp | 21 + src/tests/libxrpl/ledger/FeeSettingsEntry.cpp | 17 + .../libxrpl/ledger/LedgerHashesEntry.cpp | 17 + src/tests/libxrpl/ledger/LoanBrokerEntry.cpp | 24 + src/tests/libxrpl/ledger/LoanEntry.cpp | 29 + src/tests/libxrpl/ledger/MPTokenEntry.cpp | 34 ++ .../libxrpl/ledger/MPTokenIssuanceEntry.cpp | 33 ++ .../libxrpl/ledger/NFTokenOfferEntry.cpp | 24 + src/tests/libxrpl/ledger/NFTokenPageEntry.cpp | 25 + src/tests/libxrpl/ledger/NegativeUNLEntry.cpp | 17 + src/tests/libxrpl/ledger/OfferEntry.cpp | 23 + src/tests/libxrpl/ledger/OracleEntry.cpp | 24 + src/tests/libxrpl/ledger/PayChannelEntry.cpp | 33 ++ .../ledger/PermissionedDomainEntry.cpp | 28 + src/tests/libxrpl/ledger/RippleStateEntry.cpp | 43 ++ src/tests/libxrpl/ledger/SLEBase.cpp | 428 +++++++++++++++ src/tests/libxrpl/ledger/SignerListEntry.cpp | 18 + src/tests/libxrpl/ledger/SponsorshipEntry.cpp | 29 + src/tests/libxrpl/ledger/TicketEntry.cpp | 27 + src/tests/libxrpl/ledger/VaultEntry.cpp | 23 + .../ledger/XChainOwnedClaimIDEntry.cpp | 29 + .../XChainOwnedCreateAccountClaimIDEntry.cpp | 35 ++ 67 files changed, 3201 insertions(+) create mode 100644 include/xrpl/ledger/entries/AMMEntry.h create mode 100644 include/xrpl/ledger/entries/AccountRootEntry.h create mode 100644 include/xrpl/ledger/entries/AmendmentsEntry.h create mode 100644 include/xrpl/ledger/entries/BridgeEntry.h create mode 100644 include/xrpl/ledger/entries/CheckEntry.h create mode 100644 include/xrpl/ledger/entries/CredentialEntry.h create mode 100644 include/xrpl/ledger/entries/DIDEntry.h create mode 100644 include/xrpl/ledger/entries/DelegateEntry.h create mode 100644 include/xrpl/ledger/entries/DepositPreauthEntry.h create mode 100644 include/xrpl/ledger/entries/DirectoryNodeEntry.h create mode 100644 include/xrpl/ledger/entries/EscrowEntry.h create mode 100644 include/xrpl/ledger/entries/FeeSettingsEntry.h create mode 100644 include/xrpl/ledger/entries/LedgerHashesEntry.h create mode 100644 include/xrpl/ledger/entries/LoanBrokerEntry.h create mode 100644 include/xrpl/ledger/entries/LoanEntry.h create mode 100644 include/xrpl/ledger/entries/MPTokenEntry.h create mode 100644 include/xrpl/ledger/entries/MPTokenIssuanceEntry.h create mode 100644 include/xrpl/ledger/entries/NFTokenOfferEntry.h create mode 100644 include/xrpl/ledger/entries/NFTokenPageEntry.h create mode 100644 include/xrpl/ledger/entries/NegativeUNLEntry.h create mode 100644 include/xrpl/ledger/entries/OfferEntry.h create mode 100644 include/xrpl/ledger/entries/OracleEntry.h create mode 100644 include/xrpl/ledger/entries/PayChannelEntry.h create mode 100644 include/xrpl/ledger/entries/PermissionedDomainEntry.h create mode 100644 include/xrpl/ledger/entries/RippleStateEntry.h create mode 100644 include/xrpl/ledger/entries/SLEBase.h create mode 100644 include/xrpl/ledger/entries/SignerListEntry.h create mode 100644 include/xrpl/ledger/entries/SponsorshipEntry.h create mode 100644 include/xrpl/ledger/entries/TicketEntry.h create mode 100644 include/xrpl/ledger/entries/VaultEntry.h create mode 100644 include/xrpl/ledger/entries/XChainOwnedClaimIDEntry.h create mode 100644 include/xrpl/ledger/entries/XChainOwnedCreateAccountClaimIDEntry.h create mode 100644 src/tests/libxrpl/ledger/AMMEntry.cpp create mode 100644 src/tests/libxrpl/ledger/AccountRootEntry.cpp create mode 100644 src/tests/libxrpl/ledger/AmendmentsEntry.cpp create mode 100644 src/tests/libxrpl/ledger/BridgeEntry.cpp create mode 100644 src/tests/libxrpl/ledger/CheckEntry.cpp create mode 100644 src/tests/libxrpl/ledger/CredentialEntry.cpp create mode 100644 src/tests/libxrpl/ledger/DIDEntry.cpp create mode 100644 src/tests/libxrpl/ledger/DelegateEntry.cpp create mode 100644 src/tests/libxrpl/ledger/DepositPreauthEntry.cpp create mode 100644 src/tests/libxrpl/ledger/DirectoryNodeEntry.cpp create mode 100644 src/tests/libxrpl/ledger/EntryTestHelpers.h create mode 100644 src/tests/libxrpl/ledger/EscrowEntry.cpp create mode 100644 src/tests/libxrpl/ledger/FeeSettingsEntry.cpp create mode 100644 src/tests/libxrpl/ledger/LedgerHashesEntry.cpp create mode 100644 src/tests/libxrpl/ledger/LoanBrokerEntry.cpp create mode 100644 src/tests/libxrpl/ledger/LoanEntry.cpp create mode 100644 src/tests/libxrpl/ledger/MPTokenEntry.cpp create mode 100644 src/tests/libxrpl/ledger/MPTokenIssuanceEntry.cpp create mode 100644 src/tests/libxrpl/ledger/NFTokenOfferEntry.cpp create mode 100644 src/tests/libxrpl/ledger/NFTokenPageEntry.cpp create mode 100644 src/tests/libxrpl/ledger/NegativeUNLEntry.cpp create mode 100644 src/tests/libxrpl/ledger/OfferEntry.cpp create mode 100644 src/tests/libxrpl/ledger/OracleEntry.cpp create mode 100644 src/tests/libxrpl/ledger/PayChannelEntry.cpp create mode 100644 src/tests/libxrpl/ledger/PermissionedDomainEntry.cpp create mode 100644 src/tests/libxrpl/ledger/RippleStateEntry.cpp create mode 100644 src/tests/libxrpl/ledger/SLEBase.cpp create mode 100644 src/tests/libxrpl/ledger/SignerListEntry.cpp create mode 100644 src/tests/libxrpl/ledger/SponsorshipEntry.cpp create mode 100644 src/tests/libxrpl/ledger/TicketEntry.cpp create mode 100644 src/tests/libxrpl/ledger/VaultEntry.cpp create mode 100644 src/tests/libxrpl/ledger/XChainOwnedClaimIDEntry.cpp create mode 100644 src/tests/libxrpl/ledger/XChainOwnedCreateAccountClaimIDEntry.cpp diff --git a/.cspell.config.yaml b/.cspell.config.yaml index a706c344de..48afdc2b7a 100644 --- a/.cspell.config.yaml +++ b/.cspell.config.yaml @@ -64,6 +64,7 @@ words: - blindings - bookdir - Bougalis + - bthomee - Britto - Btrfs - Buildx diff --git a/include/xrpl/ledger/entries/AMMEntry.h b/include/xrpl/ledger/entries/AMMEntry.h new file mode 100644 index 0000000000..0c1c0fe629 --- /dev/null +++ b/include/xrpl/ledger/entries/AMMEntry.h @@ -0,0 +1,45 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +template +class AMMEntry : public SLEBase +{ +public: + using Base = SLEBase; + + // Inherit base constructors: adopt an existing SLE, or resolve one from a + // Keylet against the view. + using Base::Base; + + explicit AMMEntry( + Asset const& issue1, + Asset const& issue2, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::amm(issue1, issue2), view, j) + { + } + + explicit AMMEntry( + uint256 const& ammID, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::amm(ammID), view, j) + { + } +}; + +using AMMEntryR = AMMEntry; +using AMMEntryW = AMMEntry; + +} // namespace xrpl diff --git a/include/xrpl/ledger/entries/AccountRootEntry.h b/include/xrpl/ledger/entries/AccountRootEntry.h new file mode 100644 index 0000000000..5555ea8c50 --- /dev/null +++ b/include/xrpl/ledger/entries/AccountRootEntry.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +template +class AccountRootEntry : public SLEBase +{ +public: + using Base = SLEBase; + + // Inherit base constructors: adopt an existing SLE, or resolve one from a + // Keylet against the view. + using Base::Base; + + explicit AccountRootEntry( + AccountID const& id, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::account(id), view, j) + { + } +}; + +using AccountRootEntryR = AccountRootEntry; +using AccountRootEntryW = AccountRootEntry; + +} // namespace xrpl diff --git a/include/xrpl/ledger/entries/AmendmentsEntry.h b/include/xrpl/ledger/entries/AmendmentsEntry.h new file mode 100644 index 0000000000..ce4dca7e80 --- /dev/null +++ b/include/xrpl/ledger/entries/AmendmentsEntry.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +template +class AmendmentsEntry : public SLEBase +{ +public: + using Base = SLEBase; + + // Inherit base constructors: adopt an existing SLE, or resolve one from a + // Keylet against the view. + using Base::Base; + + explicit AmendmentsEntry( + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::amendments(), view, j) + { + } +}; + +using AmendmentsEntryR = AmendmentsEntry; +using AmendmentsEntryW = AmendmentsEntry; + +} // namespace xrpl diff --git a/include/xrpl/ledger/entries/BridgeEntry.h b/include/xrpl/ledger/entries/BridgeEntry.h new file mode 100644 index 0000000000..a51fbe0c12 --- /dev/null +++ b/include/xrpl/ledger/entries/BridgeEntry.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +template +class BridgeEntry : public SLEBase +{ +public: + using Base = SLEBase; + + // Inherit base constructors: adopt an existing SLE, or resolve one from a + // Keylet against the view. + using Base::Base; + + explicit BridgeEntry( + STXChainBridge const& bridge, + STXChainBridge::ChainType chainType, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::bridge(bridge, chainType), view, j) + { + } +}; + +using BridgeEntryR = BridgeEntry; +using BridgeEntryW = BridgeEntry; + +} // namespace xrpl diff --git a/include/xrpl/ledger/entries/CheckEntry.h b/include/xrpl/ledger/entries/CheckEntry.h new file mode 100644 index 0000000000..bf6a188ff5 --- /dev/null +++ b/include/xrpl/ledger/entries/CheckEntry.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +template +class CheckEntry : public SLEBase +{ +public: + using Base = SLEBase; + + // Inherit base constructors: adopt an existing SLE, or resolve one from a + // Keylet against the view. + using Base::Base; + + explicit CheckEntry( + AccountID const& id, + SeqProxy const& seq, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::check(id, seq), view, j) + { + } + + explicit CheckEntry( + uint256 const& checkID, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::check(checkID), view, j) + { + } +}; + +using CheckEntryR = CheckEntry; +using CheckEntryW = CheckEntry; + +} // namespace xrpl diff --git a/include/xrpl/ledger/entries/CredentialEntry.h b/include/xrpl/ledger/entries/CredentialEntry.h new file mode 100644 index 0000000000..4b6a3386a1 --- /dev/null +++ b/include/xrpl/ledger/entries/CredentialEntry.h @@ -0,0 +1,47 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +template +class CredentialEntry : public SLEBase +{ +public: + using Base = SLEBase; + + // Inherit base constructors: adopt an existing SLE, or resolve one from a + // Keylet against the view. + using Base::Base; + + explicit CredentialEntry( + AccountID const& subject, + AccountID const& issuer, + Slice const& credType, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::credential(subject, issuer, credType), view, j) + { + } + + explicit CredentialEntry( + uint256 const& credentialID, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::credential(credentialID), view, j) + { + } +}; + +using CredentialEntryR = CredentialEntry; +using CredentialEntryW = CredentialEntry; + +} // namespace xrpl diff --git a/include/xrpl/ledger/entries/DIDEntry.h b/include/xrpl/ledger/entries/DIDEntry.h new file mode 100644 index 0000000000..ee3eaf8153 --- /dev/null +++ b/include/xrpl/ledger/entries/DIDEntry.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +template +class DIDEntry : public SLEBase +{ +public: + using Base = SLEBase; + + // Inherit base constructors: adopt an existing SLE, or resolve one from a + // Keylet against the view. + using Base::Base; + + explicit DIDEntry( + AccountID const& account, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::did(account), view, j) + { + } +}; + +using DIDEntryR = DIDEntry; +using DIDEntryW = DIDEntry; + +} // namespace xrpl diff --git a/include/xrpl/ledger/entries/DelegateEntry.h b/include/xrpl/ledger/entries/DelegateEntry.h new file mode 100644 index 0000000000..de908d4dcd --- /dev/null +++ b/include/xrpl/ledger/entries/DelegateEntry.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +template +class DelegateEntry : public SLEBase +{ +public: + using Base = SLEBase; + + // Inherit base constructors: adopt an existing SLE, or resolve one from a + // Keylet against the view. + using Base::Base; + + explicit DelegateEntry( + AccountID const& account, + AccountID const& authorizedAccount, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::delegate(account, authorizedAccount), view, j) + { + } +}; + +using DelegateEntryR = DelegateEntry; +using DelegateEntryW = DelegateEntry; + +} // namespace xrpl diff --git a/include/xrpl/ledger/entries/DepositPreauthEntry.h b/include/xrpl/ledger/entries/DepositPreauthEntry.h new file mode 100644 index 0000000000..783363084d --- /dev/null +++ b/include/xrpl/ledger/entries/DepositPreauthEntry.h @@ -0,0 +1,58 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl { + +template +class DepositPreauthEntry : public SLEBase +{ +public: + using Base = SLEBase; + + // Inherit base constructors: adopt an existing SLE, or resolve one from a + // Keylet against the view. + using Base::Base; + + explicit DepositPreauthEntry( + AccountID const& owner, + AccountID const& preauthorized, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::depositPreauth(owner, preauthorized), view, j) + { + } + + explicit DepositPreauthEntry( + AccountID const& owner, + std::set> const& authCreds, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::depositPreauth(owner, authCreds), view, j) + { + } + + explicit DepositPreauthEntry( + uint256 const& preauthID, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::depositPreauth(preauthID), view, j) + { + } +}; + +using DepositPreauthEntryR = DepositPreauthEntry; +using DepositPreauthEntryW = DepositPreauthEntry; + +} // namespace xrpl diff --git a/include/xrpl/ledger/entries/DirectoryNodeEntry.h b/include/xrpl/ledger/entries/DirectoryNodeEntry.h new file mode 100644 index 0000000000..b57bec7348 --- /dev/null +++ b/include/xrpl/ledger/entries/DirectoryNodeEntry.h @@ -0,0 +1,50 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace xrpl { + +template +class DirectoryNodeEntry : public SLEBase +{ +public: + using Base = SLEBase; + + // Inherit base constructors: adopt an existing SLE, or resolve one from a + // Keylet against the view. + using Base::Base; + + explicit DirectoryNodeEntry( + AccountID const& id, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::ownerDir(id), view, j) + { + } + + /** + * Resolve a specific page of the directory rooted at @p root. + */ + explicit DirectoryNodeEntry( + uint256 const& root, + std::uint64_t index, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::page(root, index), view, j) + { + } +}; + +using DirectoryNodeEntryR = DirectoryNodeEntry; +using DirectoryNodeEntryW = DirectoryNodeEntry; + +} // namespace xrpl diff --git a/include/xrpl/ledger/entries/EscrowEntry.h b/include/xrpl/ledger/entries/EscrowEntry.h new file mode 100644 index 0000000000..9eb1418a4c --- /dev/null +++ b/include/xrpl/ledger/entries/EscrowEntry.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +template +class EscrowEntry : public SLEBase +{ +public: + using Base = SLEBase; + + // Inherit base constructors: adopt an existing SLE, or resolve one from a + // Keylet against the view. + using Base::Base; + + explicit EscrowEntry( + AccountID const& src, + SeqProxy const& seq, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::escrow(src, seq), view, j) + { + } +}; + +using EscrowEntryR = EscrowEntry; +using EscrowEntryW = EscrowEntry; + +} // namespace xrpl diff --git a/include/xrpl/ledger/entries/FeeSettingsEntry.h b/include/xrpl/ledger/entries/FeeSettingsEntry.h new file mode 100644 index 0000000000..66d61892e5 --- /dev/null +++ b/include/xrpl/ledger/entries/FeeSettingsEntry.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +template +class FeeSettingsEntry : public SLEBase +{ +public: + using Base = SLEBase; + + // Inherit base constructors: adopt an existing SLE, or resolve one from a + // Keylet against the view. + using Base::Base; + + explicit FeeSettingsEntry( + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::feeSettings(), view, j) + { + } +}; + +using FeeSettingsEntryR = FeeSettingsEntry; +using FeeSettingsEntryW = FeeSettingsEntry; + +} // namespace xrpl diff --git a/include/xrpl/ledger/entries/LedgerHashesEntry.h b/include/xrpl/ledger/entries/LedgerHashesEntry.h new file mode 100644 index 0000000000..ab54d820a1 --- /dev/null +++ b/include/xrpl/ledger/entries/LedgerHashesEntry.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +template +class LedgerHashesEntry : public SLEBase +{ +public: + using Base = SLEBase; + + // Inherit base constructors: adopt an existing SLE, or resolve one from a + // Keylet against the view. + using Base::Base; + + explicit LedgerHashesEntry( + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::skip(), view, j) + { + } +}; + +using LedgerHashesEntryR = LedgerHashesEntry; +using LedgerHashesEntryW = LedgerHashesEntry; + +} // namespace xrpl diff --git a/include/xrpl/ledger/entries/LoanBrokerEntry.h b/include/xrpl/ledger/entries/LoanBrokerEntry.h new file mode 100644 index 0000000000..77e6b660ed --- /dev/null +++ b/include/xrpl/ledger/entries/LoanBrokerEntry.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +template +class LoanBrokerEntry : public SLEBase +{ +public: + using Base = SLEBase; + + // Inherit base constructors: adopt an existing SLE, or resolve one from a + // Keylet against the view. + using Base::Base; + + explicit LoanBrokerEntry( + AccountID const& owner, + SeqProxy const& seq, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::loanBroker(owner, seq), view, j) + { + } + + explicit LoanBrokerEntry( + uint256 const& loanBrokerID, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::loanBroker(loanBrokerID), view, j) + { + } +}; + +using LoanBrokerEntryR = LoanBrokerEntry; +using LoanBrokerEntryW = LoanBrokerEntry; + +} // namespace xrpl diff --git a/include/xrpl/ledger/entries/LoanEntry.h b/include/xrpl/ledger/entries/LoanEntry.h new file mode 100644 index 0000000000..124c5915fc --- /dev/null +++ b/include/xrpl/ledger/entries/LoanEntry.h @@ -0,0 +1,45 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +template +class LoanEntry : public SLEBase +{ +public: + using Base = SLEBase; + + // Inherit base constructors: adopt an existing SLE, or resolve one from a + // Keylet against the view. + using Base::Base; + + explicit LoanEntry( + uint256 const& loanBrokerID, + SeqProxy const& loanSeq, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::loan(loanBrokerID, loanSeq), view, j) + { + } + + explicit LoanEntry( + uint256 const& loanID, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::loan(loanID), view, j) + { + } +}; + +using LoanEntryR = LoanEntry; +using LoanEntryW = LoanEntry; + +} // namespace xrpl diff --git a/include/xrpl/ledger/entries/MPTokenEntry.h b/include/xrpl/ledger/entries/MPTokenEntry.h new file mode 100644 index 0000000000..dcd6c5adb1 --- /dev/null +++ b/include/xrpl/ledger/entries/MPTokenEntry.h @@ -0,0 +1,55 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +template +class MPTokenEntry : public SLEBase +{ +public: + using Base = SLEBase; + + // Inherit base constructors: adopt an existing SLE, or resolve one from a + // Keylet against the view. + using Base::Base; + + explicit MPTokenEntry( + MPTID const& issuanceID, + AccountID const& holder, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::mptoken(issuanceID, holder), view, j) + { + } + + explicit MPTokenEntry( + uint256 const& issuanceKey, + AccountID const& holder, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::mptoken(issuanceKey, holder), view, j) + { + } + + explicit MPTokenEntry( + uint256 const& mptokenKey, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::mptoken(mptokenKey), view, j) + { + } +}; + +using MPTokenEntryR = MPTokenEntry; +using MPTokenEntryW = MPTokenEntry; + +} // namespace xrpl diff --git a/include/xrpl/ledger/entries/MPTokenIssuanceEntry.h b/include/xrpl/ledger/entries/MPTokenIssuanceEntry.h new file mode 100644 index 0000000000..52dac53217 --- /dev/null +++ b/include/xrpl/ledger/entries/MPTokenIssuanceEntry.h @@ -0,0 +1,56 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace xrpl { + +template +class MPTokenIssuanceEntry : public SLEBase +{ +public: + using Base = SLEBase; + + // Inherit base constructors: adopt an existing SLE, or resolve one from a + // Keylet against the view. + using Base::Base; + + explicit MPTokenIssuanceEntry( + std::uint32_t seq, + AccountID const& issuer, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::mptokenIssuance(makeMptID(seq, issuer)), view, j) + { + } + + explicit MPTokenIssuanceEntry( + MPTID const& issuanceID, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::mptokenIssuance(issuanceID), view, j) + { + } + + explicit MPTokenIssuanceEntry( + uint256 const& issuanceKey, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::mptokenIssuance(issuanceKey), view, j) + { + } +}; + +using MPTokenIssuanceEntryR = MPTokenIssuanceEntry; +using MPTokenIssuanceEntryW = MPTokenIssuanceEntry; + +} // namespace xrpl diff --git a/include/xrpl/ledger/entries/NFTokenOfferEntry.h b/include/xrpl/ledger/entries/NFTokenOfferEntry.h new file mode 100644 index 0000000000..a2e71652cd --- /dev/null +++ b/include/xrpl/ledger/entries/NFTokenOfferEntry.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +template +class NFTokenOfferEntry : public SLEBase +{ +public: + using Base = SLEBase; + + // Inherit base constructors: adopt an existing SLE, or resolve one from a + // Keylet against the view. + using Base::Base; + + explicit NFTokenOfferEntry( + AccountID const& owner, + SeqProxy const& seq, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::nftokenOffer(owner, seq), view, j) + { + } + + explicit NFTokenOfferEntry( + uint256 const& offerID, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::nftokenOffer(offerID), view, j) + { + } +}; + +using NFTokenOfferEntryR = NFTokenOfferEntry; +using NFTokenOfferEntryW = NFTokenOfferEntry; + +} // namespace xrpl diff --git a/include/xrpl/ledger/entries/NFTokenPageEntry.h b/include/xrpl/ledger/entries/NFTokenPageEntry.h new file mode 100644 index 0000000000..11701f17ea --- /dev/null +++ b/include/xrpl/ledger/entries/NFTokenPageEntry.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +template +class NFTokenPageEntry : public SLEBase +{ +public: + using Base = SLEBase; + + // Inherit base constructors: adopt an existing SLE, or resolve one from a + // Keylet against the view. + using Base::Base; + + explicit NFTokenPageEntry( + Keylet const& page, + uint256 const& token, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::nftokenPage(page, token), view, j) + { + } +}; + +using NFTokenPageEntryR = NFTokenPageEntry; +using NFTokenPageEntryW = NFTokenPageEntry; + +} // namespace xrpl diff --git a/include/xrpl/ledger/entries/NegativeUNLEntry.h b/include/xrpl/ledger/entries/NegativeUNLEntry.h new file mode 100644 index 0000000000..9c61058622 --- /dev/null +++ b/include/xrpl/ledger/entries/NegativeUNLEntry.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +template +class NegativeUNLEntry : public SLEBase +{ +public: + using Base = SLEBase; + + // Inherit base constructors: adopt an existing SLE, or resolve one from a + // Keylet against the view. + using Base::Base; + + explicit NegativeUNLEntry( + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::negativeUNL(), view, j) + { + } +}; + +using NegativeUNLEntryR = NegativeUNLEntry; +using NegativeUNLEntryW = NegativeUNLEntry; + +} // namespace xrpl diff --git a/include/xrpl/ledger/entries/OfferEntry.h b/include/xrpl/ledger/entries/OfferEntry.h new file mode 100644 index 0000000000..2180d3c89c --- /dev/null +++ b/include/xrpl/ledger/entries/OfferEntry.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +template +class OfferEntry : public SLEBase +{ +public: + using Base = SLEBase; + + // Inherit base constructors: adopt an existing SLE, or resolve one from a + // Keylet against the view. + using Base::Base; + + explicit OfferEntry( + AccountID const& id, + SeqProxy const& seq, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::offer(id, seq), view, j) + { + } + + explicit OfferEntry( + uint256 const& offerID, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::offer(offerID), view, j) + { + } +}; + +using OfferEntryR = OfferEntry; +using OfferEntryW = OfferEntry; + +} // namespace xrpl diff --git a/include/xrpl/ledger/entries/OracleEntry.h b/include/xrpl/ledger/entries/OracleEntry.h new file mode 100644 index 0000000000..69013a2745 --- /dev/null +++ b/include/xrpl/ledger/entries/OracleEntry.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace xrpl { + +template +class OracleEntry : public SLEBase +{ +public: + using Base = SLEBase; + + // Inherit base constructors: adopt an existing SLE, or resolve one from a + // Keylet against the view. + using Base::Base; + + explicit OracleEntry( + AccountID const& account, + std::uint32_t documentID, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::oracle(account, documentID), view, j) + { + } +}; + +using OracleEntryR = OracleEntry; +using OracleEntryW = OracleEntry; + +} // namespace xrpl diff --git a/include/xrpl/ledger/entries/PayChannelEntry.h b/include/xrpl/ledger/entries/PayChannelEntry.h new file mode 100644 index 0000000000..3540474633 --- /dev/null +++ b/include/xrpl/ledger/entries/PayChannelEntry.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +template +class PayChannelEntry : public SLEBase +{ +public: + using Base = SLEBase; + + // Inherit base constructors: adopt an existing SLE, or resolve one from a + // Keylet against the view. + using Base::Base; + + explicit PayChannelEntry( + AccountID const& src, + AccountID const& dst, + SeqProxy const& seq, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::payChannel(src, dst, seq), view, j) + { + } +}; + +using PayChannelEntryR = PayChannelEntry; +using PayChannelEntryW = PayChannelEntry; + +} // namespace xrpl diff --git a/include/xrpl/ledger/entries/PermissionedDomainEntry.h b/include/xrpl/ledger/entries/PermissionedDomainEntry.h new file mode 100644 index 0000000000..0668a8f8b6 --- /dev/null +++ b/include/xrpl/ledger/entries/PermissionedDomainEntry.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +template +class PermissionedDomainEntry : public SLEBase +{ +public: + using Base = SLEBase; + + // Inherit base constructors: adopt an existing SLE, or resolve one from a + // Keylet against the view. + using Base::Base; + + explicit PermissionedDomainEntry( + AccountID const& account, + SeqProxy const& seq, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::permissionedDomain(account, seq), view, j) + { + } + + explicit PermissionedDomainEntry( + uint256 const& domainID, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::permissionedDomain(domainID), view, j) + { + } +}; + +using PermissionedDomainEntryR = PermissionedDomainEntry; +using PermissionedDomainEntryW = PermissionedDomainEntry; + +} // namespace xrpl diff --git a/include/xrpl/ledger/entries/RippleStateEntry.h b/include/xrpl/ledger/entries/RippleStateEntry.h new file mode 100644 index 0000000000..f9bd8aa703 --- /dev/null +++ b/include/xrpl/ledger/entries/RippleStateEntry.h @@ -0,0 +1,48 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +template +class RippleStateEntry : public SLEBase +{ +public: + using Base = SLEBase; + + // Inherit base constructors: adopt an existing SLE, or resolve one from a + // Keylet against the view. + using Base::Base; + + explicit RippleStateEntry( + AccountID const& id0, + AccountID const& id1, + Currency const& currency, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::trustLine(id0, id1, currency), view, j) + { + } + + explicit RippleStateEntry( + AccountID const& id, + Issue const& issue, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::trustLine(id, issue), view, j) + { + } +}; + +using RippleStateEntryR = RippleStateEntry; +using RippleStateEntryW = RippleStateEntry; + +} // namespace xrpl diff --git a/include/xrpl/ledger/entries/SLEBase.h b/include/xrpl/ledger/entries/SLEBase.h new file mode 100644 index 0000000000..182e03b55d --- /dev/null +++ b/include/xrpl/ledger/entries/SLEBase.h @@ -0,0 +1,503 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace xrpl { + +// Concept to distinguish read-only vs writable view types +template +concept IsWritableView = std::derived_from; + +namespace detail { + +/** + * Resolves a keylet for a read-only entry. + * + * ReadView::read() on an ApplyView returns the underlying ledger's entry + * whenever the view is not already tracking one, while peek() installs the + * view's own copy and returns that. A read-only entry built with read() + * would therefore hold an SLE that goes stale the moment anything peeks the + * same key and modifies it. Resolve through peek() whenever the view really is + * an ApplyView, so every entry over that view shares one SLE. + * + * @note The const_cast is what makes reaching ApplyView::peek() possible, and + * it is defined behavior only when the view really is a non-const + * object that the caller merely observes through a const reference. + * That holds for every production view today, but it is not a + * guarantee the codebase makes: the unit tests already build + * genuinely const ApplyView-derived objects (`Sandbox const` in + * Directory_test.cpp and View_test.cpp, `PaymentSandbox const` in + * TheoreticalQuality_test.cpp and View_test.cpp). Constructing a + * read-only entry over one of those would be undefined behavior, so + * do not, until #8069 removes the cast -- by giving ApplyView a + * const-qualified peek(), which needs no amendment because + * Action::Cache is invisible to apply(), visit() and metadata. + * + * @note Consequently a "read-only" entry over an ApplyView is not free of + * side effects: peek() installs an Action::Cache entry in the apply + * state table. That is benign for transaction metadata -- Cache entries + * are skipped in ApplyStateTable::apply(), ::visit() and in metadata + * generation -- but it does cost one deep SLE copy on first touch. + */ +inline SLE::const_pointer +resolveEntry(ReadView const& view, Keylet const& key) +{ + // Safe only for a view that is not itself a const object -- see the + // note above. The entry holds a const reference because it does not + // modify the view, not because the view is const. + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) + if (auto const applyView = dynamic_cast(const_cast(&view))) + return applyView->peek(key); + return view.read(key); +} + +} // namespace detail + +/** + * View-parameterized base class for all ledger entries. + * + * SLEBase — read-only: holds shared_ptr + ReadView const& + * SLEBase — writable: holds shared_ptr + ApplyView& + Keylet, + * plus insert/update/erase operations + * + * Write-only members are gated by `requires` clauses, providing compile-time + * guarantees that read-only entries cannot mutate state. + * + * @tparam EntryType the ledger entry type this entry is statically bound to. + * Derived per-type entries pass their own type (e.g. ltACCOUNT_ROOT); the + * generic ReadOnlySLE / WritableSLE aliases leave it at ltANY, which opts out + * of the static type check. Binding the type here is what keeps an entry for + * one entry type from being constructed or converted from another -- see the + * converting constructor below. + * + * Derived classes should provide domain-specific accessors that hide + * implementation details of the underlying ledger entry format. + */ +template +class SLEBase +{ +public: + static constexpr bool kIsWritable = IsWritableView; + + // The ledger entry type this entry is bound to, and whether that binding + // is meaningful (ltANY means "any type", i.e. no static check). + static constexpr LedgerEntryType kEntryType = EntryType; + static constexpr bool kIsTyped = (EntryType != ltANY); + + // SLE pointer type: mutable for writable views, const for read-only + using SlePtrType = std::conditional_t; + + // View reference type: ApplyView& for writable, ReadView const& for + // read-only + using ViewRefType = std::conditional_t; + + // Non-virtual by design: these entries are parameterized on the view and + // entry type, never used polymorphically through a base pointer. A vptr + // would be 8 bytes of pure overhead on a type meant to be as cheap as the + // shared_ptr it wraps. See the static_assert below the class. + // + // The destructor is public because the ReadOnlySLE / WritableSLE aliases + // name this class directly and are used as value types. Since it is not + // virtual, never delete a derived entry through an SLEBase*. + ~SLEBase() = default; + + SLEBase(SLEBase const&) + requires(!kIsWritable) + = default; + SLEBase(SLEBase&&) = default; + SLEBase& + operator=(SLEBase const&) = delete; + SLEBase& + operator=(SLEBase&&) = delete; + SLEBase() = delete; + + // --- Constructors that adopt/resolve an SLE (public so the ReadOnlySLE / + // WritableSLE aliases and the per-type entries can be built directly + // from a keylet, or -- read-only only -- from an already-fetched + // SLE). --- + + /** + * Constructor for read-only context (adopt an already-fetched SLE). + * + * There is deliberately no writable equivalent: a writable entry needs + * a Keylet so that newSLE() can still build an entry when none exists, + * and that cannot be recovered from a null SLE. + */ + explicit SLEBase( + SLE::const_pointer sle, + ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + requires(!kIsWritable) + : view_(view), sle_(std::move(sle)), j_(j) + { + XRPL_ASSERT( + !kIsTyped || !sle_ || sle_->getType() == kEntryType, + "xrpl::SLEBase::SLEBase : adopted SLE matches bound entry type"); + } + + /** + * Constructor for read-only context (read from view by keylet) + */ + explicit SLEBase( + Keylet const& key, + ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + requires(!kIsWritable) + : view_(view), sle_(detail::resolveEntry(view, key)), j_(j) + { + XRPL_ASSERT( + !kIsTyped || key.type == kEntryType, + "xrpl::SLEBase::SLEBase : keylet matches bound entry type"); + } + + /** + * Converting constructor: writable → read-only. + * + * Enables implicit conversion from SLEBase to + * SLEBase, so functions taking ReadOnlySLE const& can accept + * WritableSLE. + * + * Constrained to the same entry type (or to a ltANY target, i.e. widening + * a typed entry to a generic ReadOnlySLE). The constraint is load-bearing: + * this constructor is inherited into every per-type entry, and unconstrained + * it would bind any writable entry that slices to SLEBase, so an OfferEntryW + * would convert to an AccountRootEntryR with no cast at the call site. + */ + template + SLEBase(SLEBase const& other) + requires(!kIsWritable && IsWritableView && + (OtherType == EntryType || EntryType == ltANY)) + : view_(other.readView()), sle_(other.rawSle()), j_(other.journal()) + { + } + + /** + * Constructor for writable context (peek from view by keylet) + */ + explicit SLEBase( + Keylet const& key, + ApplyView& view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + requires kIsWritable + : view_(view), key_(key), sle_(view_.peek(key)), j_(j) + { + XRPL_ASSERT( + !kIsTyped || key.type == kEntryType, + "xrpl::SLEBase::SLEBase : keylet matches bound entry type"); + } + + /** + * Constructor for writable context, for call sites that hold an + * ApplyViewContext (peek from ctx.view by keylet). + * + * ctx.tx is not retained: this exists purely so transactors can pass the + * context they already have instead of spelling out ctx.view. If an entry + * ever needs the applying transaction, store it here rather than adding + * another overload. + */ + explicit SLEBase( + Keylet const& key, + ApplyViewContext const& ctx, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + requires kIsWritable + : SLEBase(key, ctx.view, j) + { + } + + // --- Common interface (always available) --- + + /** + * Returns true if the ledger entry exists + */ + [[nodiscard]] bool + exists() const + { + return sle_ != nullptr; + } + + /** + * Explicit conversion to bool for convenient existence checking + */ + explicit + operator bool() const + { + return exists(); + } + + /** + * Returns the underlying SLE for read access. + * + * Prefer operator-> / operator* for field access; this is for the call + * sites that need the shared_ptr itself. + */ + [[nodiscard]] SLE::const_pointer + rawSle() const + { + return sle_; + } + + /** + * Returns the ledger entry type of this entry. + * + * For a per-type entry this is kEntryType, known at compile time and + * valid whether or not the entry exists. Only the generic ReadOnlySLE / + * WritableSLE aliases have to read it back out of the SLE. + * + * @throws std::logic_error for a generic (ltANY) entry if exists() is + * false. + */ + [[nodiscard]] LedgerEntryType + type() const + { + if constexpr (kIsTyped) + { + return kEntryType; + } + else + { + if (!exists()) + Throw("xrpl::SLEBase::type : entry does not exist"); + return sle_->getType(); + } + } + + /** + * Returns the keylet identifying this entry. + * + * Writable entries keep the keylet they were built from, so it is valid + * even before newSLE(). Read-only entries derive it from the SLE, which + * must therefore exist. + * + * @throws std::logic_error for a read-only entry if exists() is false. + */ + [[nodiscard]] Keylet + keylet() const + { + if constexpr (kIsWritable) + { + return key_; + } + else + { + if (!exists()) + Throw("xrpl::SLEBase::keylet : entry does not exist"); + // Take the type from the SLE, not from kEntryType: the adopt-SLE + // constructor's type check is assert-only, so a Release build can + // be holding an SLE whose type disagrees with the binding, and the + // SLE is the one telling the truth. + return Keylet(sle_->getType(), sle_->key()); + } + } + + /** + * Returns the ledger key of this entry. + * + * @throws std::logic_error same as keylet(): for read-only entries, + * if exists() is false. + */ + [[nodiscard]] uint256 + key() const + { + return keylet().key; + } + + /** + * Returns the read view (always available; ApplyView inherits ReadView) + */ + [[nodiscard]] ReadView const& + readView() const + { + return view_; + } + + /** + * Const dereference operators (always available) + * + * @throws std::logic_error if exists() is false. + */ + STLedgerEntry const* + operator->() const + { + if (!exists()) + Throw("xrpl::SLEBase::operator-> : entry does not exist"); + return sle_.get(); + } + + STLedgerEntry const& + operator*() const + { + if (!exists()) + Throw("xrpl::SLEBase::operator* : entry does not exist"); + return *sle_; + } + + // --- Writable interface (compile-time gated) --- + // + // Everything that hands out mutable access (or mutates) is non-const, so + // that a `FooEntryW const&` is as inert as a `FooEntryR`. Use readView() + // when a const entry only needs to inspect the view. + + /** + * Returns the underlying SLE for write access. + * + * Prefer operator-> / operator* for field access; this is for the call + * sites that need the shared_ptr itself. + */ + [[nodiscard]] SlePtrType const& + mutableRawSle() + requires kIsWritable + { + return sle_; + } + + /** + * Returns the apply view for write operations + */ + [[nodiscard]] ApplyView& + applyView() + requires kIsWritable + { + return view_; + } + + /** + * Mutable dereference operators + * + * @throws std::logic_error if exists() is false. + */ + STLedgerEntry* + operator->() + requires kIsWritable + { + if (!exists()) + Throw("xrpl::SLEBase::operator-> : entry does not exist"); + return sle_.get(); + } + + STLedgerEntry& + operator*() + requires kIsWritable + { + if (!exists()) + Throw("xrpl::SLEBase::operator* : entry does not exist"); + return *sle_; + } + + /** + * Inserts the entry into the view. + * + * @throws std::logic_error if exists() is false. + */ + void + insert() + requires kIsWritable + { + if (!exists()) + Throw("xrpl::SLEBase::insert : entry does not exist"); + view_.insert(sle_); + } + + /** + * Erases the entry from the view. + * + * Drops the SLE afterwards, so the entry reports !exists() and any + * further use throws here rather than either throwing from deep inside + * ApplyStateTable or -- worse -- silently succeeding. For an + * entry that already existed, ApplyStateTable::erase keeps holding this + * exact SLE and builds the DeletedNode's FinalFields from it, so a write + * through the entry after erase() would land in transaction metadata + * with no diagnostic at all. + * + * @throws std::logic_error if exists() is false. + */ + void + erase() + requires kIsWritable + { + if (!exists()) + Throw("xrpl::SLEBase::erase : entry does not exist"); + view_.erase(sle_); + sle_ = nullptr; + } + + /** + * @throws std::logic_error if exists() is false. + */ + void + update() + requires kIsWritable + { + if (!exists()) + Throw("xrpl::SLEBase::update : entry does not exist"); + view_.update(sle_); + } + + /** + * @throws std::logic_error if exists() is true: newSLE() would otherwise + * silently discard the SLE already held. + */ + void + newSLE() + requires kIsWritable + { + if (exists()) + Throw("xrpl::SLEBase::newSLE : entry already exists"); + sle_ = std::make_shared(key_); + } + + [[nodiscard]] beast::Journal + journal() const + { + return j_; + } + +protected: + ViewRefType view_; + + // Keylet is only meaningful for writable views, which need it to build an + // SLE that does not exist yet; read-only entries derive it from the SLE. + struct Empty + { + }; + + // No default member initializer: Keylet is not default-constructible, so + // every writable constructor must initialize key_ explicitly. + [[no_unique_address]] + std::conditional_t key_; + + SlePtrType sle_{}; + beast::Journal j_; +}; + +/** + * Generic (any-entry-type) SLE entries. + * + * Use these when the concrete ledger entry type is not known at a given site; + * otherwise prefer the per-type entries (e.g. AccountRootEntry.h), which + * additionally enforce the entry type at compile time. + * + * SLE::const_pointer / SLE::const_ref -> ReadOnlySLE + * SLE::pointer / SLE::ref -> WritableSLE + */ +using ReadOnlySLE = SLEBase; +using WritableSLE = SLEBase; + +static_assert( + !std::is_polymorphic_v && !std::is_polymorphic_v, + "SLEBase must stay a thin value type; it must not acquire a vtable"); + +} // namespace xrpl diff --git a/include/xrpl/ledger/entries/SignerListEntry.h b/include/xrpl/ledger/entries/SignerListEntry.h new file mode 100644 index 0000000000..f6f881cd69 --- /dev/null +++ b/include/xrpl/ledger/entries/SignerListEntry.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +template +class SignerListEntry : public SLEBase +{ +public: + using Base = SLEBase; + + // Inherit base constructors: adopt an existing SLE, or resolve one from a + // Keylet against the view. + using Base::Base; + + explicit SignerListEntry( + AccountID const& account, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::signerList(account), view, j) + { + } +}; + +using SignerListEntryR = SignerListEntry; +using SignerListEntryW = SignerListEntry; + +} // namespace xrpl diff --git a/include/xrpl/ledger/entries/SponsorshipEntry.h b/include/xrpl/ledger/entries/SponsorshipEntry.h new file mode 100644 index 0000000000..98445479d9 --- /dev/null +++ b/include/xrpl/ledger/entries/SponsorshipEntry.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +template +class SponsorshipEntry : public SLEBase +{ +public: + using Base = SLEBase; + + // Inherit base constructors: adopt an existing SLE, or resolve one from a + // Keylet against the view. + using Base::Base; + + explicit SponsorshipEntry( + AccountID const& sponsor, + AccountID const& sponsee, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::sponsorship(sponsor, sponsee), view, j) + { + } +}; + +using SponsorshipEntryR = SponsorshipEntry; +using SponsorshipEntryW = SponsorshipEntry; + +} // namespace xrpl diff --git a/include/xrpl/ledger/entries/TicketEntry.h b/include/xrpl/ledger/entries/TicketEntry.h new file mode 100644 index 0000000000..16d9a9f3d9 --- /dev/null +++ b/include/xrpl/ledger/entries/TicketEntry.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +template +class TicketEntry : public SLEBase +{ +public: + using Base = SLEBase; + + // Inherit base constructors: adopt an existing SLE, or resolve one from a + // Keylet against the view. + using Base::Base; + + explicit TicketEntry( + AccountID const& id, + SeqProxy const& ticketSeq, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::ticket(id, ticketSeq), view, j) + { + } + + explicit TicketEntry( + uint256 const& ticketID, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::ticket(ticketID), view, j) + { + } +}; + +using TicketEntryR = TicketEntry; +using TicketEntryW = TicketEntry; + +} // namespace xrpl diff --git a/include/xrpl/ledger/entries/VaultEntry.h b/include/xrpl/ledger/entries/VaultEntry.h new file mode 100644 index 0000000000..897a9117b1 --- /dev/null +++ b/include/xrpl/ledger/entries/VaultEntry.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl { + +template +class VaultEntry : public SLEBase +{ +public: + using Base = SLEBase; + + // Inherit base constructors: adopt an existing SLE, or resolve one from a + // Keylet against the view. + using Base::Base; + + explicit VaultEntry( + AccountID const& owner, + SeqProxy const& seq, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::vault(owner, seq), view, j) + { + } + + explicit VaultEntry( + uint256 const& vaultID, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::vault(vaultID), view, j) + { + } +}; + +using VaultEntryR = VaultEntry; +using VaultEntryW = VaultEntry; + +} // namespace xrpl diff --git a/include/xrpl/ledger/entries/XChainOwnedClaimIDEntry.h b/include/xrpl/ledger/entries/XChainOwnedClaimIDEntry.h new file mode 100644 index 0000000000..9cec1fcec9 --- /dev/null +++ b/include/xrpl/ledger/entries/XChainOwnedClaimIDEntry.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace xrpl { + +template +class XChainOwnedClaimIDEntry : public SLEBase +{ +public: + using Base = SLEBase; + + // Inherit base constructors: adopt an existing SLE, or resolve one from a + // Keylet against the view. + using Base::Base; + + explicit XChainOwnedClaimIDEntry( + STXChainBridge const& bridge, + std::uint64_t seq, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::xChainClaimID(bridge, seq), view, j) + { + } +}; + +using XChainOwnedClaimIDEntryR = XChainOwnedClaimIDEntry; +using XChainOwnedClaimIDEntryW = XChainOwnedClaimIDEntry; + +} // namespace xrpl diff --git a/include/xrpl/ledger/entries/XChainOwnedCreateAccountClaimIDEntry.h b/include/xrpl/ledger/entries/XChainOwnedCreateAccountClaimIDEntry.h new file mode 100644 index 0000000000..e9494e702f --- /dev/null +++ b/include/xrpl/ledger/entries/XChainOwnedCreateAccountClaimIDEntry.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace xrpl { + +template +class XChainOwnedCreateAccountClaimIDEntry + : public SLEBase +{ +public: + using Base = SLEBase; + + // Inherit base constructors: adopt an existing SLE, or resolve one from a + // Keylet against the view. + using Base::Base; + + explicit XChainOwnedCreateAccountClaimIDEntry( + STXChainBridge const& bridge, + std::uint64_t seq, + Base::ViewRefType view, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) + : Base(keylet::xChainCreateAccountClaimID(bridge, seq), view, j) + { + } +}; + +using XChainOwnedCreateAccountClaimIDEntryR = XChainOwnedCreateAccountClaimIDEntry; +using XChainOwnedCreateAccountClaimIDEntryW = XChainOwnedCreateAccountClaimIDEntry; + +} // namespace xrpl diff --git a/src/tests/libxrpl/CMakeLists.txt b/src/tests/libxrpl/CMakeLists.txt index 9cbfb8ca10..81f4abc126 100644 --- a/src/tests/libxrpl/CMakeLists.txt +++ b/src/tests/libxrpl/CMakeLists.txt @@ -31,6 +31,7 @@ set(test_modules consensus crypto json + ledger nodestore peerfinder protocol diff --git a/src/tests/libxrpl/ledger/AMMEntry.cpp b/src/tests/libxrpl/ledger/AMMEntry.cpp new file mode 100644 index 0000000000..6189d2b3e6 --- /dev/null +++ b/src/tests/libxrpl/ledger/AMMEntry.cpp @@ -0,0 +1,25 @@ +#include + +#include +#include +#include + +#include +#include +#include + +namespace xrpl::test { + +TEST(AMMEntryTests, Constructors) +{ + EntryTestEnv e; + + Asset const xrp{xrpIssue()}; + Asset const usd{IOU("USD", e.alice).issue()}; + + expectKeylet(e, keylet::amm(xrp, usd), "amm(asset, asset)", xrp, usd); + + expectKeylet(e, keylet::amm(e.someID()), "amm(uint256)", e.someID()); +} + +} // namespace xrpl::test diff --git a/src/tests/libxrpl/ledger/AccountRootEntry.cpp b/src/tests/libxrpl/ledger/AccountRootEntry.cpp new file mode 100644 index 0000000000..964d0f2f43 --- /dev/null +++ b/src/tests/libxrpl/ledger/AccountRootEntry.cpp @@ -0,0 +1,21 @@ +#include + +#include + +#include +#include +#include + +namespace xrpl::test { + +TEST(AccountRootEntryTests, Constructors) +{ + EntryTestEnv e; + + expectKeylet(e, keylet::account(e.alice.id()), "account(id)", e.alice.id()); + + expectKeylet( + e, keylet::account(Account("nobody").id()), "account(id) absent", Account("nobody").id()); +} + +} // namespace xrpl::test diff --git a/src/tests/libxrpl/ledger/AmendmentsEntry.cpp b/src/tests/libxrpl/ledger/AmendmentsEntry.cpp new file mode 100644 index 0000000000..ec45b291ec --- /dev/null +++ b/src/tests/libxrpl/ledger/AmendmentsEntry.cpp @@ -0,0 +1,17 @@ +#include + +#include + +#include +#include + +namespace xrpl::test { + +TEST(AmendmentsEntryTests, Constructors) +{ + EntryTestEnv e; + + expectKeylet(e, keylet::amendments(), "amendments()"); +} + +} // namespace xrpl::test diff --git a/src/tests/libxrpl/ledger/BridgeEntry.cpp b/src/tests/libxrpl/ledger/BridgeEntry.cpp new file mode 100644 index 0000000000..bae65ea8b5 --- /dev/null +++ b/src/tests/libxrpl/ledger/BridgeEntry.cpp @@ -0,0 +1,41 @@ +#include + +#include +#include +#include +#include + +#include +#include +#include + +namespace xrpl::test { + +TEST(BridgeEntryTests, Constructors) +{ + EntryTestEnv e; + + STXChainBridge const bridge{e.alice.id(), xrpIssue(), e.bob.id(), IOU("USD", e.bob).issue()}; + + expectKeylet( + e, + keylet::bridge(bridge, STXChainBridge::ChainType::Locking), + "bridge(bridge, Locking)", + bridge, + STXChainBridge::ChainType::Locking); + + expectKeylet( + e, + keylet::bridge(bridge, STXChainBridge::ChainType::Issuing), + "bridge(bridge, Issuing)", + bridge, + STXChainBridge::ChainType::Issuing); + + // The two chain types must not collide, or the assertions above would + // pass with chainType ignored entirely. + EXPECT_NE( + keylet::bridge(bridge, STXChainBridge::ChainType::Locking).key, + keylet::bridge(bridge, STXChainBridge::ChainType::Issuing).key); +} + +} // namespace xrpl::test diff --git a/src/tests/libxrpl/ledger/CheckEntry.cpp b/src/tests/libxrpl/ledger/CheckEntry.cpp new file mode 100644 index 0000000000..da51da047e --- /dev/null +++ b/src/tests/libxrpl/ledger/CheckEntry.cpp @@ -0,0 +1,23 @@ +#include + +#include +#include + +#include +#include + +namespace xrpl::test { + +TEST(CheckEntryTests, Constructors) +{ + EntryTestEnv e; + + SeqProxy const seq = SeqProxy::rawSequence(7); + + expectKeylet( + e, keylet::check(e.alice.id(), seq), "check(id, seq)", e.alice.id(), seq); + + expectKeylet(e, keylet::check(e.someID()), "check(uint256)", e.someID()); +} + +} // namespace xrpl::test diff --git a/src/tests/libxrpl/ledger/CredentialEntry.cpp b/src/tests/libxrpl/ledger/CredentialEntry.cpp new file mode 100644 index 0000000000..ce3f80dca0 --- /dev/null +++ b/src/tests/libxrpl/ledger/CredentialEntry.cpp @@ -0,0 +1,39 @@ +#include + +#include +#include +#include + +#include +#include + +#include + +namespace xrpl::test { + +TEST(CredentialEntryTests, Constructors) +{ + EntryTestEnv e; + + std::string const credTypeStr = "termsandconditions"; + Slice const credType = makeSlice(credTypeStr); + + expectKeylet( + e, + keylet::credential(e.alice.id(), e.bob.id(), credType), + "credential(subject, issuer, credType)", + e.alice.id(), + e.bob.id(), + credType); + + expectKeylet( + e, keylet::credential(e.someID()), "credential(uint256)", e.someID()); + + // Subject and issuer are both AccountIDs, so the assertion above only + // has teeth if their order matters. + EXPECT_NE( + keylet::credential(e.alice.id(), e.bob.id(), credType).key, + keylet::credential(e.bob.id(), e.alice.id(), credType).key); +} + +} // namespace xrpl::test diff --git a/src/tests/libxrpl/ledger/DIDEntry.cpp b/src/tests/libxrpl/ledger/DIDEntry.cpp new file mode 100644 index 0000000000..41b27a486c --- /dev/null +++ b/src/tests/libxrpl/ledger/DIDEntry.cpp @@ -0,0 +1,17 @@ +#include + +#include + +#include +#include + +namespace xrpl::test { + +TEST(DIDEntryTests, Constructors) +{ + EntryTestEnv e; + + expectKeylet(e, keylet::did(e.alice.id()), "did(account)", e.alice.id()); +} + +} // namespace xrpl::test diff --git a/src/tests/libxrpl/ledger/DelegateEntry.cpp b/src/tests/libxrpl/ledger/DelegateEntry.cpp new file mode 100644 index 0000000000..a27299df46 --- /dev/null +++ b/src/tests/libxrpl/ledger/DelegateEntry.cpp @@ -0,0 +1,29 @@ +#include + +#include +#include + +#include +#include + +namespace xrpl::test { + +TEST(DelegateEntryTests, Constructors) +{ + EntryTestEnv e; + + expectKeylet( + e, + keylet::delegate(e.alice.id(), e.bob.id()), + "delegate(account, authorizedAccount)", + e.alice.id(), + e.bob.id()); + + // Both arguments are AccountIDs, so the assertion above only has teeth + // if their order matters. + EXPECT_NE( + keylet::delegate(e.alice.id(), e.bob.id()).key, + keylet::delegate(e.bob.id(), e.alice.id()).key); +} + +} // namespace xrpl::test diff --git a/src/tests/libxrpl/ledger/DepositPreauthEntry.cpp b/src/tests/libxrpl/ledger/DepositPreauthEntry.cpp new file mode 100644 index 0000000000..bba2a58c8a --- /dev/null +++ b/src/tests/libxrpl/ledger/DepositPreauthEntry.cpp @@ -0,0 +1,54 @@ +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +namespace xrpl::test { + +TEST(DepositPreauthEntryTests, Constructors) +{ + EntryTestEnv e; + + std::string const credTypeStr = "termsandconditions"; + std::set> const authCreds{{e.bob.id(), makeSlice(credTypeStr)}}; + + expectKeylet( + e, + keylet::depositPreauth(e.alice.id(), e.bob.id()), + "depositPreauth(owner, preauthorized)", + e.alice.id(), + e.bob.id()); + + expectKeylet( + e, + keylet::depositPreauth(e.alice.id(), authCreds), + "depositPreauth(owner, authCreds)", + e.alice.id(), + authCreds); + + expectKeylet( + e, keylet::depositPreauth(e.someID()), "depositPreauth(uint256)", e.someID()); + + // Owner and preauthorized are both AccountIDs, so the assertion above + // only has teeth if their order matters. + EXPECT_NE( + keylet::depositPreauth(e.alice.id(), e.bob.id()).key, + keylet::depositPreauth(e.bob.id(), e.alice.id()).key); + + // The credential-set overload must not collide with the single-account + // one. + EXPECT_NE( + keylet::depositPreauth(e.alice.id(), authCreds).key, + keylet::depositPreauth(e.alice.id(), e.bob.id()).key); +} + +} // namespace xrpl::test diff --git a/src/tests/libxrpl/ledger/DirectoryNodeEntry.cpp b/src/tests/libxrpl/ledger/DirectoryNodeEntry.cpp new file mode 100644 index 0000000000..0efcb365f6 --- /dev/null +++ b/src/tests/libxrpl/ledger/DirectoryNodeEntry.cpp @@ -0,0 +1,28 @@ +#include + +#include +#include + +#include +#include + +#include + +namespace xrpl::test { + +TEST(DirectoryNodeEntryTests, Constructors) +{ + EntryTestEnv e; + + expectKeylet( + e, keylet::ownerDir(e.alice.id()), "ownerDir(id)", e.alice.id()); + + expectKeylet( + e, keylet::page(e.someID(), 3u), "page(root, index)", e.someID(), std::uint64_t{3}); + + // The two overloads reach different keylet:: functions; a copy-paste + // slip between them would be invisible otherwise. + EXPECT_NE(keylet::ownerDir(e.alice.id()).key, keylet::page(e.someID(), 3u).key); +} + +} // namespace xrpl::test diff --git a/src/tests/libxrpl/ledger/EntryTestHelpers.h b/src/tests/libxrpl/ledger/EntryTestHelpers.h new file mode 100644 index 0000000000..d98b40795e --- /dev/null +++ b/src/tests/libxrpl/ledger/EntryTestHelpers.h @@ -0,0 +1,123 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +namespace xrpl::test { + +/** + * Scaffolding shared by the per-entry-type suites. + * + * Each of those suites needs the same three things: a ledger with a few funded + * accounts, a throwaway ApplyView that is never applied, and some arbitrary + * uint256 to stand in for an object ID. Build one of these per test case -- + * TxTest construction dominates the runtime of these tests by a wide margin, + * and none of the assertions mutate the ledger. + */ +class EntryTestEnv +{ +public: + TxTest env; + Account const alice{"alice"}; + Account const bob{"bob"}; + Account const carol{"carol"}; + + EntryTestEnv() : av_(&fundAndClose(), TapNone) + { + } + + /** + * The closed ledger apply() was built over. Nothing here closes another + * ledger or submits a transaction afterward, so this and apply() never + * diverge. + */ + [[nodiscard]] ReadView const& + read() const + { + return env.getClosedLedger(); + } + + [[nodiscard]] ApplyView& + apply() + { + return av_; + } + + /** + * An arbitrary but stable uint256, for the entry constructors that take + * an object ID directly. Nothing in the ledger has this key, which is the + * point: those overloads should resolve to a non-existent entry. + */ + [[nodiscard]] uint256 + someID() const + { + return read().header().parentHash; + } + +private: + // Runs from the av_ member initializer, so it may only touch env and the + // accounts -- everything declared above av_. + ReadView const& + fundAndClose() + { + env.createAccount(alice, XRP(10'000)); + env.createAccount(bob, XRP(10'000)); + env.createAccount(carol, XRP(10'000)); + env.close(); + return env.getClosedLedger(); + } + + ApplyViewImpl av_; +}; + +/** + * Assert that both flavors of @p Entry built from @p args resolve the ledger + * object that @p expected names. + * + * The entry classes are near identical, so the defect they invite is a + * copy-paste one: a constructor that reaches the wrong keylet:: function, or + * that transposes two same-typed arguments. Comparing against an independently + * spelled-out keylet at the call site catches exactly that. + * + * @p what names the overload under test, so a failure says which one broke. + */ +template