Compare commits

...

29 Commits

Author SHA1 Message Date
Mayukha Vadari
df723b9e52 fix clang-tidy findings in the new SLEBase throw-coverage test
const-correctness on env, and an explicit <stdexcept> include for
std::logic_error.
2026-08-21 11:00:25 -04:00
Mayukha Vadari
282de96d12 Merge remote-tracking branch 'upstream/develop' into mvadari/rearch/init 2026-08-21 09:52:31 -04:00
Mayukha Vadari
331ab53629 respond to review feedback from bthomee: use Throw<std::logic_error>
Use Throw<std::logic_error> instead of a bare throw so exceptions are
logged consistently with the rest of the codebase, and extend the same
throwing behavior to the mutators and dereference operators that
previously only asserted on a missing entry. Add test coverage for the
missing-entry throw paths.
2026-08-21 09:52:17 -04:00
Mayukha Vadari
6d77edc029 Merge branch 'develop' into mvadari/rearch/init 2026-08-20 14:10:17 -04:00
Mayukha Vadari
f4e0f92761 respond to review feedback from bthomee and ximinez
- fix sle_ptr_type to use SLE::pointer instead of shared_ptr<SLE>
- throw std::logic_error instead of assert-only checks in type()/keylet()/key()
- remove canModify(), use exists() consistently
- move constructors to the top of the class
- fix wording in the converting-constructor doc comment
- keep the exhaustive instantiation block in SLEBase_test.cpp permanently
2026-08-20 13:14:04 -04:00
Mayukha Vadari
ddeed941af rename sle() to rawSle() and mutableSle() to mutableRawSle()
"raw" says what these hand back: the shared_ptr itself, rather than field
access through operator-> / operator*. Doc comments now point at those for the
common case.

Renamed the test's detection concept to HasMutableRawSle to match. The sle_
member keeps its name.
2026-08-13 14:26:26 -04:00
Mayukha Vadari
46b72e693c fix clang-tidy findings in the entry test suites
- namespace xrpl { namespace test { -> namespace xrpl::test, matching the rest
  of src/test (modernize-concat-nested-namespaces).
- EntryTestEnv no longer holds its ApplyViewImpl in a std::optional. Funding
  moved to a private helper called from the member initializer list, so both
  members are initialized once and there is no unchecked optional access.
- [[nodiscard]] on read(), apply() and someID(); const on the writable entry in
  expectKeylet and on fromView in SLEBase_test.
- ApplyViewContext built with designated initializers.
- Prune the includes the generated suites carried but never used. Applied via
  run-clang-tidy -export-fixes with the repo hook's path canonicalisation, then
  fix_include_style.py; UintTypes.h regrouped by hand.
2026-08-13 12:45:34 -04:00
Mayukha Vadari
b1dfde313a test: hold the ledger the throwaway apply views point into
OpenLedger::current() returns a copy of the shared_ptr it holds internally, so
ApplyViewImpl av(&*env.current(), TapNone) survives only because the app keeps
that reference. A close after the fact reassigns it, frees the OpenView, and
leaves av dangling. No test does that today, but the five sites had nothing
stopping one from being added.

Bind the ledger to a local first. This does not make a later close correct --
av would then describe the pre-close ledger -- but it takes use-after-free off
the table and matches what EntryTestEnv already does.
2026-08-13 12:11:48 -04:00
Mayukha Vadari
6238bb6e99 test: cover resolveEntry's peek path
detail::resolveEntry has two branches and only one was reachable from the
tests. OpenView derives from ReadView but not ApplyView, so the dynamic_cast
always failed for entries built over *env.current() and the peek() branch --
which carries the const_cast, its safety invariant, and the shared-SLE
rationale -- had no coverage at all.

Add a case that builds a read-only entry over an ApplyViewImpl, where the
dynamic_cast succeeds, and assert what the peek path exists to guarantee: the
entry shares the view's SLE rather than the base ledger's copy, so a write
through any other entry over that view is visible through it. Verified by
removing the branch, which fails exactly these three assertions.

Also:

- EntryTestEnv now holds the ledger apply() was built over instead of calling
  env.current() per access. Those could diverge if a suite ever closed a
  ledger, and holding it keeps the raw pointer inside av_ valid.

- Drop expectKeylet's r.type() check. For a typed entry type() returns
  kEntryType, so it was the kEntryType assertion spelled twice.
2026-08-13 11:30:45 -04:00
Mayukha Vadari
2791e6e14b add tests, respond to feedback 2026-08-12 17:21:26 -04:00
Mayukha Vadari
4e7e417891 fix clang-tidy include-cleaner findings
Drop unused <cstdint> from the ledger entry wrapper headers and add the
missing MPTAmount.h include to EscrowHelpers.h.

SLEWrapper_test.cpp instantiates every wrapper through the LEDGER_ENTRY
macro, which include-cleaner cannot see, so mark those includes with
IWYU pragma: keep instead of removing them.
2026-08-12 13:12:36 -04:00
Mayukha Vadari
d0008a36f6 fix build issues 2026-08-11 17:26:40 -04:00
Mayukha Vadari
4cc2d3fc79 Merge branch 'develop' into mvadari/rearch/init 2026-08-11 15:27:51 -04:00
Mayukha Vadari
f071af7a5b fix clang-tidy better 2026-08-11 15:26:26 -04:00
Mayukha Vadari
2981b97d5e Merge branch 'develop' of https://github.com/XRPLF/rippled into mvadari/rearch/init 2026-08-11 14:34:10 -04:00
Mayukha Vadari
dc75de050d fix clang-tidy 2026-08-11 14:29:56 -04:00
Mayukha Vadari
3ff036dc4c fix clang-tidy issues 2026-08-11 14:25:58 -04:00
Mayukha Vadari
19f742aac3 improve setup based on comments from bart 2026-08-07 13:33:06 -04:00
Mayukha Vadari
0900ce7191 Merge branch 'develop' of https://github.com/XRPLF/rippled into mvadari/rearch/init 2026-08-04 13:09:04 -04:00
Mayukha Vadari
47b203581e fix clang-tidy issues 2026-08-03 16:34:10 -04:00
Mayukha Vadari
2c5fa02692 Merge branch 'develop' of https://github.com/XRPLF/rippled into mvadari/rearch/init 2026-08-03 16:06:38 -04:00
Mayukha Vadari
755b9669e1 fix clang-tidy weirdness 2026-07-30 13:43:11 -04:00
Mayukha Vadari
1c39beca4b fix clang-tidy issues 2026-07-30 13:35:07 -04:00
Mayukha Vadari
bea1d0d2ba Merge branch 'develop' into mvadari/rearch/init 2026-07-30 13:03:49 -04:00
Mayukha Vadari
9aed87ea33 add stuff from downstream 2026-07-30 13:00:32 -04:00
Mayukha Vadari
fb809c2f43 respond to comments 2026-07-28 22:46:50 -04:00
Mayukha Vadari
1a151c8749 fix clang-tidy (hopefully) 2026-07-28 12:11:14 -04:00
Mayukha Vadari
cb90ba0a3e fix clang-tidy issues 2026-07-28 11:59:37 -04:00
Mayukha Vadari
f34585d317 refactor: Add wrapper classes for all SLEs 2026-07-28 11:51:09 -04:00
65 changed files with 3757 additions and 0 deletions

View File

@@ -0,0 +1,45 @@
#pragma once
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/SLEBase.h>
#include <xrpl/protocol/Asset.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
namespace xrpl {
template <typename ViewT>
class AMMEntry : public SLEBase<ViewT, ltAMM>
{
public:
using Base = SLEBase<ViewT, ltAMM>;
// 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::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::amm(issue1, issue2), view, j)
{
}
explicit AMMEntry(
uint256 const& ammID,
Base::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::amm(ammID), view, j)
{
}
};
using RAMMEntry = AMMEntry<ReadView>;
using WAMMEntry = AMMEntry<ApplyView>;
} // namespace xrpl

View File

@@ -0,0 +1,35 @@
#pragma once
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/SLEBase.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
namespace xrpl {
template <typename ViewT>
class AccountRootEntry : public SLEBase<ViewT, ltACCOUNT_ROOT>
{
public:
using Base = SLEBase<ViewT, ltACCOUNT_ROOT>;
// 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::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::account(id), view, j)
{
}
};
using RAccountRootEntry = AccountRootEntry<ReadView>;
using WAccountRootEntry = AccountRootEntry<ApplyView>;
} // namespace xrpl

View File

@@ -0,0 +1,33 @@
#pragma once
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/SLEBase.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
namespace xrpl {
template <typename ViewT>
class AmendmentsEntry : public SLEBase<ViewT, ltAMENDMENTS>
{
public:
using Base = SLEBase<ViewT, ltAMENDMENTS>;
// Inherit base constructors: adopt an existing SLE, or resolve one from a
// Keylet against the view.
using Base::Base;
explicit AmendmentsEntry(
Base::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::amendments(), view, j)
{
}
};
using RAmendmentsEntry = AmendmentsEntry<ReadView>;
using WAmendmentsEntry = AmendmentsEntry<ApplyView>;
} // namespace xrpl

View File

@@ -0,0 +1,36 @@
#pragma once
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/SLEBase.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/STXChainBridge.h>
namespace xrpl {
template <typename ViewT>
class BridgeEntry : public SLEBase<ViewT, ltBRIDGE>
{
public:
using Base = SLEBase<ViewT, ltBRIDGE>;
// 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::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::bridge(bridge, chainType), view, j)
{
}
};
using RBridgeEntry = BridgeEntry<ReadView>;
using WBridgeEntry = BridgeEntry<ApplyView>;
} // namespace xrpl

View File

@@ -0,0 +1,46 @@
#pragma once
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/SLEBase.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/SeqProxy.h>
namespace xrpl {
template <typename ViewT>
class CheckEntry : public SLEBase<ViewT, ltCHECK>
{
public:
using Base = SLEBase<ViewT, ltCHECK>;
// 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::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::check(id, seq), view, j)
{
}
explicit CheckEntry(
uint256 const& checkID,
Base::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::check(checkID), view, j)
{
}
};
using RCheckEntry = CheckEntry<ReadView>;
using WCheckEntry = CheckEntry<ApplyView>;
} // namespace xrpl

View File

@@ -0,0 +1,47 @@
#pragma once
#include <xrpl/basics/Slice.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/SLEBase.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
namespace xrpl {
template <typename ViewT>
class CredentialEntry : public SLEBase<ViewT, ltCREDENTIAL>
{
public:
using Base = SLEBase<ViewT, ltCREDENTIAL>;
// 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::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::credential(subject, issuer, credType), view, j)
{
}
explicit CredentialEntry(
uint256 const& credentialID,
Base::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::credential(credentialID), view, j)
{
}
};
using RCredentialEntry = CredentialEntry<ReadView>;
using WCredentialEntry = CredentialEntry<ApplyView>;
} // namespace xrpl

View File

@@ -0,0 +1,35 @@
#pragma once
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/SLEBase.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
namespace xrpl {
template <typename ViewT>
class DIDEntry : public SLEBase<ViewT, ltDID>
{
public:
using Base = SLEBase<ViewT, ltDID>;
// 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::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::did(account), view, j)
{
}
};
using RDIDEntry = DIDEntry<ReadView>;
using WDIDEntry = DIDEntry<ApplyView>;
} // namespace xrpl

View File

@@ -0,0 +1,36 @@
#pragma once
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/SLEBase.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
namespace xrpl {
template <typename ViewT>
class DelegateEntry : public SLEBase<ViewT, ltDELEGATE>
{
public:
using Base = SLEBase<ViewT, ltDELEGATE>;
// 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::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::delegate(account, authorizedAccount), view, j)
{
}
};
using RDelegateEntry = DelegateEntry<ReadView>;
using WDelegateEntry = DelegateEntry<ApplyView>;
} // namespace xrpl

View File

@@ -0,0 +1,58 @@
#pragma once
#include <xrpl/basics/Slice.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/SLEBase.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <set>
#include <utility>
namespace xrpl {
template <typename ViewT>
class DepositPreauthEntry : public SLEBase<ViewT, ltDEPOSIT_PREAUTH>
{
public:
using Base = SLEBase<ViewT, ltDEPOSIT_PREAUTH>;
// 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::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::depositPreauth(owner, preauthorized), view, j)
{
}
explicit DepositPreauthEntry(
AccountID const& owner,
std::set<std::pair<AccountID, Slice>> const& authCreds,
Base::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::depositPreauth(owner, authCreds), view, j)
{
}
explicit DepositPreauthEntry(
uint256 const& preauthID,
Base::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::depositPreauth(preauthID), view, j)
{
}
};
using RDepositPreauthEntry = DepositPreauthEntry<ReadView>;
using WDepositPreauthEntry = DepositPreauthEntry<ApplyView>;
} // namespace xrpl

View File

@@ -0,0 +1,50 @@
#pragma once
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/SLEBase.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <cstdint>
namespace xrpl {
template <typename ViewT>
class DirectoryNodeEntry : public SLEBase<ViewT, ltDIR_NODE>
{
public:
using Base = SLEBase<ViewT, ltDIR_NODE>;
// 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::view_ref_type 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::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::page(root, index), view, j)
{
}
};
using RDirectoryNodeEntry = DirectoryNodeEntry<ReadView>;
using WDirectoryNodeEntry = DirectoryNodeEntry<ApplyView>;
} // namespace xrpl

View File

@@ -0,0 +1,37 @@
#pragma once
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/SLEBase.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/SeqProxy.h>
namespace xrpl {
template <typename ViewT>
class EscrowEntry : public SLEBase<ViewT, ltESCROW>
{
public:
using Base = SLEBase<ViewT, ltESCROW>;
// 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::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::escrow(src, seq), view, j)
{
}
};
using REscrowEntry = EscrowEntry<ReadView>;
using WEscrowEntry = EscrowEntry<ApplyView>;
} // namespace xrpl

View File

@@ -0,0 +1,33 @@
#pragma once
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/SLEBase.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
namespace xrpl {
template <typename ViewT>
class FeeSettingsEntry : public SLEBase<ViewT, ltFEE_SETTINGS>
{
public:
using Base = SLEBase<ViewT, ltFEE_SETTINGS>;
// Inherit base constructors: adopt an existing SLE, or resolve one from a
// Keylet against the view.
using Base::Base;
explicit FeeSettingsEntry(
Base::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::feeSettings(), view, j)
{
}
};
using RFeeSettingsEntry = FeeSettingsEntry<ReadView>;
using WFeeSettingsEntry = FeeSettingsEntry<ApplyView>;
} // namespace xrpl

View File

@@ -0,0 +1,33 @@
#pragma once
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/SLEBase.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
namespace xrpl {
template <typename ViewT>
class LedgerHashesEntry : public SLEBase<ViewT, ltLEDGER_HASHES>
{
public:
using Base = SLEBase<ViewT, ltLEDGER_HASHES>;
// Inherit base constructors: adopt an existing SLE, or resolve one from a
// Keylet against the view.
using Base::Base;
explicit LedgerHashesEntry(
Base::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::skip(), view, j)
{
}
};
using RLedgerHashesEntry = LedgerHashesEntry<ReadView>;
using WLedgerHashesEntry = LedgerHashesEntry<ApplyView>;
} // namespace xrpl

View File

@@ -0,0 +1,46 @@
#pragma once
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/SLEBase.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/SeqProxy.h>
namespace xrpl {
template <typename ViewT>
class LoanBrokerEntry : public SLEBase<ViewT, ltLOAN_BROKER>
{
public:
using Base = SLEBase<ViewT, ltLOAN_BROKER>;
// 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::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::loanBroker(owner, seq), view, j)
{
}
explicit LoanBrokerEntry(
uint256 const& loanBrokerID,
Base::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::loanBroker(loanBrokerID), view, j)
{
}
};
using RLoanBrokerEntry = LoanBrokerEntry<ReadView>;
using WLoanBrokerEntry = LoanBrokerEntry<ApplyView>;
} // namespace xrpl

View File

@@ -0,0 +1,45 @@
#pragma once
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/SLEBase.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/SeqProxy.h>
namespace xrpl {
template <typename ViewT>
class LoanEntry : public SLEBase<ViewT, ltLOAN>
{
public:
using Base = SLEBase<ViewT, ltLOAN>;
// 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::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::loan(loanBrokerID, loanSeq), view, j)
{
}
explicit LoanEntry(
uint256 const& loanID,
Base::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::loan(loanID), view, j)
{
}
};
using RLoanEntry = LoanEntry<ReadView>;
using WLoanEntry = LoanEntry<ApplyView>;
} // namespace xrpl

View File

@@ -0,0 +1,55 @@
#pragma once
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/SLEBase.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/UintTypes.h>
namespace xrpl {
template <typename ViewT>
class MPTokenEntry : public SLEBase<ViewT, ltMPTOKEN>
{
public:
using Base = SLEBase<ViewT, ltMPTOKEN>;
// 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::view_ref_type 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::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::mptoken(issuanceKey, holder), view, j)
{
}
explicit MPTokenEntry(
uint256 const& mptokenKey,
Base::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::mptoken(mptokenKey), view, j)
{
}
};
using RMPTokenEntry = MPTokenEntry<ReadView>;
using WMPTokenEntry = MPTokenEntry<ApplyView>;
} // namespace xrpl

View File

@@ -0,0 +1,56 @@
#pragma once
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/SLEBase.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/UintTypes.h>
#include <cstdint>
namespace xrpl {
template <typename ViewT>
class MPTokenIssuanceEntry : public SLEBase<ViewT, ltMPTOKEN_ISSUANCE>
{
public:
using Base = SLEBase<ViewT, ltMPTOKEN_ISSUANCE>;
// 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::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::mptokenIssuance(makeMptID(seq, issuer)), view, j)
{
}
explicit MPTokenIssuanceEntry(
MPTID const& issuanceID,
Base::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::mptokenIssuance(issuanceID), view, j)
{
}
explicit MPTokenIssuanceEntry(
uint256 const& issuanceKey,
Base::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::mptokenIssuance(issuanceKey), view, j)
{
}
};
using RMPTokenIssuanceEntry = MPTokenIssuanceEntry<ReadView>;
using WMPTokenIssuanceEntry = MPTokenIssuanceEntry<ApplyView>;
} // namespace xrpl

View File

@@ -0,0 +1,46 @@
#pragma once
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/SLEBase.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/SeqProxy.h>
namespace xrpl {
template <typename ViewT>
class NFTokenOfferEntry : public SLEBase<ViewT, ltNFTOKEN_OFFER>
{
public:
using Base = SLEBase<ViewT, ltNFTOKEN_OFFER>;
// 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::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::nftokenOffer(owner, seq), view, j)
{
}
explicit NFTokenOfferEntry(
uint256 const& offerID,
Base::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::nftokenOffer(offerID), view, j)
{
}
};
using RNFTokenOfferEntry = NFTokenOfferEntry<ReadView>;
using WNFTokenOfferEntry = NFTokenOfferEntry<ApplyView>;
} // namespace xrpl

View File

@@ -0,0 +1,37 @@
#pragma once
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/SLEBase.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Keylet.h>
#include <xrpl/protocol/LedgerFormats.h>
namespace xrpl {
template <typename ViewT>
class NFTokenPageEntry : public SLEBase<ViewT, ltNFTOKEN_PAGE>
{
public:
using Base = SLEBase<ViewT, ltNFTOKEN_PAGE>;
// 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::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::nftokenPage(page, token), view, j)
{
}
};
using RNFTokenPageEntry = NFTokenPageEntry<ReadView>;
using WNFTokenPageEntry = NFTokenPageEntry<ApplyView>;
} // namespace xrpl

View File

@@ -0,0 +1,33 @@
#pragma once
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/SLEBase.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
namespace xrpl {
template <typename ViewT>
class NegativeUNLEntry : public SLEBase<ViewT, ltNEGATIVE_UNL>
{
public:
using Base = SLEBase<ViewT, ltNEGATIVE_UNL>;
// Inherit base constructors: adopt an existing SLE, or resolve one from a
// Keylet against the view.
using Base::Base;
explicit NegativeUNLEntry(
Base::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::negativeUNL(), view, j)
{
}
};
using RNegativeUNLEntry = NegativeUNLEntry<ReadView>;
using WNegativeUNLEntry = NegativeUNLEntry<ApplyView>;
} // namespace xrpl

View File

@@ -0,0 +1,46 @@
#pragma once
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/SLEBase.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/SeqProxy.h>
namespace xrpl {
template <typename ViewT>
class OfferEntry : public SLEBase<ViewT, ltOFFER>
{
public:
using Base = SLEBase<ViewT, ltOFFER>;
// 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::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::offer(id, seq), view, j)
{
}
explicit OfferEntry(
uint256 const& offerID,
Base::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::offer(offerID), view, j)
{
}
};
using ROfferEntry = OfferEntry<ReadView>;
using WOfferEntry = OfferEntry<ApplyView>;
} // namespace xrpl

View File

@@ -0,0 +1,38 @@
#pragma once
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/SLEBase.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <cstdint>
namespace xrpl {
template <typename ViewT>
class OracleEntry : public SLEBase<ViewT, ltORACLE>
{
public:
using Base = SLEBase<ViewT, ltORACLE>;
// 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::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::oracle(account, documentID), view, j)
{
}
};
using ROracleEntry = OracleEntry<ReadView>;
using WOracleEntry = OracleEntry<ApplyView>;
} // namespace xrpl

View File

@@ -0,0 +1,38 @@
#pragma once
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/SLEBase.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/SeqProxy.h>
namespace xrpl {
template <typename ViewT>
class PayChannelEntry : public SLEBase<ViewT, ltPAYCHAN>
{
public:
using Base = SLEBase<ViewT, ltPAYCHAN>;
// 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::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::payChannel(src, dst, seq), view, j)
{
}
};
using RPayChannelEntry = PayChannelEntry<ReadView>;
using WPayChannelEntry = PayChannelEntry<ApplyView>;
} // namespace xrpl

View File

@@ -0,0 +1,46 @@
#pragma once
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/SLEBase.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/SeqProxy.h>
namespace xrpl {
template <typename ViewT>
class PermissionedDomainEntry : public SLEBase<ViewT, ltPERMISSIONED_DOMAIN>
{
public:
using Base = SLEBase<ViewT, ltPERMISSIONED_DOMAIN>;
// 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::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::permissionedDomain(account, seq), view, j)
{
}
explicit PermissionedDomainEntry(
uint256 const& domainID,
Base::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::permissionedDomain(domainID), view, j)
{
}
};
using RPermissionedDomainEntry = PermissionedDomainEntry<ReadView>;
using WPermissionedDomainEntry = PermissionedDomainEntry<ApplyView>;
} // namespace xrpl

View File

@@ -0,0 +1,48 @@
#pragma once
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/SLEBase.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Issue.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/UintTypes.h>
namespace xrpl {
template <typename ViewT>
class RippleStateEntry : public SLEBase<ViewT, ltRIPPLE_STATE>
{
public:
using Base = SLEBase<ViewT, ltRIPPLE_STATE>;
// 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::view_ref_type 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::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::trustLine(id, issue), view, j)
{
}
};
using RRippleStateEntry = RippleStateEntry<ReadView>;
using WRippleStateEntry = RippleStateEntry<ApplyView>;
} // namespace xrpl

View File

@@ -0,0 +1,493 @@
#pragma once
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/contract.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/protocol/Keylet.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/STLedgerEntry.h>
#include <concepts>
#include <memory>
#include <stdexcept>
#include <type_traits>
#include <utility>
namespace xrpl {
// Concept to distinguish read-only vs writable view types
template <typename V>
concept WritableView = std::derived_from<V, ApplyView>;
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 safe only because xrpld never instantiates a ReadView as a
* genuinely const object -- every view is a non-const object that some
* call sites merely observe through a const reference. If an actually
* const-qualified view type is ever introduced (an immutable snapshot,
* say), this becomes undefined behavior and must be revisited.
*
* @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)
{
// Views are never const objects; the read-only entry only holds a const
// reference because it does not itself modify the view.
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
if (auto const applyView = dynamic_cast<ApplyView*>(const_cast<ReadView*>(&view)))
return applyView->peek(key);
return view.read(key);
}
} // namespace detail
/**
* View-parameterized base class for all ledger entries.
*
* SLEBase<ReadView> — read-only: holds shared_ptr<SLE const> + ReadView const&
* SLEBase<ApplyView> — writable: holds shared_ptr<SLE> + 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 <typename ViewT, LedgerEntryType EntryType = ltANY>
class SLEBase
{
public:
static constexpr bool kIsWritable = WritableView<ViewT>;
// 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 sle_ptr_type = std::conditional_t<kIsWritable, SLE::pointer, SLE::const_pointer>;
// View reference type: ApplyView& for writable, ReadView const& for
// read-only
using view_ref_type = std::conditional_t<kIsWritable, ApplyView&, ReadView const&>;
// 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&) = 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,
view_ref_type 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,
view_ref_type 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<ApplyView> to
* SLEBase<ReadView>, 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 a WOfferEntry
* would convert to an RAccountRootEntry with no cast at the call site.
*/
template <typename OtherViewT, LedgerEntryType OtherType>
SLEBase(SLEBase<OtherViewT, OtherType> const& other)
requires(!kIsWritable && WritableView<OtherViewT> &&
(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<std::logic_error>("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<std::logic_error>("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<std::logic_error>("xrpl::SLEBase::operator-> : entry does not exist");
return sle_.get();
}
STLedgerEntry const&
operator*() const
{
if (!exists())
Throw<std::logic_error>("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 `WFooEntry const&` is as inert as a `RFooEntry`. 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]] sle_ptr_type 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<std::logic_error>("xrpl::SLEBase::operator-> : entry does not exist");
return sle_.get();
}
STLedgerEntry&
operator*()
requires kIsWritable
{
if (!exists())
Throw<std::logic_error>("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<std::logic_error>("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 trips an assertion 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<std::logic_error>("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<std::logic_error>("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<std::logic_error>("xrpl::SLEBase::newSLE : entry already exists");
sle_ = std::make_shared<SLE>(key_);
}
[[nodiscard]] beast::Journal
journal() const
{
return j_;
}
protected:
view_ref_type 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<kIsWritable, Keylet, Empty> key_;
sle_ptr_type 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<ReadView>;
using WritableSLE = SLEBase<ApplyView>;
static_assert(
!std::is_polymorphic_v<ReadOnlySLE> && !std::is_polymorphic_v<WritableSLE>,
"SLEBase must stay a thin value type; it must not acquire a vtable");
} // namespace xrpl

View File

@@ -0,0 +1,35 @@
#pragma once
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/SLEBase.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
namespace xrpl {
template <typename ViewT>
class SignerListEntry : public SLEBase<ViewT, ltSIGNER_LIST>
{
public:
using Base = SLEBase<ViewT, ltSIGNER_LIST>;
// 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::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::signerList(account), view, j)
{
}
};
using RSignerListEntry = SignerListEntry<ReadView>;
using WSignerListEntry = SignerListEntry<ApplyView>;
} // namespace xrpl

View File

@@ -0,0 +1,36 @@
#pragma once
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/SLEBase.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
namespace xrpl {
template <typename ViewT>
class SponsorshipEntry : public SLEBase<ViewT, ltSPONSORSHIP>
{
public:
using Base = SLEBase<ViewT, ltSPONSORSHIP>;
// 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::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::sponsorship(sponsor, sponsee), view, j)
{
}
};
using RSponsorshipEntry = SponsorshipEntry<ReadView>;
using WSponsorshipEntry = SponsorshipEntry<ApplyView>;
} // namespace xrpl

View File

@@ -0,0 +1,46 @@
#pragma once
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/SLEBase.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/SeqProxy.h>
namespace xrpl {
template <typename ViewT>
class TicketEntry : public SLEBase<ViewT, ltTICKET>
{
public:
using Base = SLEBase<ViewT, ltTICKET>;
// 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::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::ticket(id, ticketSeq), view, j)
{
}
explicit TicketEntry(
uint256 const& ticketID,
Base::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::ticket(ticketID), view, j)
{
}
};
using RTicketEntry = TicketEntry<ReadView>;
using WTicketEntry = TicketEntry<ApplyView>;
} // namespace xrpl

View File

@@ -0,0 +1,46 @@
#pragma once
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/SLEBase.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/SeqProxy.h>
namespace xrpl {
template <typename ViewT>
class VaultEntry : public SLEBase<ViewT, ltVAULT>
{
public:
using Base = SLEBase<ViewT, ltVAULT>;
// 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::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::vault(owner, seq), view, j)
{
}
explicit VaultEntry(
uint256 const& vaultID,
Base::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::vault(vaultID), view, j)
{
}
};
using RVaultEntry = VaultEntry<ReadView>;
using WVaultEntry = VaultEntry<ApplyView>;
} // namespace xrpl

View File

@@ -0,0 +1,38 @@
#pragma once
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/SLEBase.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/STXChainBridge.h>
#include <cstdint>
namespace xrpl {
template <typename ViewT>
class XChainOwnedClaimIDEntry : public SLEBase<ViewT, ltXCHAIN_OWNED_CLAIM_ID>
{
public:
using Base = SLEBase<ViewT, ltXCHAIN_OWNED_CLAIM_ID>;
// 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::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::xChainClaimID(bridge, seq), view, j)
{
}
};
using RXChainOwnedClaimIDEntry = XChainOwnedClaimIDEntry<ReadView>;
using WXChainOwnedClaimIDEntry = XChainOwnedClaimIDEntry<ApplyView>;
} // namespace xrpl

View File

@@ -0,0 +1,39 @@
#pragma once
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/SLEBase.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/STXChainBridge.h>
#include <cstdint>
namespace xrpl {
template <typename ViewT>
class XChainOwnedCreateAccountClaimIDEntry
: public SLEBase<ViewT, ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID>
{
public:
using Base = SLEBase<ViewT, ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID>;
// 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::view_ref_type view,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: Base(keylet::xChainCreateAccountClaimID(bridge, seq), view, j)
{
}
};
using RXChainOwnedCreateAccountClaimIDEntry = XChainOwnedCreateAccountClaimIDEntry<ReadView>;
using WXChainOwnedCreateAccountClaimIDEntry = XChainOwnedCreateAccountClaimIDEntry<ApplyView>;
} // namespace xrpl

View File

@@ -0,0 +1,40 @@
#include <test/jtx/Account.h>
#include <test/ledger/EntryTestHelpers.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/ledger/helpers/AMMEntry.h>
#include <xrpl/protocol/Asset.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Issue.h>
namespace xrpl::test {
class AMMEntry_test : public beast::unit_test::Suite
{
void
testConstructors()
{
testcase("constructors");
using namespace jtx;
EntryTestEnv e(*this);
Asset const xrp{xrpIssue()};
Asset const usd{e.alice["USD"].issue()};
expectKeylet<AMMEntry>(*this, e, keylet::amm(xrp, usd), "amm(asset, asset)", xrp, usd);
expectKeylet<AMMEntry>(*this, e, keylet::amm(e.someID()), "amm(uint256)", e.someID());
}
public:
void
run() override
{
testConstructors();
}
};
BEAST_DEFINE_TESTSUITE(AMMEntry, ledger, xrpl);
} // namespace xrpl::test

View File

@@ -0,0 +1,41 @@
#include <test/jtx/Account.h>
#include <test/ledger/EntryTestHelpers.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/ledger/helpers/AccountRootEntry.h>
#include <xrpl/protocol/Indexes.h>
namespace xrpl::test {
class AccountRootEntry_test : public beast::unit_test::Suite
{
void
testConstructors()
{
testcase("constructors");
using namespace jtx;
EntryTestEnv e(*this);
expectKeylet<AccountRootEntry>(
*this, e, keylet::account(e.alice.id()), "account(id)", e.alice.id());
expectKeylet<AccountRootEntry>(
*this,
e,
keylet::account(Account("nobody").id()),
"account(id) absent",
Account("nobody").id());
}
public:
void
run() override
{
testConstructors();
}
};
BEAST_DEFINE_TESTSUITE(AccountRootEntry, ledger, xrpl);
} // namespace xrpl::test

View File

@@ -0,0 +1,32 @@
#include <test/ledger/EntryTestHelpers.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/ledger/helpers/AmendmentsEntry.h>
#include <xrpl/protocol/Indexes.h>
namespace xrpl::test {
class AmendmentsEntry_test : public beast::unit_test::Suite
{
void
testConstructors()
{
testcase("constructors");
using namespace jtx;
EntryTestEnv e(*this);
expectKeylet<AmendmentsEntry>(*this, e, keylet::amendments(), "amendments()");
}
public:
void
run() override
{
testConstructors();
}
};
BEAST_DEFINE_TESTSUITE(AmendmentsEntry, ledger, xrpl);
} // namespace xrpl::test

View File

@@ -0,0 +1,58 @@
#include <test/jtx/Account.h>
#include <test/ledger/EntryTestHelpers.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/ledger/helpers/BridgeEntry.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Issue.h>
#include <xrpl/protocol/Keylet.h>
#include <xrpl/protocol/STXChainBridge.h>
namespace xrpl::test {
class BridgeEntry_test : public beast::unit_test::Suite
{
void
testConstructors()
{
testcase("constructors");
using namespace jtx;
EntryTestEnv e(*this);
STXChainBridge const bridge{e.alice.id(), xrpIssue(), e.bob.id(), e.bob["USD"].issue()};
expectKeylet<BridgeEntry>(
*this,
e,
keylet::bridge(bridge, STXChainBridge::ChainType::Locking),
"bridge(bridge, Locking)",
bridge,
STXChainBridge::ChainType::Locking);
expectKeylet<BridgeEntry>(
*this,
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.
BEAST_EXPECT(
keylet::bridge(bridge, STXChainBridge::ChainType::Locking).key !=
keylet::bridge(bridge, STXChainBridge::ChainType::Issuing).key);
}
public:
void
run() override
{
testConstructors();
}
};
BEAST_DEFINE_TESTSUITE(BridgeEntry, ledger, xrpl);
} // namespace xrpl::test

View File

@@ -0,0 +1,39 @@
#include <test/jtx/Account.h>
#include <test/ledger/EntryTestHelpers.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/ledger/helpers/CheckEntry.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/SeqProxy.h>
namespace xrpl::test {
class CheckEntry_test : public beast::unit_test::Suite
{
void
testConstructors()
{
testcase("constructors");
using namespace jtx;
EntryTestEnv e(*this);
SeqProxy const seq = SeqProxy::rawSequence(7);
expectKeylet<CheckEntry>(
*this, e, keylet::check(e.alice.id(), seq), "check(id, seq)", e.alice.id(), seq);
expectKeylet<CheckEntry>(*this, e, keylet::check(e.someID()), "check(uint256)", e.someID());
}
public:
void
run() override
{
testConstructors();
}
};
BEAST_DEFINE_TESTSUITE(CheckEntry, ledger, xrpl);
} // namespace xrpl::test

View File

@@ -0,0 +1,56 @@
#include <test/jtx/Account.h>
#include <test/ledger/EntryTestHelpers.h>
#include <xrpl/basics/Slice.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/ledger/helpers/CredentialEntry.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Keylet.h>
#include <string>
namespace xrpl::test {
class CredentialEntry_test : public beast::unit_test::Suite
{
void
testConstructors()
{
testcase("constructors");
using namespace jtx;
EntryTestEnv e(*this);
std::string const credTypeStr = "termsandconditions";
Slice const credType = makeSlice(credTypeStr);
expectKeylet<CredentialEntry>(
*this,
e,
keylet::credential(e.alice.id(), e.bob.id(), credType),
"credential(subject, issuer, credType)",
e.alice.id(),
e.bob.id(),
credType);
expectKeylet<CredentialEntry>(
*this, 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.
BEAST_EXPECT(
keylet::credential(e.alice.id(), e.bob.id(), credType).key !=
keylet::credential(e.bob.id(), e.alice.id(), credType).key);
}
public:
void
run() override
{
testConstructors();
}
};
BEAST_DEFINE_TESTSUITE(CredentialEntry, ledger, xrpl);
} // namespace xrpl::test

View File

@@ -0,0 +1,33 @@
#include <test/jtx/Account.h>
#include <test/ledger/EntryTestHelpers.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/ledger/helpers/DIDEntry.h>
#include <xrpl/protocol/Indexes.h>
namespace xrpl::test {
class DIDEntry_test : public beast::unit_test::Suite
{
void
testConstructors()
{
testcase("constructors");
using namespace jtx;
EntryTestEnv e(*this);
expectKeylet<DIDEntry>(*this, e, keylet::did(e.alice.id()), "did(account)", e.alice.id());
}
public:
void
run() override
{
testConstructors();
}
};
BEAST_DEFINE_TESTSUITE(DIDEntry, ledger, xrpl);
} // namespace xrpl::test

View File

@@ -0,0 +1,46 @@
#include <test/jtx/Account.h>
#include <test/ledger/EntryTestHelpers.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/ledger/helpers/DelegateEntry.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Keylet.h>
namespace xrpl::test {
class DelegateEntry_test : public beast::unit_test::Suite
{
void
testConstructors()
{
testcase("constructors");
using namespace jtx;
EntryTestEnv e(*this);
expectKeylet<DelegateEntry>(
*this,
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.
BEAST_EXPECT(
keylet::delegate(e.alice.id(), e.bob.id()).key !=
keylet::delegate(e.bob.id(), e.alice.id()).key);
}
public:
void
run() override
{
testConstructors();
}
};
BEAST_DEFINE_TESTSUITE(DelegateEntry, ledger, xrpl);
} // namespace xrpl::test

View File

@@ -0,0 +1,72 @@
#include <test/jtx/Account.h>
#include <test/ledger/EntryTestHelpers.h>
#include <xrpl/basics/Slice.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/ledger/helpers/DepositPreauthEntry.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Keylet.h>
#include <set>
#include <string>
#include <utility>
namespace xrpl::test {
class DepositPreauthEntry_test : public beast::unit_test::Suite
{
void
testConstructors()
{
testcase("constructors");
using namespace jtx;
EntryTestEnv e(*this);
std::string const credTypeStr = "termsandconditions";
std::set<std::pair<AccountID, Slice>> const authCreds{{e.bob.id(), makeSlice(credTypeStr)}};
expectKeylet<DepositPreauthEntry>(
*this,
e,
keylet::depositPreauth(e.alice.id(), e.bob.id()),
"depositPreauth(owner, preauthorized)",
e.alice.id(),
e.bob.id());
expectKeylet<DepositPreauthEntry>(
*this,
e,
keylet::depositPreauth(e.alice.id(), authCreds),
"depositPreauth(owner, authCreds)",
e.alice.id(),
authCreds);
expectKeylet<DepositPreauthEntry>(
*this, 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.
BEAST_EXPECT(
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.
BEAST_EXPECT(
keylet::depositPreauth(e.alice.id(), authCreds).key !=
keylet::depositPreauth(e.alice.id(), e.bob.id()).key);
}
public:
void
run() override
{
testConstructors();
}
};
BEAST_DEFINE_TESTSUITE(DepositPreauthEntry, ledger, xrpl);
} // namespace xrpl::test

View File

@@ -0,0 +1,49 @@
#include <test/jtx/Account.h>
#include <test/ledger/EntryTestHelpers.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/ledger/helpers/DirectoryNodeEntry.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Keylet.h>
#include <cstdint>
namespace xrpl::test {
class DirectoryNodeEntry_test : public beast::unit_test::Suite
{
void
testConstructors()
{
testcase("constructors");
using namespace jtx;
EntryTestEnv e(*this);
expectKeylet<DirectoryNodeEntry>(
*this, e, keylet::ownerDir(e.alice.id()), "ownerDir(id)", e.alice.id());
expectKeylet<DirectoryNodeEntry>(
*this,
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.
BEAST_EXPECT(keylet::ownerDir(e.alice.id()).key != keylet::page(e.someID(), 3u).key);
}
public:
void
run() override
{
testConstructors();
}
};
BEAST_DEFINE_TESTSUITE(DirectoryNodeEntry, ledger, xrpl);
} // namespace xrpl::test

View File

@@ -0,0 +1,131 @@
#pragma once
#include <test/jtx/Account.h>
#include <test/jtx/Env.h>
#include <test/jtx/amount.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ApplyViewImpl.h>
#include <xrpl/ledger/OpenView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/protocol/Keylet.h>
#include <xrpl/protocol/LedgerHeader.h>
#include <memory>
#include <string>
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 suite rather
* than per testcase -- Env construction dominates the runtime of these tests by
* a wide margin, and none of the assertions mutate the ledger.
*/
class EntryTestEnv
{
public:
jtx::Env env;
jtx::Account const alice{"alice"};
jtx::Account const bob{"bob"};
jtx::Account const carol{"carol"};
explicit EntryTestEnv(beast::unit_test::Suite& suite)
: env(suite), ledger_(fundAndClose()), av_(&*ledger_, TapNone)
{
}
/**
* The ledger apply() was built over -- deliberately not env.current().
* A suite that closed a ledger or submitted a transaction would make those
* two diverge, leaving apply() pointing at a ledger read() no longer names.
*/
[[nodiscard]] ReadView const&
read() const
{
return *ledger_;
}
[[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 member initializer list, so it may only touch env and the
// accounts -- everything declared above ledger_.
std::shared_ptr<OpenView const>
fundAndClose()
{
env.fund(jtx::XRP(10'000), alice, bob, carol);
env.close();
return env.current();
}
// Declared before av_, which holds a raw pointer into it, so that it is
// destroyed after av_.
std::shared_ptr<OpenView const> ledger_;
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 <template <typename> class Entry, typename... Args>
void
expectKeylet(
beast::unit_test::Suite& suite,
EntryTestEnv& e,
Keylet const& expected,
std::string const& what,
Args const&... args)
{
bool const present = e.read().read(expected) != nullptr;
// The writable entry retains its keylet, so it can be inspected whether
// or not the entry exists.
Entry<ApplyView> const w(args..., e.apply());
suite.expect(w.keylet().key == expected.key, what + ": writable key");
suite.expect(w.keylet().type == expected.type, what + ": writable type");
suite.expect(w.exists() == present, what + ": writable exists");
// The read-only entry has no keylet of its own -- it derives one from the
// SLE, and only when the SLE exists. Its agreement with a direct read
// of the expected keylet is what shows it resolved the same key.
Entry<ReadView> const r(args..., e.read());
suite.expect(r.exists() == present, what + ": read-only exists");
if (present)
suite.expect(r.key() == expected.key, what + ": read-only key");
// Not r.type(): for a typed entry that returns kEntryType, so checking it
// would just be this same assertion spelled twice.
static_assert(Entry<ReadView>::kEntryType == Entry<ApplyView>::kEntryType);
suite.expect(Entry<ReadView>::kEntryType == expected.type, what + ": kEntryType");
}
} // namespace xrpl::test

View File

@@ -0,0 +1,37 @@
#include <test/jtx/Account.h>
#include <test/ledger/EntryTestHelpers.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/ledger/helpers/EscrowEntry.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/SeqProxy.h>
namespace xrpl::test {
class EscrowEntry_test : public beast::unit_test::Suite
{
void
testConstructors()
{
testcase("constructors");
using namespace jtx;
EntryTestEnv e(*this);
SeqProxy const seq = SeqProxy::rawSequence(11);
expectKeylet<EscrowEntry>(
*this, e, keylet::escrow(e.alice.id(), seq), "escrow(src, seq)", e.alice.id(), seq);
}
public:
void
run() override
{
testConstructors();
}
};
BEAST_DEFINE_TESTSUITE(EscrowEntry, ledger, xrpl);
} // namespace xrpl::test

View File

@@ -0,0 +1,32 @@
#include <test/ledger/EntryTestHelpers.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/ledger/helpers/FeeSettingsEntry.h>
#include <xrpl/protocol/Indexes.h>
namespace xrpl::test {
class FeeSettingsEntry_test : public beast::unit_test::Suite
{
void
testConstructors()
{
testcase("constructors");
using namespace jtx;
EntryTestEnv e(*this);
expectKeylet<FeeSettingsEntry>(*this, e, keylet::feeSettings(), "feeSettings()");
}
public:
void
run() override
{
testConstructors();
}
};
BEAST_DEFINE_TESTSUITE(FeeSettingsEntry, ledger, xrpl);
} // namespace xrpl::test

View File

@@ -0,0 +1,32 @@
#include <test/ledger/EntryTestHelpers.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/ledger/helpers/LedgerHashesEntry.h>
#include <xrpl/protocol/Indexes.h>
namespace xrpl::test {
class LedgerHashesEntry_test : public beast::unit_test::Suite
{
void
testConstructors()
{
testcase("constructors");
using namespace jtx;
EntryTestEnv e(*this);
expectKeylet<LedgerHashesEntry>(*this, e, keylet::skip(), "skip()");
}
public:
void
run() override
{
testConstructors();
}
};
BEAST_DEFINE_TESTSUITE(LedgerHashesEntry, ledger, xrpl);
} // namespace xrpl::test

View File

@@ -0,0 +1,45 @@
#include <test/jtx/Account.h>
#include <test/ledger/EntryTestHelpers.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/ledger/helpers/LoanBrokerEntry.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/SeqProxy.h>
namespace xrpl::test {
class LoanBrokerEntry_test : public beast::unit_test::Suite
{
void
testConstructors()
{
testcase("constructors");
using namespace jtx;
EntryTestEnv e(*this);
SeqProxy const seq = SeqProxy::rawSequence(5);
expectKeylet<LoanBrokerEntry>(
*this,
e,
keylet::loanBroker(e.alice.id(), seq),
"loanBroker(owner, seq)",
e.alice.id(),
seq);
expectKeylet<LoanBrokerEntry>(
*this, e, keylet::loanBroker(e.someID()), "loanBroker(uint256)", e.someID());
}
public:
void
run() override
{
testConstructors();
}
};
BEAST_DEFINE_TESTSUITE(LoanBrokerEntry, ledger, xrpl);
} // namespace xrpl::test

View File

@@ -0,0 +1,49 @@
#include <test/ledger/EntryTestHelpers.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/ledger/helpers/LoanEntry.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Keylet.h>
#include <xrpl/protocol/SeqProxy.h>
namespace xrpl::test {
class LoanEntry_test : public beast::unit_test::Suite
{
void
testConstructors()
{
testcase("constructors");
using namespace jtx;
EntryTestEnv e(*this);
SeqProxy const seq = SeqProxy::rawSequence(9);
expectKeylet<LoanEntry>(
*this,
e,
keylet::loan(e.someID(), seq),
"loan(loanBrokerID, loanSeq)",
e.someID(),
seq);
expectKeylet<LoanEntry>(*this, e, keylet::loan(e.someID()), "loan(uint256)", e.someID());
// Both overloads start with the same uint256, so they must not produce
// the same key -- otherwise arity is the only thing keeping them apart
// and the test proves nothing.
BEAST_EXPECT(keylet::loan(e.someID(), seq).key != keylet::loan(e.someID()).key);
}
public:
void
run() override
{
testConstructors();
}
};
BEAST_DEFINE_TESTSUITE(LoanEntry, ledger, xrpl);
} // namespace xrpl::test

View File

@@ -0,0 +1,53 @@
#include <test/jtx/Account.h>
#include <test/ledger/EntryTestHelpers.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/ledger/helpers/MPTokenEntry.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/UintTypes.h>
namespace xrpl::test {
class MPTokenEntry_test : public beast::unit_test::Suite
{
void
testConstructors()
{
testcase("constructors");
using namespace jtx;
EntryTestEnv e(*this);
MPTID const issuanceID = makeMptID(1, e.alice.id());
expectKeylet<MPTokenEntry>(
*this,
e,
keylet::mptoken(issuanceID, e.bob.id()),
"mptoken(MPTID, holder)",
issuanceID,
e.bob.id());
expectKeylet<MPTokenEntry>(
*this,
e,
keylet::mptoken(e.someID(), e.bob.id()),
"mptoken(issuanceKey, holder)",
e.someID(),
e.bob.id());
expectKeylet<MPTokenEntry>(
*this, e, keylet::mptoken(e.someID()), "mptoken(uint256)", e.someID());
}
public:
void
run() override
{
testConstructors();
}
};
BEAST_DEFINE_TESTSUITE(MPTokenEntry, ledger, xrpl);
} // namespace xrpl::test

View File

@@ -0,0 +1,50 @@
#include <test/jtx/Account.h>
#include <test/ledger/EntryTestHelpers.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/ledger/helpers/MPTokenIssuanceEntry.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/UintTypes.h>
#include <cstdint>
namespace xrpl::test {
class MPTokenIssuanceEntry_test : public beast::unit_test::Suite
{
void
testConstructors()
{
testcase("constructors");
using namespace jtx;
EntryTestEnv e(*this);
MPTID const issuanceID = makeMptID(1, e.alice.id());
expectKeylet<MPTokenIssuanceEntry>(
*this,
e,
keylet::mptokenIssuance(makeMptID(1, e.alice.id())),
"mptokenIssuance(seq, issuer)",
std::uint32_t{1},
e.alice.id());
expectKeylet<MPTokenIssuanceEntry>(
*this, e, keylet::mptokenIssuance(issuanceID), "mptokenIssuance(MPTID)", issuanceID);
expectKeylet<MPTokenIssuanceEntry>(
*this, e, keylet::mptokenIssuance(e.someID()), "mptokenIssuance(uint256)", e.someID());
}
public:
void
run() override
{
testConstructors();
}
};
BEAST_DEFINE_TESTSUITE(MPTokenIssuanceEntry, ledger, xrpl);
} // namespace xrpl::test

View File

@@ -0,0 +1,45 @@
#include <test/jtx/Account.h>
#include <test/ledger/EntryTestHelpers.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/ledger/helpers/NFTokenOfferEntry.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/SeqProxy.h>
namespace xrpl::test {
class NFTokenOfferEntry_test : public beast::unit_test::Suite
{
void
testConstructors()
{
testcase("constructors");
using namespace jtx;
EntryTestEnv e(*this);
SeqProxy const seq = SeqProxy::rawSequence(13);
expectKeylet<NFTokenOfferEntry>(
*this,
e,
keylet::nftokenOffer(e.alice.id(), seq),
"nftokenOffer(owner, seq)",
e.alice.id(),
seq);
expectKeylet<NFTokenOfferEntry>(
*this, e, keylet::nftokenOffer(e.someID()), "nftokenOffer(uint256)", e.someID());
}
public:
void
run() override
{
testConstructors();
}
};
BEAST_DEFINE_TESTSUITE(NFTokenOfferEntry, ledger, xrpl);
} // namespace xrpl::test

View File

@@ -0,0 +1,42 @@
#include <test/jtx/Account.h>
#include <test/ledger/EntryTestHelpers.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/ledger/helpers/NFTokenPageEntry.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Keylet.h>
namespace xrpl::test {
class NFTokenPageEntry_test : public beast::unit_test::Suite
{
void
testConstructors()
{
testcase("constructors");
using namespace jtx;
EntryTestEnv e(*this);
Keylet const pageMin = keylet::nftokenPageMin(e.alice.id());
expectKeylet<NFTokenPageEntry>(
*this,
e,
keylet::nftokenPage(pageMin, e.someID()),
"nftokenPage(page, token)",
pageMin,
e.someID());
}
public:
void
run() override
{
testConstructors();
}
};
BEAST_DEFINE_TESTSUITE(NFTokenPageEntry, ledger, xrpl);
} // namespace xrpl::test

View File

@@ -0,0 +1,32 @@
#include <test/ledger/EntryTestHelpers.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/ledger/helpers/NegativeUNLEntry.h>
#include <xrpl/protocol/Indexes.h>
namespace xrpl::test {
class NegativeUNLEntry_test : public beast::unit_test::Suite
{
void
testConstructors()
{
testcase("constructors");
using namespace jtx;
EntryTestEnv e(*this);
expectKeylet<NegativeUNLEntry>(*this, e, keylet::negativeUNL(), "negativeUNL()");
}
public:
void
run() override
{
testConstructors();
}
};
BEAST_DEFINE_TESTSUITE(NegativeUNLEntry, ledger, xrpl);
} // namespace xrpl::test

View File

@@ -0,0 +1,39 @@
#include <test/jtx/Account.h>
#include <test/ledger/EntryTestHelpers.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/ledger/helpers/OfferEntry.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/SeqProxy.h>
namespace xrpl::test {
class OfferEntry_test : public beast::unit_test::Suite
{
void
testConstructors()
{
testcase("constructors");
using namespace jtx;
EntryTestEnv e(*this);
SeqProxy const seq = SeqProxy::rawSequence(3);
expectKeylet<OfferEntry>(
*this, e, keylet::offer(e.alice.id(), seq), "offer(id, seq)", e.alice.id(), seq);
expectKeylet<OfferEntry>(*this, e, keylet::offer(e.someID()), "offer(uint256)", e.someID());
}
public:
void
run() override
{
testConstructors();
}
};
BEAST_DEFINE_TESTSUITE(OfferEntry, ledger, xrpl);
} // namespace xrpl::test

View File

@@ -0,0 +1,41 @@
#include <test/jtx/Account.h>
#include <test/ledger/EntryTestHelpers.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/ledger/helpers/OracleEntry.h>
#include <xrpl/protocol/Indexes.h>
#include <cstdint>
namespace xrpl::test {
class OracleEntry_test : public beast::unit_test::Suite
{
void
testConstructors()
{
testcase("constructors");
using namespace jtx;
EntryTestEnv e(*this);
expectKeylet<OracleEntry>(
*this,
e,
keylet::oracle(e.alice.id(), 7u),
"oracle(account, documentID)",
e.alice.id(),
std::uint32_t{7});
}
public:
void
run() override
{
testConstructors();
}
};
BEAST_DEFINE_TESTSUITE(OracleEntry, ledger, xrpl);
} // namespace xrpl::test

View File

@@ -0,0 +1,50 @@
#include <test/jtx/Account.h>
#include <test/ledger/EntryTestHelpers.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/ledger/helpers/PayChannelEntry.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Keylet.h>
#include <xrpl/protocol/SeqProxy.h>
namespace xrpl::test {
class PayChannelEntry_test : public beast::unit_test::Suite
{
void
testConstructors()
{
testcase("constructors");
using namespace jtx;
EntryTestEnv e(*this);
SeqProxy const seq = SeqProxy::rawSequence(4);
expectKeylet<PayChannelEntry>(
*this,
e,
keylet::payChannel(e.alice.id(), e.bob.id(), seq),
"payChannel(src, dst, seq)",
e.alice.id(),
e.bob.id(),
seq);
// Source and destination are both AccountIDs, so the assertion above
// only has teeth if their order matters.
BEAST_EXPECT(
keylet::payChannel(e.alice.id(), e.bob.id(), seq).key !=
keylet::payChannel(e.bob.id(), e.alice.id(), seq).key);
}
public:
void
run() override
{
testConstructors();
}
};
BEAST_DEFINE_TESTSUITE(PayChannelEntry, ledger, xrpl);
} // namespace xrpl::test

View File

@@ -0,0 +1,49 @@
#include <test/jtx/Account.h>
#include <test/ledger/EntryTestHelpers.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/ledger/helpers/PermissionedDomainEntry.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/SeqProxy.h>
namespace xrpl::test {
class PermissionedDomainEntry_test : public beast::unit_test::Suite
{
void
testConstructors()
{
testcase("constructors");
using namespace jtx;
EntryTestEnv e(*this);
SeqProxy const seq = SeqProxy::rawSequence(6);
expectKeylet<PermissionedDomainEntry>(
*this,
e,
keylet::permissionedDomain(e.alice.id(), seq),
"permissionedDomain(account, seq)",
e.alice.id(),
seq);
expectKeylet<PermissionedDomainEntry>(
*this,
e,
keylet::permissionedDomain(e.someID()),
"permissionedDomain(uint256)",
e.someID());
}
public:
void
run() override
{
testConstructors();
}
};
BEAST_DEFINE_TESTSUITE(PermissionedDomainEntry, ledger, xrpl);
} // namespace xrpl::test

View File

@@ -0,0 +1,60 @@
#include <test/jtx/Account.h>
#include <test/ledger/EntryTestHelpers.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/ledger/helpers/RippleStateEntry.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Keylet.h>
#include <xrpl/protocol/UintTypes.h>
namespace xrpl::test {
class RippleStateEntry_test : public beast::unit_test::Suite
{
void
testConstructors()
{
testcase("constructors");
using namespace jtx;
EntryTestEnv e(*this);
auto const usd = e.alice["USD"];
Currency const currency = usd.currency;
expectKeylet<RippleStateEntry>(
*this,
e,
keylet::trustLine(e.alice.id(), e.bob.id(), currency),
"trustLine(id0, id1, currency)",
e.alice.id(),
e.bob.id(),
currency);
expectKeylet<RippleStateEntry>(
*this,
e,
keylet::trustLine(e.bob.id(), usd.issue()),
"trustLine(id, issue)",
e.bob.id(),
usd.issue());
// Trust lines are deliberately symmetric in their two accounts -- the
// keylet canonicalizes them -- so unlike the other two-account entries
// there is no transposition to catch here.
BEAST_EXPECT(
keylet::trustLine(e.alice.id(), e.bob.id(), currency).key ==
keylet::trustLine(e.bob.id(), e.alice.id(), currency).key);
}
public:
void
run() override
{
testConstructors();
}
};
BEAST_DEFINE_TESTSUITE(RippleStateEntry, ledger, xrpl);
} // namespace xrpl::test

View File

@@ -0,0 +1,452 @@
#include <test/jtx/Account.h>
#include <test/jtx/Env.h>
#include <test/jtx/JTx.h>
#include <test/jtx/amount.h>
#include <test/jtx/noop.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ApplyViewImpl.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/AMMEntry.h> // IWYU pragma: keep
#include <xrpl/ledger/helpers/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AmendmentsEntry.h> // IWYU pragma: keep
#include <xrpl/ledger/helpers/BridgeEntry.h> // IWYU pragma: keep
#include <xrpl/ledger/helpers/CheckEntry.h> // IWYU pragma: keep
#include <xrpl/ledger/helpers/CredentialEntry.h> // IWYU pragma: keep
#include <xrpl/ledger/helpers/DIDEntry.h> // IWYU pragma: keep
#include <xrpl/ledger/helpers/DelegateEntry.h> // IWYU pragma: keep
#include <xrpl/ledger/helpers/DepositPreauthEntry.h> // IWYU pragma: keep
#include <xrpl/ledger/helpers/DirectoryNodeEntry.h> // IWYU pragma: keep
#include <xrpl/ledger/helpers/EscrowEntry.h> // IWYU pragma: keep
#include <xrpl/ledger/helpers/FeeSettingsEntry.h> // IWYU pragma: keep
#include <xrpl/ledger/helpers/LedgerHashesEntry.h> // IWYU pragma: keep
#include <xrpl/ledger/helpers/LoanBrokerEntry.h> // IWYU pragma: keep
#include <xrpl/ledger/helpers/LoanEntry.h> // IWYU pragma: keep
#include <xrpl/ledger/helpers/MPTokenEntry.h> // IWYU pragma: keep
#include <xrpl/ledger/helpers/MPTokenIssuanceEntry.h> // IWYU pragma: keep
#include <xrpl/ledger/helpers/NFTokenOfferEntry.h> // IWYU pragma: keep
#include <xrpl/ledger/helpers/NFTokenPageEntry.h> // IWYU pragma: keep
#include <xrpl/ledger/helpers/NegativeUNLEntry.h> // IWYU pragma: keep
#include <xrpl/ledger/helpers/OfferEntry.h>
#include <xrpl/ledger/helpers/OracleEntry.h> // IWYU pragma: keep
#include <xrpl/ledger/helpers/PayChannelEntry.h> // IWYU pragma: keep
#include <xrpl/ledger/helpers/PermissionedDomainEntry.h> // IWYU pragma: keep
#include <xrpl/ledger/helpers/RippleStateEntry.h> // IWYU pragma: keep
#include <xrpl/ledger/helpers/SLEBase.h>
#include <xrpl/ledger/helpers/SignerListEntry.h> // IWYU pragma: keep
#include <xrpl/ledger/helpers/SponsorshipEntry.h> // IWYU pragma: keep
#include <xrpl/ledger/helpers/TicketEntry.h>
#include <xrpl/ledger/helpers/VaultEntry.h> // IWYU pragma: keep
#include <xrpl/ledger/helpers/XChainOwnedClaimIDEntry.h> // IWYU pragma: keep
#include <xrpl/ledger/helpers/XChainOwnedCreateAccountClaimIDEntry.h> // IWYU pragma: keep
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/SeqProxy.h>
#include <stdexcept>
#include <tuple>
#include <type_traits>
namespace xrpl {
// The entry classes have no consumers yet, and an un-instantiated class
// template is barely type-checked. Instantiate every one explicitly so the
// compiler actually checks them. Keep this block even once real call sites
// exist: it is what catches a new ledger entry type being added without its
// wrapper class, or the wrapper class existing but never actually being used.
//
// Driving this off ledger_entries.macro keeps it exhaustive by construction:
// adding a ledger entry type without adding its entry class stops compiling
// here, and the static_assert pins each one to the right LedgerEntryType.
//
// Keep this loop in one file rather than splitting it across the per-entry
// *Entry_test.cpp suites. Those are hand-written, so a new ledger entry type
// would simply have no file there and nothing would complain; this is the only
// thing making the coverage exhaustive rather than merely extensive.
template class SLEBase<ReadView>;
template class SLEBase<ApplyView>;
#pragma push_macro("LEDGER_ENTRY")
#undef LEDGER_ENTRY
#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);
#include <xrpl/protocol/detail/ledger_entries.macro>
#undef LEDGER_ENTRY
#pragma pop_macro("LEDGER_ENTRY")
// --- Entry-type safety, checked at compile time. ---
//
// The writable -> read-only converting constructor is inherited into every
// per-type entry, so without the entry-type constraint it will bind any
// writable entry that slices to SLEBase. These assertions pin down which
// conversions are legal.
// An entry class for one entry type must never be constructible from another.
static_assert(
!std::is_convertible_v<WOfferEntry, RAccountRootEntry>,
"cross-entry-type conversion must not compile");
static_assert(
!std::is_constructible_v<RAccountRootEntry, WOfferEntry>,
"cross-entry-type construction must not compile, even explicitly");
static_assert(
!std::is_convertible_v<ROfferEntry, RAccountRootEntry>,
"read-only cross-entry-type conversion must not compile");
// Nor from a type-erased writable entry, which carries no static type.
static_assert(
!std::is_convertible_v<WritableSLE, RAccountRootEntry>,
"generic -> typed conversion must not compile");
// The intended conversions must keep working: same type writable -> read-only,
// and typed -> generic widening.
static_assert(
std::is_convertible_v<WAccountRootEntry, RAccountRootEntry>,
"same-type writable -> read-only conversion must keep working");
static_assert(
std::is_convertible_v<WAccountRootEntry, ReadOnlySLE>,
"typed -> generic widening must keep working");
// Detection idioms for the writable interface. These have to go through a
// template parameter: a requires-expression over a concrete type is checked
// eagerly, so spelling the calls out inline would be a hard error rather than
// the `false` the assertions below want.
template <typename T>
concept HasMutableRawSle = requires(T& t) { t.mutableRawSle(); };
template <typename T>
concept HasApplyView = requires(T& t) { t.applyView(); };
namespace test {
class SLEBase_test : public beast::unit_test::Suite
{
void
testReadOnly()
{
testcase("read-only entry");
using namespace jtx;
Env env(*this);
Account const alice("alice");
Account const bob("bob");
env.fund(XRP(10000), alice);
env.close();
RAccountRootEntry const absent(bob.id(), *env.current());
BEAST_EXPECT(!absent.exists());
BEAST_EXPECT(!static_cast<bool>(absent));
// A typed entry knows its entry type even with nothing to read.
BEAST_EXPECT(absent.type() == ltACCOUNT_ROOT);
RAccountRootEntry const present(alice.id(), *env.current());
BEAST_EXPECT(present.exists());
BEAST_EXPECT(static_cast<bool>(present));
BEAST_EXPECT(present.key() == keylet::account(alice.id()).key);
BEAST_EXPECT(present.type() == ltACCOUNT_ROOT);
BEAST_EXPECT(present.keylet().type == ltACCOUNT_ROOT);
BEAST_EXPECT(present->getType() == ltACCOUNT_ROOT);
BEAST_EXPECT((*present).getType() == ltACCOUNT_ROOT);
BEAST_EXPECT(&present.readView() == &*env.current());
}
void
testAdoptSLE()
{
testcase("read-only entry adopting an SLE");
using namespace jtx;
Env env(*this);
Account const alice("alice");
env.fund(XRP(10000), alice);
env.close();
auto const sle = env.current()->read(keylet::account(alice.id()));
BEAST_EXPECT(sle != nullptr);
RAccountRootEntry const adopted(sle, *env.current(), env.journal);
BEAST_EXPECT(adopted.exists());
BEAST_EXPECT(adopted.rawSle() == sle);
BEAST_EXPECT(adopted.key() == keylet::account(alice.id()).key);
BEAST_EXPECT(adopted.type() == ltACCOUNT_ROOT);
// keylet() reports the SLE's own type, not the entry's static binding, so
// it stays truthful in a Release build where the constructor's
// entry-type assert is compiled out.
BEAST_EXPECT(adopted.keylet().type == ltACCOUNT_ROOT);
// Adopting a null SLE is allowed: the assert only fires on a
// type mismatch, and a null pointer has no type to mismatch.
RAccountRootEntry const empty(SLE::const_pointer{}, *env.current());
BEAST_EXPECT(!empty.exists());
BEAST_EXPECT(empty.type() == ltACCOUNT_ROOT);
// A generic entry adopting the same SLE has to read the type back.
ReadOnlySLE const generic(sle, *env.current());
BEAST_EXPECT(generic.exists());
BEAST_EXPECT(generic.type() == ltACCOUNT_ROOT);
BEAST_EXPECT(generic.keylet().type == ltACCOUNT_ROOT);
// There is deliberately no writable equivalent.
static_assert(
!std::is_constructible_v<WAccountRootEntry, SLE::pointer, ApplyView&>,
"writable entries must not be constructible from a bare SLE");
}
void
testWritableAccessors()
{
testcase("writable entry accessors");
using namespace jtx;
Env env(*this);
Account const alice("alice");
env.fund(XRP(10000), alice);
env.close();
auto const ledger = env.current();
ApplyViewImpl av(&*ledger, TapNone);
WAccountRootEntry account(alice.id(), av, env.journal);
BEAST_EXPECT(account.exists());
BEAST_EXPECT(account.mutableRawSle() == account.rawSle());
BEAST_EXPECT(&account.applyView() == &av);
BEAST_EXPECT(&account.readView() == static_cast<ReadView const*>(&av));
BEAST_EXPECT(&account.journal().sink() == &env.journal.sink());
// The mutable dereference operators reach the same entry.
BEAST_EXPECT(account.operator->() == account.rawSle().get());
BEAST_EXPECT(&*account == account.rawSle().get());
// Everything handing out mutable access is non-const, so a const
// writable entry is as inert as a read-only one.
static_assert(HasMutableRawSle<WAccountRootEntry>);
static_assert(HasApplyView<WAccountRootEntry>);
static_assert(
!HasMutableRawSle<WAccountRootEntry const>,
"mutableRawSle() must not be callable on a const writable entry");
static_assert(
!HasApplyView<WAccountRootEntry const>,
"applyView() must not be callable on a const writable entry");
// Read-only entries do not have the writable interface at all.
static_assert(
!HasMutableRawSle<RAccountRootEntry>,
"mutableRawSle() must not exist on a read-only entry");
static_assert(
!HasApplyView<RAccountRootEntry>, "applyView() must not exist on a read-only entry");
}
void
testApplyViewContextCtor()
{
testcase("writable entry from ApplyViewContext");
using namespace jtx;
Env env(*this);
Account const alice("alice");
env.fund(XRP(10000), alice);
env.close();
auto const ledger = env.current();
ApplyViewImpl av(&*ledger, TapNone);
auto const jt = env.jt(noop(alice));
BEAST_EXPECT(jt.stx != nullptr);
ApplyViewContext const ctx{.view = av, .tx = *jt.stx};
// Delegates to the (Keylet, ApplyView&) constructor; ctx.tx is not
// retained, so this must be indistinguishable from building from
// ctx.view directly.
WAccountRootEntry fromCtx(keylet::account(alice.id()), ctx, env.journal);
BEAST_EXPECT(fromCtx.exists());
BEAST_EXPECT(&fromCtx.applyView() == &av);
BEAST_EXPECT(fromCtx.key() == keylet::account(alice.id()).key);
WAccountRootEntry const fromView(keylet::account(alice.id()), av, env.journal);
BEAST_EXPECT(fromCtx.rawSle() == fromView.rawSle());
}
void
testWritableLifecycle()
{
testcase("writable entry lifecycle");
using namespace jtx;
Env env(*this);
Account const alice("alice");
env.fund(XRP(10000), alice);
env.close();
// A view we never apply, so nothing here reaches the ledger. Hold the
// ledger it points into: OpenLedger::current() hands out a copy of its
// own shared_ptr, so a later close would otherwise free it.
auto const ledger = env.current();
ApplyViewImpl av(&*ledger, TapNone);
// Entry that does not exist yet: newSLE() -> insert().
{
WTicketEntry ticket(keylet::ticket(alice.id(), SeqProxy::rawTicket(1)), av);
BEAST_EXPECT(!ticket.exists());
BEAST_EXPECT(ticket.key() == keylet::ticket(alice.id(), SeqProxy::rawTicket(1)).key);
BEAST_EXPECT(ticket.type() == ltTICKET);
BEAST_EXPECT(ticket.keylet().type == ltTICKET);
ticket.newSLE();
BEAST_EXPECT(ticket.exists());
ticket.insert();
ticket.update();
// Erasing an entry inserted in this same view drops it outright.
ticket.erase();
BEAST_EXPECT(!ticket.exists());
}
// Entry that already exists. ApplyStateTable::erase() keeps holding
// this exact SLE and builds the DeletedNode's FinalFields from it, so
// the entry must drop its pointer or a later write would silently
// land in transaction metadata.
{
WAccountRootEntry account(alice.id(), av);
BEAST_EXPECT(account.exists());
account.erase();
BEAST_EXPECT(!account.exists());
}
}
void
testConversion()
{
testcase("writable to read-only conversion");
using namespace jtx;
Env env(*this);
Account const alice("alice");
env.fund(XRP(10000), alice);
env.close();
auto const ledger = env.current();
ApplyViewImpl av(&*ledger, TapNone);
WAccountRootEntry const writable(alice.id(), av);
BEAST_EXPECT(writable.exists());
RAccountRootEntry const readOnly = writable;
BEAST_EXPECT(readOnly.exists());
BEAST_EXPECT(readOnly.rawSle() == writable.rawSle());
ReadOnlySLE const generic = writable;
BEAST_EXPECT(generic.exists());
BEAST_EXPECT(generic.rawSle() == writable.rawSle());
// A generic entry has to read the type back out of the SLE.
BEAST_EXPECT(generic.type() == ltACCOUNT_ROOT);
}
void
testResolveEntryPeeks()
{
testcase("read-only entry over an ApplyView shares the view's SLE");
using namespace jtx;
Env env(*this);
Account const alice("alice");
env.fund(XRP(10000), alice);
env.close();
// env.current() is an OpenView, which derives from ReadView but not
// from ApplyView, so resolveEntry's dynamic_cast fails and this takes
// the plain ReadView::read() path.
auto const ledger = env.current();
RAccountRootEntry const overLedger(alice.id(), *ledger);
BEAST_EXPECT(overLedger.exists());
ApplyViewImpl av(&*ledger, TapNone);
// ReadView const& binds an ApplyViewImpl just as happily, and there the
// dynamic_cast succeeds, so this one resolves through ApplyView::peek().
RAccountRootEntry const readOnly(alice.id(), av);
BEAST_EXPECT(readOnly.exists());
WAccountRootEntry writable(alice.id(), av);
BEAST_EXPECT(writable.exists());
// The invariant resolveEntry() exists to hold: one SLE per key per
// view. read() would have handed back the base ledger's entry instead,
// which is a different object.
BEAST_EXPECT(readOnly.rawSle() == writable.rawSle());
BEAST_EXPECT(readOnly.rawSle() != overLedger.rawSle());
// Which is what keeps a read-only entry from going stale: a write
// through any other entry over the same view is visible through it.
auto const bumped = writable->getFieldU32(sfSequence) + 1;
writable->setFieldU32(sfSequence, bumped);
BEAST_EXPECT(readOnly->getFieldU32(sfSequence) == bumped);
}
void
testThrowsOnMissingEntry()
{
testcase("throws on missing entry");
using namespace jtx;
Env const env(*this);
Account const bob("bob");
// A generic read-only entry has no static type to fall back on, so
// type() must read it off the (absent) SLE and throw.
ReadOnlySLE const absent(keylet::account(bob.id()), *env.current());
BEAST_EXPECT(!absent.exists());
try
{
std::ignore = absent.type();
fail("type() on a missing entry should throw");
}
catch (std::logic_error const&)
{
pass();
}
// A per-type read-only entry always knows its type, but keylet() and
// key() still have to derive the ledger key from the SLE.
RAccountRootEntry const missing(bob.id(), *env.current());
BEAST_EXPECT(!missing.exists());
try
{
std::ignore = missing.key();
fail("key() on a missing entry should throw");
}
catch (std::logic_error const&)
{
pass();
}
}
public:
void
run() override
{
testReadOnly();
testAdoptSLE();
testWritableAccessors();
testApplyViewContextCtor();
testWritableLifecycle();
testConversion();
testResolveEntryPeeks();
testThrowsOnMissingEntry();
}
};
BEAST_DEFINE_TESTSUITE(SLEBase, ledger, xrpl);
} // namespace test
} // namespace xrpl

View File

@@ -0,0 +1,34 @@
#include <test/jtx/Account.h>
#include <test/ledger/EntryTestHelpers.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/ledger/helpers/SignerListEntry.h>
#include <xrpl/protocol/Indexes.h>
namespace xrpl::test {
class SignerListEntry_test : public beast::unit_test::Suite
{
void
testConstructors()
{
testcase("constructors");
using namespace jtx;
EntryTestEnv e(*this);
expectKeylet<SignerListEntry>(
*this, e, keylet::signerList(e.alice.id()), "signerList(account)", e.alice.id());
}
public:
void
run() override
{
testConstructors();
}
};
BEAST_DEFINE_TESTSUITE(SignerListEntry, ledger, xrpl);
} // namespace xrpl::test

View File

@@ -0,0 +1,46 @@
#include <test/jtx/Account.h>
#include <test/ledger/EntryTestHelpers.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/ledger/helpers/SponsorshipEntry.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Keylet.h>
namespace xrpl::test {
class SponsorshipEntry_test : public beast::unit_test::Suite
{
void
testConstructors()
{
testcase("constructors");
using namespace jtx;
EntryTestEnv e(*this);
expectKeylet<SponsorshipEntry>(
*this,
e,
keylet::sponsorship(e.alice.id(), e.bob.id()),
"sponsorship(sponsor, sponsee)",
e.alice.id(),
e.bob.id());
// Sponsor and sponsee are both AccountIDs, so the assertion above only
// has teeth if their order matters.
BEAST_EXPECT(
keylet::sponsorship(e.alice.id(), e.bob.id()).key !=
keylet::sponsorship(e.bob.id(), e.alice.id()).key);
}
public:
void
run() override
{
testConstructors();
}
};
BEAST_DEFINE_TESTSUITE(SponsorshipEntry, ledger, xrpl);
} // namespace xrpl::test

View File

@@ -0,0 +1,45 @@
#include <test/jtx/Account.h>
#include <test/ledger/EntryTestHelpers.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/ledger/helpers/TicketEntry.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/SeqProxy.h>
namespace xrpl::test {
class TicketEntry_test : public beast::unit_test::Suite
{
void
testConstructors()
{
testcase("constructors");
using namespace jtx;
EntryTestEnv e(*this);
SeqProxy const ticketSeq = SeqProxy::rawTicket(2);
expectKeylet<TicketEntry>(
*this,
e,
keylet::ticket(e.alice.id(), ticketSeq),
"ticket(id, ticketSeq)",
e.alice.id(),
ticketSeq);
expectKeylet<TicketEntry>(
*this, e, keylet::ticket(e.someID()), "ticket(uint256)", e.someID());
}
public:
void
run() override
{
testConstructors();
}
};
BEAST_DEFINE_TESTSUITE(TicketEntry, ledger, xrpl);
} // namespace xrpl::test

View File

@@ -0,0 +1,39 @@
#include <test/jtx/Account.h>
#include <test/ledger/EntryTestHelpers.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/ledger/helpers/VaultEntry.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/SeqProxy.h>
namespace xrpl::test {
class VaultEntry_test : public beast::unit_test::Suite
{
void
testConstructors()
{
testcase("constructors");
using namespace jtx;
EntryTestEnv e(*this);
SeqProxy const seq = SeqProxy::rawSequence(8);
expectKeylet<VaultEntry>(
*this, e, keylet::vault(e.alice.id(), seq), "vault(owner, seq)", e.alice.id(), seq);
expectKeylet<VaultEntry>(*this, e, keylet::vault(e.someID()), "vault(uint256)", e.someID());
}
public:
void
run() override
{
testConstructors();
}
};
BEAST_DEFINE_TESTSUITE(VaultEntry, ledger, xrpl);
} // namespace xrpl::test

View File

@@ -0,0 +1,45 @@
#include <test/jtx/Account.h>
#include <test/ledger/EntryTestHelpers.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/ledger/helpers/XChainOwnedClaimIDEntry.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Issue.h>
#include <xrpl/protocol/STXChainBridge.h>
#include <cstdint>
namespace xrpl::test {
class XChainOwnedClaimIDEntry_test : public beast::unit_test::Suite
{
void
testConstructors()
{
testcase("constructors");
using namespace jtx;
EntryTestEnv e(*this);
STXChainBridge const bridge{e.alice.id(), xrpIssue(), e.bob.id(), e.bob["USD"].issue()};
expectKeylet<XChainOwnedClaimIDEntry>(
*this,
e,
keylet::xChainClaimID(bridge, 5u),
"xChainClaimID(bridge, seq)",
bridge,
std::uint64_t{5});
}
public:
void
run() override
{
testConstructors();
}
};
BEAST_DEFINE_TESTSUITE(XChainOwnedClaimIDEntry, ledger, xrpl);
} // namespace xrpl::test

View File

@@ -0,0 +1,52 @@
#include <test/jtx/Account.h>
#include <test/ledger/EntryTestHelpers.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/ledger/helpers/XChainOwnedCreateAccountClaimIDEntry.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Issue.h>
#include <xrpl/protocol/Keylet.h>
#include <xrpl/protocol/STXChainBridge.h>
#include <cstdint>
namespace xrpl::test {
class XChainOwnedCreateAccountClaimIDEntry_test : public beast::unit_test::Suite
{
void
testConstructors()
{
testcase("constructors");
using namespace jtx;
EntryTestEnv e(*this);
STXChainBridge const bridge{e.alice.id(), xrpIssue(), e.bob.id(), e.bob["USD"].issue()};
expectKeylet<XChainOwnedCreateAccountClaimIDEntry>(
*this,
e,
keylet::xChainCreateAccountClaimID(bridge, 5u),
"xChainCreateAccountClaimID(bridge, seq)",
bridge,
std::uint64_t{5});
// Must not collide with the plain claim-ID keylet, which takes the same
// arguments.
BEAST_EXPECT(
keylet::xChainCreateAccountClaimID(bridge, 5u).key !=
keylet::xChainClaimID(bridge, 5u).key);
}
public:
void
run() override
{
testConstructors();
}
};
BEAST_DEFINE_TESTSUITE(XChainOwnedCreateAccountClaimIDEntry, ledger, xrpl);
} // namespace xrpl::test