From fd2cc6dcb308fb811960380eba7bf4309934d05d Mon Sep 17 00:00:00 2001 From: Olek <115580134+oleks-rip@users.noreply.github.com> Date: Fri, 10 Jul 2026 17:58:19 -0400 Subject: [PATCH 1/5] feat: XLS-68: Sponsor, #5887 continuation (#7350) Co-authored-by: tequ Co-authored-by: yinyiqian1 Co-authored-by: Mayukha Vadari Co-authored-by: Mayukha Vadari Co-authored-by: xrplf-ai-reviewer[bot] <266832837+xrplf-ai-reviewer[bot]@users.noreply.github.com> Co-authored-by: Peter Chen <34582813+PeterChen13579@users.noreply.github.com> Co-authored-by: Zhiyuan Wang <96991820+Kassaking7@users.noreply.github.com> Co-authored-by: Ayaz Salikhov Co-authored-by: Ayaz Salikhov Co-authored-by: Zhiyuan Wang <1830604455@qq.com> --- .cspell.config.yaml | 3 + include/xrpl/basics/base_uint.h | 2 +- include/xrpl/ledger/ApplyView.h | 21 +- include/xrpl/ledger/OwnerCounts.h | 70 + include/xrpl/ledger/PaymentSandbox.h | 14 +- include/xrpl/ledger/ReadView.h | 6 +- include/xrpl/ledger/View.h | 3 +- .../xrpl/ledger/helpers/AccountRootHelpers.h | 295 +- include/xrpl/ledger/helpers/EscrowHelpers.h | 77 +- include/xrpl/ledger/helpers/MPTokenHelpers.h | 10 +- include/xrpl/ledger/helpers/OracleHelpers.h | 31 + .../xrpl/ledger/helpers/RippleStateHelpers.h | 6 +- include/xrpl/ledger/helpers/SponsorHelpers.h | 184 + include/xrpl/ledger/helpers/TokenHelpers.h | 6 +- include/xrpl/protocol/Book.h | 4 +- include/xrpl/protocol/Fees.h | 9 +- include/xrpl/protocol/Indexes.h | 4 + include/xrpl/protocol/Issue.h | 2 +- include/xrpl/protocol/LedgerFormats.h | 6 +- include/xrpl/protocol/STObject.h | 4 +- include/xrpl/protocol/STTx.h | 3 + include/xrpl/protocol/TER.h | 2 + include/xrpl/protocol/TxFlags.h | 25 +- include/xrpl/protocol/detail/features.macro | 1 + .../xrpl/protocol/detail/ledger_entries.macro | 66 +- include/xrpl/protocol/detail/sfields.macro | 15 + .../xrpl/protocol/detail/transactions.macro | 29 + include/xrpl/protocol/jss.h | 3 + .../ledger_entries/AccountRoot.h | 105 + .../ledger_entries/RippleState.h | 70 + .../ledger_entries/Sponsorship.h | 344 + .../transactions/SponsorshipSet.h | 290 + .../transactions/SponsorshipTransfer.h | 179 + include/xrpl/tx/ApplyContext.h | 9 + include/xrpl/tx/Transactor.h | 24 + include/xrpl/tx/invariants/InvariantCheck.h | 5 +- .../xrpl/tx/invariants/SponsorshipInvariant.h | 61 + include/xrpl/tx/paths/AMMOffer.h | 6 +- include/xrpl/tx/paths/Offer.h | 3 +- .../tx/transactors/sponsor/SponsorshipSet.h | 52 + .../transactors/sponsor/SponsorshipTransfer.h | 49 + .../transactors/token/MPTokenIssuanceCreate.h | 2 +- src/libxrpl/ledger/PaymentSandbox.cpp | 15 +- src/libxrpl/ledger/View.cpp | 24 +- .../ledger/helpers/AccountRootHelpers.cpp | 409 +- .../ledger/helpers/CredentialHelpers.cpp | 2 +- src/libxrpl/ledger/helpers/MPTokenHelpers.cpp | 91 +- src/libxrpl/ledger/helpers/NFTokenHelpers.cpp | 43 +- src/libxrpl/ledger/helpers/OfferHelpers.cpp | 2 +- .../ledger/helpers/PaymentChannelHelpers.cpp | 2 +- .../ledger/helpers/RippleStateHelpers.cpp | 83 +- src/libxrpl/ledger/helpers/SponsorHelpers.cpp | 345 ++ src/libxrpl/ledger/helpers/TokenHelpers.cpp | 44 +- src/libxrpl/protocol/Indexes.cpp | 7 + src/libxrpl/protocol/InnerObjectFormats.cpp | 8 + src/libxrpl/protocol/LedgerFormats.cpp | 1 + src/libxrpl/protocol/STObject.cpp | 6 +- src/libxrpl/protocol/STTx.cpp | 19 +- src/libxrpl/protocol/TER.cpp | 2 + src/libxrpl/protocol/TxFormats.cpp | 3 + src/libxrpl/tx/Transactor.cpp | 337 +- src/libxrpl/tx/applySteps.cpp | 3 + src/libxrpl/tx/invariants/InvariantCheck.cpp | 42 +- .../tx/invariants/SponsorshipInvariant.cpp | 158 + src/libxrpl/tx/invariants/VaultInvariant.cpp | 9 +- src/libxrpl/tx/paths/BookStep.cpp | 2 +- src/libxrpl/tx/paths/MPTEndpointStep.cpp | 3 +- .../tx/transactors/account/AccountDelete.cpp | 39 +- .../tx/transactors/account/SignerListSet.cpp | 34 +- .../tx/transactors/bridge/XChainBridge.cpp | 19 +- .../tx/transactors/check/CheckCancel.cpp | 3 +- .../tx/transactors/check/CheckCash.cpp | 37 +- .../tx/transactors/check/CheckCreate.cpp | 16 +- .../credentials/CredentialAccept.cpp | 33 +- .../credentials/CredentialCreate.cpp | 15 +- .../tx/transactors/delegate/DelegateSet.cpp | 20 +- src/libxrpl/tx/transactors/dex/AMMCreate.cpp | 19 +- src/libxrpl/tx/transactors/dex/AMMDeposit.cpp | 9 +- .../tx/transactors/dex/AMMWithdraw.cpp | 17 +- .../tx/transactors/dex/OfferCreate.cpp | 6 +- src/libxrpl/tx/transactors/did/DIDDelete.cpp | 3 +- src/libxrpl/tx/transactors/did/DIDSet.cpp | 5 +- .../tx/transactors/escrow/EscrowCancel.cpp | 5 +- .../tx/transactors/escrow/EscrowCreate.cpp | 33 +- .../tx/transactors/escrow/EscrowFinish.cpp | 19 +- .../lending/LoanBrokerCoverClawback.cpp | 2 +- .../lending/LoanBrokerCoverDeposit.cpp | 2 +- .../lending/LoanBrokerCoverWithdraw.cpp | 9 +- .../transactors/lending/LoanBrokerDelete.cpp | 10 +- .../tx/transactors/lending/LoanBrokerSet.cpp | 8 +- .../tx/transactors/lending/LoanDelete.cpp | 7 +- .../tx/transactors/lending/LoanManage.cpp | 1 + .../tx/transactors/lending/LoanPay.cpp | 6 +- .../tx/transactors/lending/LoanSet.cpp | 26 +- .../tx/transactors/nft/NFTokenAcceptOffer.cpp | 4 +- .../tx/transactors/nft/NFTokenMint.cpp | 11 +- .../tx/transactors/oracle/OracleDelete.cpp | 9 +- .../tx/transactors/oracle/OracleSet.cpp | 40 +- .../tx/transactors/payment/DepositPreauth.cpp | 34 +- .../tx/transactors/payment/Payment.cpp | 77 +- .../payment_channel/PaymentChannelCreate.cpp | 37 +- .../payment_channel/PaymentChannelFund.cpp | 18 +- .../PermissionedDomainDelete.cpp | 2 +- .../PermissionedDomainSet.cpp | 5 +- .../tx/transactors/sponsor/SponsorshipSet.cpp | 420 ++ .../sponsor/SponsorshipTransfer.cpp | 542 ++ src/libxrpl/tx/transactors/system/Batch.cpp | 29 +- .../tx/transactors/system/TicketCreate.cpp | 16 +- .../tx/transactors/token/MPTokenAuthorize.cpp | 2 +- .../token/MPTokenIssuanceCreate.cpp | 36 +- .../token/MPTokenIssuanceDestroy.cpp | 3 +- src/libxrpl/tx/transactors/token/TrustSet.cpp | 170 +- .../tx/transactors/vault/VaultClawback.cpp | 9 +- .../tx/transactors/vault/VaultCreate.cpp | 23 +- .../tx/transactors/vault/VaultDelete.cpp | 11 +- .../tx/transactors/vault/VaultDeposit.cpp | 18 +- .../tx/transactors/vault/VaultWithdraw.cpp | 9 +- src/test/app/AMMExtendedMPT_test.cpp | 2 +- src/test/app/AMMExtended_test.cpp | 2 +- src/test/app/AMMMPT_test.cpp | 2 +- src/test/app/AccountSet_test.cpp | 3 +- src/test/app/CheckMPT_test.cpp | 2 +- src/test/app/Check_test.cpp | 2 +- src/test/app/Credentials_test.cpp | 4 +- src/test/app/Delegate_test.cpp | 22 +- src/test/app/DepositAuth_test.cpp | 4 +- src/test/app/EscrowToken_test.cpp | 2 +- src/test/app/FlowMPT_test.cpp | 2 +- src/test/app/Flow_test.cpp | 2 +- src/test/app/Invariants_test.cpp | 190 +- src/test/app/LoanBroker_test.cpp | 3 +- src/test/app/Loan_test.cpp | 41 +- src/test/app/MPToken_test.cpp | 11 +- src/test/app/NFToken_test.cpp | 2 +- src/test/app/OfferMPT_test.cpp | 18 +- src/test/app/Offer_test.cpp | 18 +- src/test/app/Oracle_test.cpp | 6 +- src/test/app/Sponsor_test.cpp | 5511 +++++++++++++++++ src/test/app/Ticket_test.cpp | 10 +- src/test/app/TrustSet_test.cpp | 2 +- src/test/app/TxQ_test.cpp | 40 + src/test/app/Vault_test.cpp | 8 +- src/test/app/XChain_test.cpp | 4 +- src/test/jtx/Env.h | 18 + src/test/jtx/JTx.h | 2 +- src/test/jtx/TestHelpers.h | 12 + src/test/jtx/impl/AMMTest.cpp | 2 +- src/test/jtx/impl/Env.cpp | 27 + src/test/jtx/impl/TestHelpers.cpp | 18 + src/test/jtx/impl/multisign.cpp | 3 +- src/test/jtx/impl/owners.cpp | 18 + src/test/jtx/impl/sig.cpp | 2 +- src/test/jtx/impl/sponsor.cpp | 98 + src/test/jtx/owners.h | 54 + src/test/jtx/sponsor.h | 105 + src/test/ledger/PaymentSandbox_test.cpp | 228 +- src/test/rpc/AccountObjects_test.cpp | 269 +- src/test/rpc/AccountTx_test.cpp | 80 +- src/test/rpc/LedgerEntry_test.cpp | 59 +- src/test/rpc/Simulate_test.cpp | 153 + .../ledger_entries/AccountRootTests.cpp | 81 + .../ledger_entries/RippleStateTests.cpp | 54 + .../ledger_entries/SponsorshipTests.cpp | 329 + .../transactions/SponsorshipSetTests.cpp | 261 + .../transactions/SponsorshipTransferTests.cpp | 198 + src/xrpld/app/misc/detail/TxQ.cpp | 4 + src/xrpld/rpc/detail/TransactionSign.cpp | 6 +- .../rpc/handlers/account/AccountObjects.cpp | 55 +- src/xrpld/rpc/handlers/ledger/LedgerEntry.cpp | 22 + .../rpc/handlers/transaction/Simulate.cpp | 52 +- 170 files changed, 13151 insertions(+), 660 deletions(-) create mode 100644 include/xrpl/ledger/OwnerCounts.h create mode 100644 include/xrpl/ledger/helpers/OracleHelpers.h create mode 100644 include/xrpl/ledger/helpers/SponsorHelpers.h create mode 100644 include/xrpl/protocol_autogen/ledger_entries/Sponsorship.h create mode 100644 include/xrpl/protocol_autogen/transactions/SponsorshipSet.h create mode 100644 include/xrpl/protocol_autogen/transactions/SponsorshipTransfer.h create mode 100644 include/xrpl/tx/invariants/SponsorshipInvariant.h create mode 100644 include/xrpl/tx/transactors/sponsor/SponsorshipSet.h create mode 100644 include/xrpl/tx/transactors/sponsor/SponsorshipTransfer.h create mode 100644 src/libxrpl/ledger/helpers/SponsorHelpers.cpp create mode 100644 src/libxrpl/tx/invariants/SponsorshipInvariant.cpp create mode 100644 src/libxrpl/tx/transactors/sponsor/SponsorshipSet.cpp create mode 100644 src/libxrpl/tx/transactors/sponsor/SponsorshipTransfer.cpp create mode 100644 src/test/app/Sponsor_test.cpp create mode 100644 src/test/jtx/impl/sponsor.cpp create mode 100644 src/test/jtx/sponsor.h create mode 100644 src/tests/libxrpl/protocol_autogen/ledger_entries/SponsorshipTests.cpp create mode 100644 src/tests/libxrpl/protocol_autogen/transactions/SponsorshipSetTests.cpp create mode 100644 src/tests/libxrpl/protocol_autogen/transactions/SponsorshipTransferTests.cpp diff --git a/.cspell.config.yaml b/.cspell.config.yaml index c558fc0984..dc24bf833b 100644 --- a/.cspell.config.yaml +++ b/.cspell.config.yaml @@ -279,6 +279,8 @@ words: - sles - soci - socidb + - sponsee + - sponsees - SRPMS - sslws - statsd @@ -327,6 +329,7 @@ words: - unserviced - unshareable - unshares + - unsponsored - unsquelch - unsquelched - unsquelching diff --git a/include/xrpl/basics/base_uint.h b/include/xrpl/basics/base_uint.h index 481a7dbd77..f75100f862 100644 --- a/include/xrpl/basics/base_uint.h +++ b/include/xrpl/basics/base_uint.h @@ -595,7 +595,7 @@ template [[nodiscard]] constexpr bool operator==(BaseUInt const& lhs, BaseUInt const& rhs) { - return (lhs <=> rhs) == 0; + return (lhs <=> rhs) == 0; // NOLINT(modernize-use-nullptr) } //------------------------------------------------------------------------------ diff --git a/include/xrpl/ledger/ApplyView.h b/include/xrpl/ledger/ApplyView.h index e519013d9a..afe6b291b2 100644 --- a/include/xrpl/ledger/ApplyView.h +++ b/include/xrpl/ledger/ApplyView.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include // IWYU pragma: keep @@ -11,6 +12,7 @@ #include #include #include +#include #include #include @@ -288,7 +290,7 @@ public: // Called when the owner count changes // This is required to support PaymentSandbox virtual void - adjustOwnerCountHook(AccountID const& account, std::uint32_t cur, std::uint32_t next) + adjustOwnerCountHook(AccountID const& account, OwnerCounts const& cur, OwnerCounts const& next) { } @@ -411,6 +413,23 @@ public: emptyDirDelete(Keylet const& directory); }; +/** Bundles the mutable ledger view and the transaction being applied. + + Passed together to avoid threading two separate parameters through every + helper that needs both the view (for state reads/writes) and the + transaction (for field inspection and metadata). + + Both members are non-owning references; the caller is responsible for + ensuring that the referenced objects outlive the ApplyViewContext. + + TODO: replace with ApplyContext after it's untangled with xrpl/tx +*/ +struct ApplyViewContext +{ + ApplyView& view; + STTx const& tx; +}; + namespace directory { /** Helper functions for managing low-level directory operations. These are not part of the ApplyView interface. diff --git a/include/xrpl/ledger/OwnerCounts.h b/include/xrpl/ledger/OwnerCounts.h new file mode 100644 index 0000000000..74d60014b5 --- /dev/null +++ b/include/xrpl/ledger/OwnerCounts.h @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include // IWYU pragma: keep +#include + +#include +#include + +namespace xrpl { + +struct OwnerCounts +{ + std::uint32_t owner = 0; + std::uint32_t sponsored = 0; + std::uint32_t sponsoring = 0; + + OwnerCounts() = default; + OwnerCounts(SLE::const_ref sle) + : owner(sle->at(sfOwnerCount)) + , sponsored(sle->at(sfSponsoredOwnerCount)) + , sponsoring(sle->at(sfSponsoringOwnerCount)) + { + XRPL_ASSERT( + owner >= sponsored, + "xrpl::OwnerCounts : OwnerCount must be greater than or equal to " + "SponsoredOwnerCount"); + XRPL_ASSERT(sle->getType() == ltACCOUNT_ROOT, "xrpl::OwnerCounts : sle is AccountRoot"); + } + + [[nodiscard]] std::uint32_t + count() const + { + std::int64_t const x = static_cast(owner) - sponsored + sponsoring; + if (x < 0) + { + // LCOV_EXCL_START + UNREACHABLE("xrpl::OwnerCounts::count : count less than zero"); + return 0; + // LCOV_EXCL_STOP + } + + if (x > std::numeric_limits::max()) + return std::numeric_limits::max(); // LCOV_EXCL_LINE + return static_cast(x); + } + + auto + operator<=>(OwnerCounts const& o) const + { + if (auto cmp = count() <=> o.count(); cmp != 0) // NOLINT(modernize-use-nullptr) + return cmp; + if (auto cmp = owner <=> o.owner; cmp != 0) // NOLINT(modernize-use-nullptr) + return cmp; + if (auto cmp = sponsored <=> o.sponsored; cmp != 0) // NOLINT(modernize-use-nullptr) + return cmp; + return sponsoring <=> o.sponsoring; + } + + bool + operator==(OwnerCounts const& o) const + { + return this == &o || + (owner == o.owner && sponsored == o.sponsored && sponsoring == o.sponsoring); + } +}; + +} // namespace xrpl diff --git a/include/xrpl/ledger/PaymentSandbox.h b/include/xrpl/ledger/PaymentSandbox.h index 0117a962ff..8afe21b397 100644 --- a/include/xrpl/ledger/PaymentSandbox.h +++ b/include/xrpl/ledger/PaymentSandbox.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -107,12 +108,12 @@ public: issuerSelfDebitMPT(MPTIssue const& issue, std::uint64_t amount, std::int64_t origBalance); void - ownerCount(AccountID const& id, std::uint32_t cur, std::uint32_t next); + ownerCount(AccountID const& id, OwnerCounts const& cur, OwnerCounts const& next); // Get the adjusted owner count. Since DeferredCredits is meant to be used // in payments, and payments only decrease owner counts, return the max // remembered owner count. - [[nodiscard]] std::optional + [[nodiscard]] std::optional ownerCount(AccountID const& id) const; void @@ -124,7 +125,7 @@ private: std::map creditsIOU_; std::map creditsMPT_; - std::map ownerCounts_; + std::map ownerCounts_; }; } // namespace detail @@ -218,10 +219,11 @@ public: override; void - adjustOwnerCountHook(AccountID const& account, std::uint32_t cur, std::uint32_t next) override; + adjustOwnerCountHook(AccountID const& account, OwnerCounts const& cur, OwnerCounts const& next) + override; - [[nodiscard]] std::uint32_t - ownerCountHook(AccountID const& account, std::uint32_t count) const override; + [[nodiscard]] OwnerCounts + ownerCountHook(AccountID const& account, OwnerCounts const& count) const override; /** Apply changes to base view. diff --git a/include/xrpl/ledger/ReadView.h b/include/xrpl/ledger/ReadView.h index 724533039b..da92e0b510 100644 --- a/include/xrpl/ledger/ReadView.h +++ b/include/xrpl/ledger/ReadView.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -13,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -189,8 +191,8 @@ public: // changes that accounts make during a payment. `ownerCountHook` adjusts the // ownerCount so it returns the max value of the ownerCount so far. // This is required to support PaymentSandbox. - [[nodiscard]] virtual std::uint32_t - ownerCountHook(AccountID const& account, std::uint32_t count) const + [[nodiscard]] virtual OwnerCounts + ownerCountHook(AccountID const& account, OwnerCounts const& count) const { return count; } diff --git a/include/xrpl/ledger/View.h b/include/xrpl/ledger/View.h index c89764df1d..7fad61d407 100644 --- a/include/xrpl/ledger/View.h +++ b/include/xrpl/ledger/View.h @@ -210,8 +210,7 @@ canWithdraw(ReadView const& view, STTx const& tx); [[nodiscard]] TER doWithdraw( - ApplyView& view, - STTx const& tx, + ApplyViewContext ctx, AccountID const& senderAcct, AccountID const& dstAcct, AccountID const& sourceAcct, diff --git a/include/xrpl/ledger/helpers/AccountRootHelpers.h b/include/xrpl/ledger/helpers/AccountRootHelpers.h index d0cfc175a3..ae8cf53f2f 100644 --- a/include/xrpl/ledger/helpers/AccountRootHelpers.h +++ b/include/xrpl/ledger/helpers/AccountRootHelpers.h @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -26,21 +27,293 @@ namespace xrpl { [[nodiscard]] bool isGlobalFrozen(ReadView const& view, AccountID const& issuer); -// Calculate liquid XRP balance for an account. -// This function may be used to calculate the amount of XRP that -// the holder is able to freely spend. It subtracts reserve requirements. -// -// ownerCountAdj adjusts the owner count in case the caller calculates -// before ledger entries are added or removed. Positive to add, negative -// to subtract. -// -// @param ownerCountAdj positive to add to count, negative to reduce count. +/** Calculate liquid XRP balance for an account. + * + * This function may be used to calculate the amount of XRP that + * the holder is able to freely spend. It subtracts reserve requirements. + * + * ownerCountAdj adjusts the owner count in case the caller calculates + * before ledger entries are added or removed. Positive to add, negative + * to subtract. + * + * @param view The ledger view to read from + * @param id The account ID to check + * @param ownerCountAdj Positive to add to count, negative to reduce count + * @param j Journal for logging + * @return The liquid XRP amount available to the account + */ [[nodiscard]] XRPAmount xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, beast::Journal j); -/** Adjust the owner count up or down. */ +struct Adjustment +{ + std::int32_t ownerCountDelta = 0; + std::int32_t accountCountDelta = 0; +}; + +/** Returns the account reserve, in drops. + * + * Actual owner count can be adjusted by delta in ownerCountAdj + * Actual reserve count can be adjusted by delta in accountCountAdj + * The reserve is calculated as: + * (ownerCount + "sponsoring object count" - "sponsored object count" + additionalOwnerCount) * + * increment + (1 if not sponsored account + sponsoringAccountCount) * "reserve base" + * + * @param view The ledger view to read from + * @param sle The ledger entry for the account + * @param j Journal for logging + * @param adj Adjustment to the owner/account count (default: 0/0). Positive to add, negative to + * subtract. + * @return The account reserve amount in drops + */ +[[nodiscard]] XRPAmount +accountReserve(ReadView const& view, SLE::const_ref sle, beast::Journal j, Adjustment adj = {}); + +/** Convenience overload that accepts AccountID instead of SLE. + * + * @param view The ledger view to read from + * @param id The account ID + * @param j Journal for logging + * @param adj Adjustment to the owner/account count (default: 0/0). Positive to add, negative to + * subtract. + * @return The account reserve amount in drops + */ +[[nodiscard]] inline XRPAmount +accountReserve(ReadView const& view, AccountID const& id, beast::Journal j, Adjustment adj = {}) +{ + return accountReserve(view, view.read(keylet::account(id)), j, adj); +} + +/** Check if an account has sufficient reserve. + * + * @param view The ledger view to read from + * @param tx The transaction being processed + * @param accSle The account's ledger entry + * @param accBalance The account's balance + * @param sponsorSle The sponsor's ledger entry (if applicable) + * @param adj Adjustment to the owner/account count (default: 0/0). Positive to add, negative to + * subtract. + * @param j Journal for logging (default: null sink) + * @param insufReserveCode The transaction result code to return if the reserve is insufficient + * (default: tecINSUFFICIENT_RESERVE). + * @return Transaction result code + */ +[[nodiscard]] TER +checkReserve( + ApplyViewContext ctx, + SLE::const_ref accSle, + XRPAmount accBalance, + SLE::const_ref sponsorSle, + Adjustment adj, + beast::Journal j, + TER insufReserveCode = tecINSUFFICIENT_RESERVE); + +/** Check if an account has sufficient reserve, deriving the sponsor internally. + * + * Equivalent to the overload above, but resolves the sponsor via + * getEffectiveTxReserveSponsor(ctx, accSle) instead of taking it explicitly. Use this + * in the common case where the sponsor is simply the transaction's reserve + * sponsor for accSle. Callers that must force the account's-own-reserve branch + * (passing a null sponsor) or supply a different sponsor should use the + * explicit overload above. + * + * @param ctx The apply-view context (view + tx) + * @param accSle The account's ledger entry + * @param accBalance The account's balance + * @param adj Reserve adjustments (owner/account count deltas) + * @param j Journal for logging (default: null sink) + * @return Transaction result code + */ +[[nodiscard]] TER +checkReserve( + ApplyViewContext ctx, + SLE::const_ref accSle, + XRPAmount accBalance, + Adjustment adj, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}); + +/** Return number of the objects which reserve is covered by the account(sle) (so called "owner + * count"). Actual owner count can be adjusted by delta in ownerCountAdj. + * + * @param sle The account's ledger entry + * @param j Journal for logging + * @param ownerCountAdj Adjustment to the owner count (default: 0) + * @return The adjusted owner count + */ +std::uint32_t +ownerCount(SLE::const_ref sle, beast::Journal j, std::int32_t ownerCountAdj = 0); + +/** Increase owner-count fields when the caller supplies the sponsor. + * + * This helper does not create a ledger object. It updates reserve accounting + * after the caller has created/updated an object. + * If sponsorSle is provided, this also adjusts the account's sponsored count + * and the sponsor's sponsoring count. + * + * @param view The apply view for making changes + * @param accountSle The account's ledger entry + * @param sponsorSle The sponsor's ledger entry (if applicable) + * @param count Amount to add to the owner count + * @param j Journal for logging + */ void -adjustOwnerCount(ApplyView& view, SLE::ref sle, std::int32_t amount, beast::Journal j); +increaseOwnerCount( + ApplyView& view, + SLE::ref accountSle, + SLE::ref sponsorSle, + std::uint32_t count, + beast::Journal j); + +/** Increase owner-count fields, deriving the tx reserve sponsor internally. + * + * Equivalent to the overload above, but resolves the sponsor via + * getEffectiveTxReserveSponsor(ctx, accountSle) instead of taking it explicitly. Use + * this when the sponsor is the transaction's reserve sponsor for accountSle + * (the common create path). Deletion paths, which derive the sponsor from an + * object's sfSponsor field, should keep using the explicit overload. + * + * @param ctx The apply-view context (view + tx) + * @param accountSle The account's ledger entry + * @param count Amount to add to the owner count + * @param j Journal for logging + */ +void +increaseOwnerCount( + ApplyViewContext ctx, + SLE::ref accountSle, + std::uint32_t count, + beast::Journal j); + +/** Convenience overload that accepts AccountID instead of SLE references. + * + * @param view The apply view for making changes + * @param account The account ID + * @param sponsor The optional sponsor account ID + * @param count Amount to add to the owner count + * @param j Journal for logging + */ +inline void +increaseOwnerCount( + ApplyView& view, + AccountID const& account, + std::optional const& sponsor, + std::uint32_t count, + beast::Journal j) +{ + increaseOwnerCount( + view, + view.peek(keylet::account(account)), + sponsor ? view.peek(keylet::account(*sponsor)) : SLE::pointer(), + count, + j); +} + +/** Decrease owner-count fields when the caller supplies the sponsor. + * + * This helper does not delete a ledger object. It updates reserve accounting + * after the caller has removed an owner-counted reserve, or for special + * owner-count changes whose sponsor cannot be derived from an object's + * sfSponsor field. + * + * @param view The apply view for making changes + * @param accountSle The account's ledger entry + * @param sponsorSle The sponsor's ledger entry (if applicable) + * @param count Amount to remove from the owner count + * @param j Journal for logging + */ +void +decreaseOwnerCount( + ApplyView& view, + SLE::ref accountSle, + SLE::ref sponsorSle, + std::uint32_t count, + beast::Journal j); + +/** Convenience overload that accepts AccountID instead of SLE references. + * + * @param view The apply view for making changes + * @param account The account ID + * @param sponsor The optional sponsor account ID + * @param count Amount to remove from the owner count + * @param j Journal for logging + */ +inline void +decreaseOwnerCount( + ApplyView& view, + AccountID const& account, + std::optional const& sponsor, + std::uint32_t count, + beast::Journal j) +{ + decreaseOwnerCount( + view, + view.peek(keylet::account(account)), + sponsor ? view.peek(keylet::account(*sponsor)) : SLE::pointer(), + count, + j); +} + +/** Decrease owner-count fields for an existing ledger object. + * + * This helper derives the reserve sponsor from objectSle's sfSponsor field, + * then updates the same owner-count fields as decreaseOwnerCount. Use this + * when removing an existing object whose reserve sponsor is stored on that + * object. + * + * @param view The apply view for making changes + * @param accountSle The account's ledger entry + * @param objectSle The object's ledger entry + * @param count Amount to remove from the owner count + * @param j Journal for logging + */ +void +decreaseOwnerCountForObject( + ApplyView& view, + SLE::ref accountSle, + SLE::ref objectSle, + std::uint32_t count, + beast::Journal j); + +/** Convenience overload that accepts AccountID instead of account SLE reference. + * + * @param view The apply view for making changes + * @param account The account ID + * @param objectSle The object's ledger entry + * @param count Amount to remove from the owner count + * @param j Journal for logging + */ +inline void +decreaseOwnerCountForObject( + ApplyView& view, + AccountID const& account, + SLE::ref objectSle, + std::uint32_t count, + beast::Journal j) +{ + SLE::ref accountSle = view.peek(keylet::account(account)); + decreaseOwnerCountForObject(view, accountSle, objectSle, count, j); +} + +/** Adjust a LoanBroker's owner count. + * + * A LoanBroker's sfOwnerCount tracks the number of outstanding loans on + * that broker; it is not a reserve-backed owner count and is distinct + * from the broker's pseudo-account's owner count. Loans can never carry a + * reserve sponsor (LoanSet rejects reserve sponsorship at preflight), so + * this never involves sponsor accounting and never invokes the + * ownerCountHook used for ACCOUNT_ROOT reserve tracking. + * + * @param view The apply view for making changes + * @param brokerSle The LoanBroker's ledger entry + * @param delta Amount to add (positive) or remove (negative) from the count + * @param j Journal for logging + */ +void +adjustLoanBrokerOwnerCount( + ApplyView& view, + SLE::ref brokerSle, + std::int32_t delta, + beast::Journal j); /** Returns IOU issuer transfer fee as Rate. Rate specifies * the fee as fractions of 1 billion. For example, 1% transfer rate diff --git a/include/xrpl/ledger/helpers/EscrowHelpers.h b/include/xrpl/ledger/helpers/EscrowHelpers.h index d173c33c56..9f54e53769 100644 --- a/include/xrpl/ledger/helpers/EscrowHelpers.h +++ b/include/xrpl/ledger/helpers/EscrowHelpers.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -22,17 +23,15 @@ #include #include -#include - namespace xrpl { template TER escrowUnlockApplyHelper( - ApplyView& view, + ApplyViewContext ctx, Rate lockedRate, SLE::ref sleDest, - STAmount const& xrpBalance, + XRPAmount xrpBalance, STAmount const& amount, AccountID const& issuer, AccountID const& sender, @@ -43,10 +42,10 @@ escrowUnlockApplyHelper( template <> inline TER escrowUnlockApplyHelper( - ApplyView& view, + ApplyViewContext ctx, Rate lockedRate, SLE::ref sleDest, - STAmount const& xrpBalance, + XRPAmount xrpBalance, STAmount const& amount, AccountID const& issuer, AccountID const& sender, @@ -66,16 +65,26 @@ escrowUnlockApplyHelper( if (receiverIssuer) return tesSUCCESS; - if (!view.exists(trustLineKey) && createAsset) + if (!ctx.view.exists(trustLineKey) && createAsset) { // Can the account cover the trust line's reserve? - if (std::uint32_t const ownerCount = {sleDest->at(sfOwnerCount)}; - xrpBalance < view.fees().accountReserve(ownerCount + 1)) + auto const sponsorSle = getEffectiveTxReserveSponsor(ctx, sleDest); + if (!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE + + if (auto const ret = checkReserve( + ctx, + sleDest, + xrpBalance, + *sponsorSle, + {.ownerCountDelta = 1}, + journal, + tecNO_LINE_INSUF_RESERVE); + !isTesSuccess(ret)) { JLOG(journal.trace()) << "Trust line does not exist. " "Insufficient reserve to create line."; - - return tecNO_LINE_INSUF_RESERVE; + return ret; } Currency const currency = issue.currency; @@ -83,7 +92,7 @@ escrowUnlockApplyHelper( initialBalance.get().account = noAccount(); if (TER const ter = trustCreate( - view, // payment sandbox + ctx.view, // payment sandbox recvLow, // is dest low? issuer, // source receiver, // destination @@ -97,19 +106,20 @@ escrowUnlockApplyHelper( Issue(currency, receiver), // limit of zero 0, // quality in 0, // quality out + *sponsorSle, // sponsor journal); // journal !isTesSuccess(ter)) { return ter; // LCOV_EXCL_LINE } - view.update(sleDest); + ctx.view.update(sleDest); } - if (!view.exists(trustLineKey) && !receiverIssuer) + if (!ctx.view.exists(trustLineKey) && !receiverIssuer) return tecNO_LINE; - auto const xferRate = transferRate(view, amount); + auto const xferRate = transferRate(ctx.view, amount); // update if issuer rate is less than locked rate if (xferRate < lockedRate) lockedRate = xferRate; @@ -137,7 +147,7 @@ escrowUnlockApplyHelper( // of the funds if (!createAsset) { - auto const sleRippleState = view.peek(trustLineKey); + auto const sleRippleState = ctx.view.peek(trustLineKey); if (!sleRippleState) return tecINTERNAL; // LCOV_EXCL_LINE @@ -163,7 +173,7 @@ escrowUnlockApplyHelper( // if destination is not the issuer then transfer funds if (!receiverIssuer) { - auto const ter = directSendNoFee(view, issuer, receiver, finalAmt, true, journal); + auto const ter = directSendNoFee(ctx.view, issuer, receiver, finalAmt, true, journal); if (!isTesSuccess(ter)) return ter; // LCOV_EXCL_LINE } @@ -173,10 +183,10 @@ escrowUnlockApplyHelper( template <> inline TER escrowUnlockApplyHelper( - ApplyView& view, + ApplyViewContext ctx, Rate lockedRate, SLE::ref sleDest, - STAmount const& xrpBalance, + XRPAmount xrpBalance, STAmount const& amount, AccountID const& issuer, AccountID const& sender, @@ -189,27 +199,32 @@ escrowUnlockApplyHelper( auto const mptID = amount.get().getMptID(); auto const issuanceKey = keylet::mptokenIssuance(mptID); - if (!view.exists(keylet::mptoken(issuanceKey.key, receiver)) && createAsset && !receiverIssuer) + auto const mptKeylet = keylet::mptoken(issuanceKey.key, receiver); + if (!ctx.view.exists(mptKeylet) && createAsset && !receiverIssuer) { - if (std::uint32_t const ownerCount = {sleDest->at(sfOwnerCount)}; - xrpBalance < view.fees().accountReserve(ownerCount + 1)) - { - return tecINSUFFICIENT_RESERVE; - } + auto const sponsorSle = getEffectiveTxReserveSponsor(ctx, sleDest); + if (!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE - if (auto const ter = createMPToken(view, mptID, receiver, 0); !isTesSuccess(ter)) + if (auto const ret = checkReserve( + ctx, sleDest, xrpBalance, *sponsorSle, {.ownerCountDelta = 1}, journal); + !isTesSuccess(ret)) + return ret; + + if (auto const ter = createMPToken(ctx.view, mptID, receiver, *sponsorSle, 0); + !isTesSuccess(ter)) { return ter; // LCOV_EXCL_LINE } // update owner count. - adjustOwnerCount(view, sleDest, 1, journal); + increaseOwnerCount(ctx.view, sleDest, *sponsorSle, 1, journal); } - if (!view.exists(keylet::mptoken(issuanceKey.key, receiver)) && !receiverIssuer) + if (!ctx.view.exists(mptKeylet) && !receiverIssuer) return tecNO_PERMISSION; - auto const xferRate = transferRate(view, amount); + auto const xferRate = transferRate(ctx.view, amount); // update if issuer rate is less than locked rate if (xferRate < lockedRate) lockedRate = xferRate; @@ -232,11 +247,11 @@ escrowUnlockApplyHelper( finalAmt = amount.value() - xferFee; } return unlockEscrowMPT( - view, + ctx.view, sender, receiver, finalAmt, - view.rules().enabled(fixTokenEscrowV1) ? amount : finalAmt, + ctx.view.rules().enabled(fixTokenEscrowV1) ? amount : finalAmt, journal); } diff --git a/include/xrpl/ledger/helpers/MPTokenHelpers.h b/include/xrpl/ledger/helpers/MPTokenHelpers.h index a725871231..8a2a4a5b84 100644 --- a/include/xrpl/ledger/helpers/MPTokenHelpers.h +++ b/include/xrpl/ledger/helpers/MPTokenHelpers.h @@ -86,7 +86,7 @@ canAddHolding(ReadView const& view, MPTIssue const& mptIssue); [[nodiscard]] TER authorizeMPToken( - ApplyView& view, + ApplyViewContext ctx, XRPAmount const& priorBalance, MPTID const& mptIssuanceID, AccountID const& account, @@ -117,7 +117,7 @@ requireAuth( */ [[nodiscard]] TER enforceMPTokenAuthorization( - ApplyView& view, + ApplyViewContext ctx, MPTID const& mptIssuanceID, AccountID const& account, XRPAmount const& priorBalance, @@ -203,7 +203,7 @@ canMPTTradeAndTransfer( [[nodiscard]] TER addEmptyHolding( - ApplyView& view, + ApplyViewContext ctx, AccountID const& accountID, XRPAmount priorBalance, MPTIssue const& mptIssue, @@ -211,7 +211,7 @@ addEmptyHolding( [[nodiscard]] TER removeEmptyHolding( - ApplyView& view, + ApplyViewContext ctx, AccountID const& accountID, MPTIssue const& mptIssue, beast::Journal journal); @@ -243,6 +243,7 @@ createMPToken( ApplyView& view, MPTID const& mptIssuanceID, AccountID const& account, + SLE::ref sponsorSle, std::uint32_t const flags); TER @@ -250,6 +251,7 @@ checkCreateMPT( xrpl::ApplyView& view, xrpl::MPTIssue const& mptIssue, xrpl::AccountID const& holder, + SLE::ref sponsorSle, beast::Journal j); //------------------------------------------------------------------------------ diff --git a/include/xrpl/ledger/helpers/OracleHelpers.h b/include/xrpl/ledger/helpers/OracleHelpers.h new file mode 100644 index 0000000000..da04618e46 --- /dev/null +++ b/include/xrpl/ledger/helpers/OracleHelpers.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include // IWYU pragma: keep +#include + +#include +#include + +namespace xrpl { + +constexpr std::uint32_t kMinOracleReserveCount = 1; +constexpr std::uint32_t kMaxOracleReserveCount = 2; +constexpr std::size_t kOracleReserveCountThreshold = 5; + +template + requires requires(T const& t) { t.size(); } +inline std::uint32_t +calculateOracleReserve(T const& priceDataSeries) +{ + return priceDataSeries.size() > kOracleReserveCountThreshold ? kMaxOracleReserveCount + : kMinOracleReserveCount; +} + +inline std::uint32_t +calculateOracleReserve(SLE::const_ref oracleSle) +{ + return calculateOracleReserve(oracleSle->getFieldArray(sfPriceDataSeries)); +} + +} // namespace xrpl diff --git a/include/xrpl/ledger/helpers/RippleStateHelpers.h b/include/xrpl/ledger/helpers/RippleStateHelpers.h index ab09b931cc..398ec86bb7 100644 --- a/include/xrpl/ledger/helpers/RippleStateHelpers.h +++ b/include/xrpl/ledger/helpers/RippleStateHelpers.h @@ -156,6 +156,7 @@ trustCreate( // Issuer should be the account being set. std::uint32_t uQualityIn, std::uint32_t uQualityOut, + SLE::ref sponsorSle, beast::Journal j); [[nodiscard]] TER @@ -178,6 +179,7 @@ issueIOU( AccountID const& account, STAmount const& amount, Issue const& issue, + SLE::ref sponsorSle, beast::Journal j); [[nodiscard]] TER @@ -235,7 +237,7 @@ canTransfer(ReadView const& view, Issue const& issue, AccountID const& from, Acc /// canAddHolding() in preflight with the same View and Asset [[nodiscard]] TER addEmptyHolding( - ApplyView& view, + ApplyViewContext ctx, AccountID const& accountID, XRPAmount priorBalance, Issue const& issue, @@ -243,7 +245,7 @@ addEmptyHolding( [[nodiscard]] TER removeEmptyHolding( - ApplyView& view, + ApplyViewContext ctx, AccountID const& accountID, Issue const& issue, beast::Journal journal); diff --git a/include/xrpl/ledger/helpers/SponsorHelpers.h b/include/xrpl/ledger/helpers/SponsorHelpers.h new file mode 100644 index 0000000000..fd2315b024 --- /dev/null +++ b/include/xrpl/ledger/helpers/SponsorHelpers.h @@ -0,0 +1,184 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace xrpl { + +/** Whether the given transaction type may use reserve sponsorship (v1). + * + * Reserve sponsorship is restricted to an explicit allow-list of transaction + * types; all others reject spfSponsorReserve at preflight. + */ +bool +isReserveSponsorAllowed(TxType txType); + +/** Whether the transaction's fee is sponsored (sfSponsor present + spfSponsorFee set). */ +inline bool +isFeeSponsored(STTx const& tx) +{ + return tx.isFieldPresent(sfSponsor) && ((tx.getFieldU32(sfSponsorFlags) & spfSponsorFee) != 0u); +} + +/** Whether the transaction's reserve is sponsored (sfSponsor present + spfSponsorReserve set). */ +inline bool +isReserveSponsored(STTx const& tx) +{ + return tx.isFieldPresent(sfSponsor) && + ((tx.getFieldU32(sfSponsorFlags) & spfSponsorReserve) != 0u); +} + +/** Return the AccountID of the transaction's reserve sponsor, or nullopt if unsponsored. */ +std::optional +getTxReserveSponsorID(STTx const& tx); + +/** Return a mutable SLE for the transaction's reserve sponsor account. + * + * @param ctx The apply-view context (view + tx) + * @return The sponsor account SLE, a null pointer if the tx is not + * reserve-sponsored, or tecINTERNAL if the sponsor account cannot + * be loaded (an already-checked invariant). + */ +std::expected +getTxReserveSponsor(ApplyViewContext ctx); + +/** Return a read-only SLE for the transaction's reserve sponsor account. + * + * @param view The ledger read view + * @param tx The transaction to inspect + * @return The sponsor account SLE, a null pointer if the tx is not + * reserve-sponsored, or tecINTERNAL if the sponsor account cannot + * be loaded (an already-checked invariant). + */ +std::expected +getTxReserveSponsor(ReadView const& view, STTx const& tx); + +/** The transaction's reserve sponsor for the given account, if applicable. + * + * A reserve sponsor only covers the transaction submitter's own objects, so + * this returns the tx reserve sponsor SLE only when accountSle is the tx's own + * (non-pseudo) account; otherwise it returns a null sponsor pointer. This is + * the single source of truth for the "sponsor applies to tx.Account only" rule + * that the sponsor-deriving helper overloads in AccountRootHelpers rely on. + * + * @param ctx The apply-view context (view + tx) + * @param accountSle The account whose sponsor is being resolved + * @return The sponsor SLE (nullptr if unsponsored), or tecINTERNAL if the + * sponsor account cannot be loaded (an already-checked invariant) + */ +[[nodiscard]] std::expected +getEffectiveTxReserveSponsor(ApplyViewContext ctx, SLE::const_ref accountSle); + +/** Return the AccountID stored in the given sponsor field of a ledger entry, or nullopt if absent. + */ +std::optional +getLedgerEntryReserveSponsorID(SLE::const_ref sle, SF_ACCOUNT const& field = sfSponsor); + +/** Return a mutable SLE for the reserve sponsor recorded on a ledger entry. + * + * Reads the sponsor AccountID from @p field on @p sle and peeks the + * corresponding account root in @p view. + * + * @param view The mutable apply view + * @param sle The ledger entry whose sponsor field is inspected + * @param field The field that holds the sponsor AccountID (defaults to sfSponsor) + * @return The sponsor account SLE, or a null pointer if the entry is unsponsored. + */ +SLE::pointer +getLedgerEntryReserveSponsor( + ApplyView& view, + SLE::const_ref sle, + SF_ACCOUNT const& field = sfSponsor); + +/** Stamp a reserve sponsor onto a ledger entry using an explicit sponsor SLE. + * + * Sets @p field on @p sle to the AccountID from @p sponsorSle. A no-op when + * @p sponsorSle is null (unsponsored). For RippleState entries the field must + * be sfHighSponsor or sfLowSponsor; for all other entry types it must be + * sfSponsor. + * + * @param sle The ledger entry to stamp + * @param sponsorSle The sponsor's account root SLE (null → no-op) + * @param field The sponsor field to set (defaults to sfSponsor) + */ +void +addSponsorToLedgerEntry( + SLE::ref sle, + SLE::const_ref sponsorSle, + SF_ACCOUNT const& field = sfSponsor); + +/** Stamp the transaction's reserve sponsor onto a newly-created ledger entry. + * + * Equivalent to the overload above, but resolves the sponsor via + * getTxReserveSponsor(ctx) instead of taking it explicitly. A no-op when the + * transaction is not reserve-sponsored. The entry is assumed to be owned by + * the transaction submitter, which is the only account a tx reserve sponsor + * can cover. + */ +void +addSponsorToLedgerEntry(ApplyViewContext ctx, SLE::ref sle, SF_ACCOUNT const& field = sfSponsor); + +/** Remove the reserve sponsor field from a ledger entry. + * + * A no-op when @p field is not present on @p sle. For RippleState entries + * the field must be sfHighSponsor or sfLowSponsor; for all other entry types + * it must be sfSponsor. + * + * @param sle The ledger entry to modify + * @param field The sponsor field to clear (defaults to sfSponsor) + */ +void +removeSponsorFromLedgerEntry(SLE::ref sle, SF_ACCOUNT const& field = sfSponsor); + +/** Whether @p account is the owner of a ledger entry for sponsorship purposes. + * + * Ownership rules vary by entry type. For RippleState entries the owner is + * whichever side of the trust line holds the reserve. For credentials, the + * owner is the subject once accepted and the issuer before acceptance. + * + * @param view The ledger read view (used for SignerList lookup) + * @param sle The ledger entry whose owner is checked + * @param account The candidate account to match against + * @return true if @p account owns @p sle, false otherwise. + */ +bool +isLedgerEntryOwner(ReadView const& view, SLE const& sle, AccountID const& account); + +/** Whether this ledger entry type can have a reserve sponsor attached to it. */ +bool +isLedgerEntrySupportedBySponsorship(SLE const& sle); + +/** Return the number of owner-count units the ledger entry consumes. + * + * Most entries cost 1. Exceptions: Oracles scale with their price-data series + * size, Vaults cost 2 (vault + pseudo-account), and legacy SignerList entries + * (pre-MultiSignReserve) cost 2 + signer count. + */ +std::uint32_t +getLedgerEntryOwnerCount(SLE const& sle); + +/** Return the SField used to store the reserve sponsor for @p owner on @p sle. + * + * For most entry types this is sfSponsor. RippleState entries use + * sfHighSponsor or sfLowSponsor depending on which side of the trust line + * @p owner holds. + * + * @param sle The ledger entry + * @param owner The account whose sponsor field is needed + * @return sfHighSponsor, sfLowSponsor, or sfSponsor as appropriate. + */ +SF_ACCOUNT const& +getLedgerEntrySponsorField(SLE const& sle, AccountID const& owner); + +} // namespace xrpl diff --git a/include/xrpl/ledger/helpers/TokenHelpers.h b/include/xrpl/ledger/helpers/TokenHelpers.h index 32f785a0d6..33d9761d84 100644 --- a/include/xrpl/ledger/helpers/TokenHelpers.h +++ b/include/xrpl/ledger/helpers/TokenHelpers.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -302,7 +303,7 @@ canAddHolding(ReadView const& view, Asset const& asset); [[nodiscard]] TER addEmptyHolding( - ApplyView& view, + ApplyViewContext ctx, AccountID const& accountID, XRPAmount priorBalance, Asset const& asset, @@ -310,7 +311,7 @@ addEmptyHolding( [[nodiscard]] TER removeEmptyHolding( - ApplyView& view, + ApplyViewContext ctx, AccountID const& accountID, Asset const& asset, beast::Journal journal); @@ -371,6 +372,7 @@ accountSend( AccountID const& to, STAmount const& saAmount, beast::Journal j, + SLE::ref sponsorSle = {}, WaiveTransferFee waiveFee = WaiveTransferFee::No, AllowMPTOverflow allowOverflow = AllowMPTOverflow::No); diff --git a/include/xrpl/protocol/Book.h b/include/xrpl/protocol/Book.h index 476bdba35a..92d1353929 100644 --- a/include/xrpl/protocol/Book.h +++ b/include/xrpl/protocol/Book.h @@ -74,9 +74,9 @@ operator==(Book const& lhs, Book const& rhs) [[nodiscard]] constexpr std::weak_ordering operator<=>(Book const& lhs, Book const& rhs) { - if (auto const c{lhs.in <=> rhs.in}; c != 0) + if (auto const c{lhs.in <=> rhs.in}; c != 0) // NOLINT(modernize-use-nullptr) return c; - if (auto const c{lhs.out <=> rhs.out}; c != 0) + if (auto const c{lhs.out <=> rhs.out}; c != 0) // NOLINT(modernize-use-nullptr) return c; // Manually compare optionals diff --git a/include/xrpl/protocol/Fees.h b/include/xrpl/protocol/Fees.h index 4e79204f4c..2f9159cd9b 100644 --- a/include/xrpl/protocol/Fees.h +++ b/include/xrpl/protocol/Fees.h @@ -2,7 +2,6 @@ #include -#include #include namespace xrpl { @@ -39,13 +38,13 @@ struct Fees /** Returns the account reserve given the owner count, in drops. - The reserve is calculated as the reserve base plus - the reserve increment times the number of increments. + The reserve is calculated as the reserve base times the number of accounts plus the reserve + increment times the number of increments. */ [[nodiscard]] XRPAmount - accountReserve(std::size_t ownerCount) const + accountReserve(std::uint32_t ownerCount, std::uint32_t accountCount) const { - return reserve + ownerCount * increment; + return (reserve * accountCount) + (increment * ownerCount); } }; diff --git a/include/xrpl/protocol/Indexes.h b/include/xrpl/protocol/Indexes.h index 053a66787f..79ecd05ad5 100644 --- a/include/xrpl/protocol/Indexes.h +++ b/include/xrpl/protocol/Indexes.h @@ -142,6 +142,10 @@ ticket(uint256 const& key) Keylet signerList(AccountID const& account) noexcept; +/** A Sponsorship */ +Keylet +sponsorship(AccountID const& sponsor, AccountID const& sponsee) noexcept; + /** A Check */ /** @{ */ Keylet diff --git a/include/xrpl/protocol/Issue.h b/include/xrpl/protocol/Issue.h index 3d556e83eb..a4980404cd 100644 --- a/include/xrpl/protocol/Issue.h +++ b/include/xrpl/protocol/Issue.h @@ -84,7 +84,7 @@ operator==(Issue const& lhs, Issue const& rhs) [[nodiscard]] constexpr std::weak_ordering operator<=>(Issue const& lhs, Issue const& rhs) { - if (auto const c{lhs.currency <=> rhs.currency}; c != 0) + if (auto const c{lhs.currency <=> rhs.currency}; c != 0) // NOLINT(modernize-use-nullptr) return c; if (isXRP(lhs.currency)) diff --git a/include/xrpl/protocol/LedgerFormats.h b/include/xrpl/protocol/LedgerFormats.h index 5b8a8cc2c5..142e5763be 100644 --- a/include/xrpl/protocol/LedgerFormats.h +++ b/include/xrpl/protocol/LedgerFormats.h @@ -208,7 +208,11 @@ enum LedgerEntryType : std::uint16_t { LEDGER_OBJECT(Loan, \ LSF_FLAG(lsfLoanDefault, 0x00010000) \ LSF_FLAG(lsfLoanImpaired, 0x00020000) \ - LSF_FLAG(lsfLoanOverpayment, 0x00040000)) /* True, loan allows overpayments */ + LSF_FLAG(lsfLoanOverpayment, 0x00040000)) /* True, loan allows overpayments */ \ + \ + LEDGER_OBJECT(Sponsorship, \ + LSF_FLAG(lsfSponsorshipRequireSignForFee, 0x00010000) \ + LSF_FLAG(lsfSponsorshipRequireSignForReserve, 0x00020000)) // clang-format on diff --git a/include/xrpl/protocol/STObject.h b/include/xrpl/protocol/STObject.h index fe5c611648..9abf56e91b 100644 --- a/include/xrpl/protocol/STObject.h +++ b/include/xrpl/protocol/STObject.h @@ -229,10 +229,10 @@ public: [[nodiscard]] AccountID getAccountID(SField const& field) const; - /** The account responsible for the fee and authorization: the delegate when + /** The account responsible for the authorization: the delegate when sfDelegate is present, otherwise the account. */ [[nodiscard]] AccountID - getFeePayer() const; + getInitiator() const; [[nodiscard]] Blob getFieldVL(SField const& field) const; diff --git a/include/xrpl/protocol/STTx.h b/include/xrpl/protocol/STTx.h index b36207bf61..5d5424e623 100644 --- a/include/xrpl/protocol/STTx.h +++ b/include/xrpl/protocol/STTx.h @@ -141,6 +141,9 @@ public: [[nodiscard]] std::vector const& getBatchTransactionIDs() const; + [[nodiscard]] AccountID + getFeePayerID() const; + private: /** Check the signature. @param rules The current ledger rules. diff --git a/include/xrpl/protocol/TER.h b/include/xrpl/protocol/TER.h index 54b081f358..730d021254 100644 --- a/include/xrpl/protocol/TER.h +++ b/include/xrpl/protocol/TER.h @@ -225,6 +225,7 @@ enum TERcodes : TERUnderlyingType { // create a pseudo-account terNO_DELEGATE_PERMISSION, // Delegate does not have permission terLOCKED, // MPT is locked + terNO_PERMISSION, // No permission but retry }; //------------------------------------------------------------------------------ @@ -368,6 +369,7 @@ enum TECcodes : TERUnderlyingType { // reclaimed after those networks reset. tecNO_DELEGATE_PERMISSION = 198, tecBAD_PROOF = 199, + tecNO_SPONSOR_PERMISSION = 200, }; //------------------------------------------------------------------------------ diff --git a/include/xrpl/protocol/TxFlags.h b/include/xrpl/protocol/TxFlags.h index 461afd24e7..1d4f33a39d 100644 --- a/include/xrpl/protocol/TxFlags.h +++ b/include/xrpl/protocol/TxFlags.h @@ -102,7 +102,8 @@ inline constexpr FlagValue tfUniversalMask = ~tfUniversal; TRANSACTION(Payment, \ TF_FLAG(tfNoRippleDirect, 0x00010000) \ TF_FLAG(tfPartialPayment, 0x00020000) \ - TF_FLAG(tfLimitQuality, 0x00040000), \ + TF_FLAG(tfLimitQuality, 0x00040000) \ + TF_FLAG(tfSponsorCreatedAccount, 0x00080000), \ MASK_ADJ(0)) \ \ TRANSACTION(TrustSet, \ @@ -141,7 +142,7 @@ inline constexpr FlagValue tfUniversalMask = ~tfUniversal; TF_FLAG(tfMPTCanTrade, lsfMPTCanTrade) \ TF_FLAG(tfMPTCanTransfer, lsfMPTCanTransfer) \ TF_FLAG(tfMPTCanClawback, lsfMPTCanClawback) \ - TF_FLAG(tfMPTCanHoldConfidentialBalance, lsfMPTCanHoldConfidentialBalance), \ + TF_FLAG(tfMPTCanHoldConfidentialBalance, lsfMPTCanHoldConfidentialBalance), \ MASK_ADJ(0)) \ \ TRANSACTION(MPTokenAuthorize, \ @@ -215,6 +216,20 @@ inline constexpr FlagValue tfUniversalMask = ~tfUniversal; TF_FLAG(tfLoanDefault, 0x00010000) \ TF_FLAG(tfLoanImpair, 0x00020000) \ TF_FLAG(tfLoanUnimpair, 0x00040000), \ + MASK_ADJ(0)) \ + \ + TRANSACTION(SponsorshipSet, \ + TF_FLAG(tfSponsorshipSetRequireSignForFee, 0x00010000) \ + TF_FLAG(tfSponsorshipClearRequireSignForFee, 0x00020000) \ + TF_FLAG(tfSponsorshipSetRequireSignForReserve, 0x00040000) \ + TF_FLAG(tfSponsorshipClearRequireSignForReserve, 0x00080000) \ + TF_FLAG(tfDeleteObject, 0x00100000), \ + MASK_ADJ(0)) \ + \ + TRANSACTION(SponsorshipTransfer, \ + TF_FLAG(tfSponsorshipEnd, 0x00010000) \ + TF_FLAG(tfSponsorshipCreate, 0x00020000) \ + TF_FLAG(tfSponsorshipReassign, 0x00040000), \ MASK_ADJ(0)) // clang-format on @@ -444,6 +459,12 @@ getAsfFlagMap() #pragma pop_macro("ACCOUNTSET_FLAG_TO_MAP") #pragma pop_macro("ACCOUNTSET_FLAGS") +// Sponsor flags (spf) + +inline constexpr FlagValue spfSponsorFee = 1; +inline constexpr FlagValue spfSponsorReserve = 2; +inline constexpr FlagValue spfSponsorFlagMask = ~(spfSponsorFee | spfSponsorReserve); + } // namespace xrpl // NOLINTEND(readability-identifier-naming) diff --git a/include/xrpl/protocol/detail/features.macro b/include/xrpl/protocol/detail/features.macro index 3f078561a3..bfe03a6303 100644 --- a/include/xrpl/protocol/detail/features.macro +++ b/include/xrpl/protocol/detail/features.macro @@ -15,6 +15,7 @@ // Add new amendments to the top of this list. // Keep it sorted in reverse chronological order. +XRPL_FEATURE(Sponsor, Supported::Yes, VoteBehavior::DefaultNo) XRPL_FEATURE(BatchV1_1, Supported::Yes, VoteBehavior::DefaultNo) XRPL_FEATURE(LendingProtocolV1_1, Supported::No, VoteBehavior::DefaultNo) XRPL_FEATURE(ConfidentialTransfer, Supported::Yes, VoteBehavior::DefaultNo) diff --git a/include/xrpl/protocol/detail/ledger_entries.macro b/include/xrpl/protocol/detail/ledger_entries.macro index 2f403708c3..90810e06d2 100644 --- a/include/xrpl/protocol/detail/ledger_entries.macro +++ b/include/xrpl/protocol/detail/ledger_entries.macro @@ -127,29 +127,32 @@ LEDGER_ENTRY(ltTICKET, 0x0054, Ticket, ticket, ({ \sa keylet::account */ LEDGER_ENTRY(ltACCOUNT_ROOT, 0x0061, AccountRoot, account, ({ - {sfAccount, SoeRequired}, - {sfSequence, SoeRequired}, - {sfBalance, SoeRequired}, - {sfOwnerCount, SoeRequired}, - {sfPreviousTxnID, SoeRequired}, - {sfPreviousTxnLgrSeq, SoeRequired}, - {sfAccountTxnID, SoeOptional}, - {sfRegularKey, SoeOptional}, - {sfEmailHash, SoeOptional}, - {sfWalletLocator, SoeOptional}, - {sfWalletSize, SoeOptional}, - {sfMessageKey, SoeOptional}, - {sfTransferRate, SoeOptional}, - {sfDomain, SoeOptional}, - {sfTickSize, SoeOptional}, - {sfTicketCount, SoeOptional}, - {sfNFTokenMinter, SoeOptional}, - {sfMintedNFTokens, SoeDefault}, - {sfBurnedNFTokens, SoeDefault}, - {sfFirstNFTokenSequence, SoeOptional}, - {sfAMMID, SoeOptional}, // pseudo-account designator - {sfVaultID, SoeOptional}, // pseudo-account designator - {sfLoanBrokerID, SoeOptional}, // pseudo-account designator + {sfAccount, SoeRequired}, + {sfSequence, SoeRequired}, + {sfBalance, SoeRequired}, + {sfOwnerCount, SoeRequired}, + {sfPreviousTxnID, SoeRequired}, + {sfPreviousTxnLgrSeq, SoeRequired}, + {sfAccountTxnID, SoeOptional}, + {sfRegularKey, SoeOptional}, + {sfEmailHash, SoeOptional}, + {sfWalletLocator, SoeOptional}, + {sfWalletSize, SoeOptional}, + {sfMessageKey, SoeOptional}, + {sfTransferRate, SoeOptional}, + {sfDomain, SoeOptional}, + {sfTickSize, SoeOptional}, + {sfTicketCount, SoeOptional}, + {sfNFTokenMinter, SoeOptional}, + {sfMintedNFTokens, SoeDefault}, + {sfBurnedNFTokens, SoeDefault}, + {sfFirstNFTokenSequence, SoeOptional}, + {sfSponsoredOwnerCount, SoeDefault}, + {sfSponsoringOwnerCount, SoeDefault}, + {sfSponsoringAccountCount, SoeDefault}, + {sfAMMID, SoeOptional}, // pseudo-account designator + {sfVaultID, SoeOptional}, // pseudo-account designator + {sfLoanBrokerID, SoeOptional}, // pseudo-account designator })) /** A ledger object which contains a list of object identifiers. @@ -286,6 +289,8 @@ LEDGER_ENTRY(ltRIPPLE_STATE, 0x0072, RippleState, state, ({ {sfHighNode, SoeOptional}, {sfHighQualityIn, SoeOptional}, {sfHighQualityOut, SoeOptional}, + {sfHighSponsor, SoeOptional}, + {sfLowSponsor, SoeOptional}, })) /** The ledger object which lists the network's fee settings. @@ -616,5 +621,20 @@ LEDGER_ENTRY(ltLOAN, 0x0089, Loan, loan, ({ {sfLoanScale, SoeDefault}, })) +/** A ledger object representing a sponsorship. + \sa keylet::sponsorship + */ +LEDGER_ENTRY(ltSPONSORSHIP, 0x0090, Sponsorship, sponsorship, ({ + {sfPreviousTxnID, SoeRequired}, + {sfPreviousTxnLgrSeq, SoeRequired}, + {sfOwner, SoeRequired}, + {sfSponsee, SoeRequired}, + {sfFeeAmount, SoeOptional}, + {sfMaxFee, SoeOptional}, + {sfRemainingOwnerCount, SoeDefault}, + {sfOwnerNode, SoeRequired}, + {sfSponseeNode, SoeRequired}, +})) + #undef EXPAND #undef LEDGER_ENTRY_DUPLICATE diff --git a/include/xrpl/protocol/detail/sfields.macro b/include/xrpl/protocol/detail/sfields.macro index 0d453eea11..4ef76c8b75 100644 --- a/include/xrpl/protocol/detail/sfields.macro +++ b/include/xrpl/protocol/detail/sfields.macro @@ -114,6 +114,11 @@ TYPED_SFIELD(sfLateInterestRate, UINT32, 66) // 1/10 basis points (bi TYPED_SFIELD(sfCloseInterestRate, UINT32, 67) // 1/10 basis points (bips) TYPED_SFIELD(sfOverpaymentInterestRate, UINT32, 68) // 1/10 basis points (bips) TYPED_SFIELD(sfConfidentialBalanceVersion, UINT32, 69) +TYPED_SFIELD(sfSponsoredOwnerCount, UINT32, 70) +TYPED_SFIELD(sfSponsoringOwnerCount, UINT32, 71) +TYPED_SFIELD(sfSponsoringAccountCount, UINT32, 72) +TYPED_SFIELD(sfRemainingOwnerCount, UINT32, 73) +TYPED_SFIELD(sfSponsorFlags, UINT32, 74) // 64-bit integers (common) TYPED_SFIELD(sfIndexNext, UINT64, 1) @@ -148,6 +153,7 @@ TYPED_SFIELD(sfLockedAmount, UINT64, 29, SField::kSmdBaseTen|SFie TYPED_SFIELD(sfVaultNode, UINT64, 30) TYPED_SFIELD(sfLoanBrokerNode, UINT64, 31) TYPED_SFIELD(sfConfidentialOutstandingAmount, UINT64, 32, SField::kSmdBaseTen|SField::kSmdDefault) +TYPED_SFIELD(sfSponseeNode, UINT64, 33) // 128-bit TYPED_SFIELD(sfEmailHash, UINT128, 1) @@ -209,6 +215,7 @@ TYPED_SFIELD(sfLoanBrokerID, UINT256, 37, TYPED_SFIELD(sfLoanID, UINT256, 38) TYPED_SFIELD(sfReferenceHolding, UINT256, 39) TYPED_SFIELD(sfBlindingFactor, UINT256, 40) +TYPED_SFIELD(sfObjectID, UINT256, 41) // number (common) TYPED_SFIELD(sfNumber, NUMBER, 1) @@ -268,6 +275,8 @@ TYPED_SFIELD(sfPrice, AMOUNT, 28) TYPED_SFIELD(sfSignatureReward, AMOUNT, 29) TYPED_SFIELD(sfMinAccountCreateAmount, AMOUNT, 30) TYPED_SFIELD(sfLPTokenBalance, AMOUNT, 31) +TYPED_SFIELD(sfFeeAmount, AMOUNT, 32) +TYPED_SFIELD(sfMaxFee, AMOUNT, 33) // variable length (common) TYPED_SFIELD(sfPublicKey, VL, 1) @@ -343,6 +352,11 @@ TYPED_SFIELD(sfIssuingChainDoor, ACCOUNT, 23) TYPED_SFIELD(sfSubject, ACCOUNT, 24) TYPED_SFIELD(sfBorrower, ACCOUNT, 25) TYPED_SFIELD(sfCounterparty, ACCOUNT, 26) +TYPED_SFIELD(sfSponsor, ACCOUNT, 27) +TYPED_SFIELD(sfHighSponsor, ACCOUNT, 28) +TYPED_SFIELD(sfLowSponsor, ACCOUNT, 29) +TYPED_SFIELD(sfCounterpartySponsor, ACCOUNT, 30) +TYPED_SFIELD(sfSponsee, ACCOUNT, 31) // vector of 256-bit TYPED_SFIELD(sfIndexes, VECTOR256, 1, SField::kSmdNever) @@ -407,6 +421,7 @@ UNTYPED_SFIELD(sfRawTransaction, OBJECT, 34) UNTYPED_SFIELD(sfBatchSigner, OBJECT, 35) UNTYPED_SFIELD(sfBook, OBJECT, 36) UNTYPED_SFIELD(sfCounterpartySignature, OBJECT, 37, SField::kSmdDefault, SField::kNotSigning) +UNTYPED_SFIELD(sfSponsorSignature, OBJECT, 38, SField::kSmdDefault, SField::kNotSigning) // array of objects (common) // ARRAY/1 is reserved for end of array diff --git a/include/xrpl/protocol/detail/transactions.macro b/include/xrpl/protocol/detail/transactions.macro index b0c4f66bae..e805596c00 100644 --- a/include/xrpl/protocol/detail/transactions.macro +++ b/include/xrpl/protocol/detail/transactions.macro @@ -1165,6 +1165,35 @@ TRANSACTION(ttCONFIDENTIAL_MPT_CLAWBACK, 89, ConfidentialMPTClawback, {sfZKProof, SoeRequired}, })) +/** This transaction transfers sponsorship on an object/account. */ +#if TRANSACTION_INCLUDE +# include +#endif +TRANSACTION(ttSPONSORSHIP_TRANSFER, 90, SponsorshipTransfer, + Delegation::NotDelegable, + featureSponsor, + NoPriv, + ({ + {sfObjectID, SoeOptional}, + {sfSponsee, SoeOptional}, +})) + +/** This transaction creates a Sponsorship object. */ +#if TRANSACTION_INCLUDE +# include +#endif +TRANSACTION(ttSPONSORSHIP_SET, 91, SponsorshipSet, + Delegation::Delegable, + featureSponsor, + NoPriv, + ({ + {sfCounterpartySponsor, SoeOptional}, + {sfSponsee, SoeOptional}, + {sfFeeAmount, SoeOptional}, + {sfMaxFee, SoeOptional}, + {sfRemainingOwnerCount, SoeOptional}, +})) + /** This system-generated transaction type is used to update the status of the various amendments. For details, see: https://xrpl.org/amendments.html diff --git a/include/xrpl/protocol/jss.h b/include/xrpl/protocol/jss.h index 191ed385f3..b20b71661a 100644 --- a/include/xrpl/protocol/jss.h +++ b/include/xrpl/protocol/jss.h @@ -558,6 +558,9 @@ JSS(source_account); // in: PathRequest, RipplePathFind JSS(source_amount); // in: PathRequest, RipplePathFind JSS(source_currencies); // in: PathRequest, RipplePathFind JSS(source_tag); // out: AccountChannels +JSS(sponsee); // in: LedgerEntry +JSS(sponsor); // in: LedgerEntry +JSS(sponsored); // in: AccountObjects JSS(stand_alone); // out: NetworkOPs JSS(standard_deviation); // out: get_aggregate_price JSS(start); // in: TxHistory diff --git a/include/xrpl/protocol_autogen/ledger_entries/AccountRoot.h b/include/xrpl/protocol_autogen/ledger_entries/AccountRoot.h index f9a12a027f..725f46437e 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/AccountRoot.h +++ b/include/xrpl/protocol_autogen/ledger_entries/AccountRoot.h @@ -447,6 +447,78 @@ public: return this->sle_->isFieldPresent(sfFirstNFTokenSequence); } + /** + * @brief Get sfSponsoredOwnerCount (SoeDefault) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getSponsoredOwnerCount() const + { + if (hasSponsoredOwnerCount()) + return this->sle_->at(sfSponsoredOwnerCount); + return std::nullopt; + } + + /** + * @brief Check if sfSponsoredOwnerCount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasSponsoredOwnerCount() const + { + return this->sle_->isFieldPresent(sfSponsoredOwnerCount); + } + + /** + * @brief Get sfSponsoringOwnerCount (SoeDefault) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getSponsoringOwnerCount() const + { + if (hasSponsoringOwnerCount()) + return this->sle_->at(sfSponsoringOwnerCount); + return std::nullopt; + } + + /** + * @brief Check if sfSponsoringOwnerCount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasSponsoringOwnerCount() const + { + return this->sle_->isFieldPresent(sfSponsoringOwnerCount); + } + + /** + * @brief Get sfSponsoringAccountCount (SoeDefault) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getSponsoringAccountCount() const + { + if (hasSponsoringAccountCount()) + return this->sle_->at(sfSponsoringAccountCount); + return std::nullopt; + } + + /** + * @brief Check if sfSponsoringAccountCount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasSponsoringAccountCount() const + { + return this->sle_->isFieldPresent(sfSponsoringAccountCount); + } + /** * @brief Get sfAMMID (SoeOptional) * @return The field value, or std::nullopt if not present. @@ -786,6 +858,39 @@ public: return *this; } + /** + * @brief Set sfSponsoredOwnerCount (SoeDefault) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setSponsoredOwnerCount(std::decay_t const& value) + { + object_[sfSponsoredOwnerCount] = value; + return *this; + } + + /** + * @brief Set sfSponsoringOwnerCount (SoeDefault) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setSponsoringOwnerCount(std::decay_t const& value) + { + object_[sfSponsoringOwnerCount] = value; + return *this; + } + + /** + * @brief Set sfSponsoringAccountCount (SoeDefault) + * @return Reference to this builder for method chaining. + */ + AccountRootBuilder& + setSponsoringAccountCount(std::decay_t const& value) + { + object_[sfSponsoringAccountCount] = value; + return *this; + } + /** * @brief Set sfAMMID (SoeOptional) * @return Reference to this builder for method chaining. diff --git a/include/xrpl/protocol_autogen/ledger_entries/RippleState.h b/include/xrpl/protocol_autogen/ledger_entries/RippleState.h index e8debfe792..388a062ec1 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/RippleState.h +++ b/include/xrpl/protocol_autogen/ledger_entries/RippleState.h @@ -243,6 +243,54 @@ public: { return this->sle_->isFieldPresent(sfHighQualityOut); } + + /** + * @brief Get sfHighSponsor (SoeOptional) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getHighSponsor() const + { + if (hasHighSponsor()) + return this->sle_->at(sfHighSponsor); + return std::nullopt; + } + + /** + * @brief Check if sfHighSponsor is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasHighSponsor() const + { + return this->sle_->isFieldPresent(sfHighSponsor); + } + + /** + * @brief Get sfLowSponsor (SoeOptional) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getLowSponsor() const + { + if (hasLowSponsor()) + return this->sle_->at(sfLowSponsor); + return std::nullopt; + } + + /** + * @brief Check if sfLowSponsor is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasLowSponsor() const + { + return this->sle_->isFieldPresent(sfLowSponsor); + } }; /** @@ -410,6 +458,28 @@ public: return *this; } + /** + * @brief Set sfHighSponsor (SoeOptional) + * @return Reference to this builder for method chaining. + */ + RippleStateBuilder& + setHighSponsor(std::decay_t const& value) + { + object_[sfHighSponsor] = value; + return *this; + } + + /** + * @brief Set sfLowSponsor (SoeOptional) + * @return Reference to this builder for method chaining. + */ + RippleStateBuilder& + setLowSponsor(std::decay_t const& value) + { + object_[sfLowSponsor] = value; + return *this; + } + /** * @brief Build and return the completed RippleState wrapper. * @param index The ledger entry index. diff --git a/include/xrpl/protocol_autogen/ledger_entries/Sponsorship.h b/include/xrpl/protocol_autogen/ledger_entries/Sponsorship.h new file mode 100644 index 0000000000..c309a38aef --- /dev/null +++ b/include/xrpl/protocol_autogen/ledger_entries/Sponsorship.h @@ -0,0 +1,344 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::ledger_entries { + +class SponsorshipBuilder; + +/** + * @brief Ledger Entry: Sponsorship + * + * Type: ltSPONSORSHIP (0x0090) + * RPC Name: sponsorship + * + * Immutable wrapper around SLE providing type-safe field access. + * Use SponsorshipBuilder to construct new ledger entries. + */ +class Sponsorship : public LedgerEntryBase +{ +public: + static constexpr LedgerEntryType entryType = ltSPONSORSHIP; + + /** + * @brief Construct a Sponsorship ledger entry wrapper from an existing SLE object. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + explicit Sponsorship(SLE::const_pointer sle) + : LedgerEntryBase(std::move(sle)) + { + // Verify ledger entry type + if (sle_->getType() != entryType) + { + throw std::runtime_error("Invalid ledger entry type for Sponsorship"); + } + } + + // Ledger entry-specific field getters + + /** + * @brief Get sfPreviousTxnID (SoeRequired) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getPreviousTxnID() const + { + return this->sle_->at(sfPreviousTxnID); + } + + /** + * @brief Get sfPreviousTxnLgrSeq (SoeRequired) + * @return The field value. + */ + [[nodiscard]] + SF_UINT32::type::value_type + getPreviousTxnLgrSeq() const + { + return this->sle_->at(sfPreviousTxnLgrSeq); + } + + /** + * @brief Get sfOwner (SoeRequired) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getOwner() const + { + return this->sle_->at(sfOwner); + } + + /** + * @brief Get sfSponsee (SoeRequired) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getSponsee() const + { + return this->sle_->at(sfSponsee); + } + + /** + * @brief Get sfFeeAmount (SoeOptional) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getFeeAmount() const + { + if (hasFeeAmount()) + return this->sle_->at(sfFeeAmount); + return std::nullopt; + } + + /** + * @brief Check if sfFeeAmount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasFeeAmount() const + { + return this->sle_->isFieldPresent(sfFeeAmount); + } + + /** + * @brief Get sfMaxFee (SoeOptional) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getMaxFee() const + { + if (hasMaxFee()) + return this->sle_->at(sfMaxFee); + return std::nullopt; + } + + /** + * @brief Check if sfMaxFee is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasMaxFee() const + { + return this->sle_->isFieldPresent(sfMaxFee); + } + + /** + * @brief Get sfRemainingOwnerCount (SoeDefault) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getRemainingOwnerCount() const + { + if (hasRemainingOwnerCount()) + return this->sle_->at(sfRemainingOwnerCount); + return std::nullopt; + } + + /** + * @brief Check if sfRemainingOwnerCount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasRemainingOwnerCount() const + { + return this->sle_->isFieldPresent(sfRemainingOwnerCount); + } + + /** + * @brief Get sfOwnerNode (SoeRequired) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getOwnerNode() const + { + return this->sle_->at(sfOwnerNode); + } + + /** + * @brief Get sfSponseeNode (SoeRequired) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getSponseeNode() const + { + return this->sle_->at(sfSponseeNode); + } +}; + +/** + * @brief Builder for Sponsorship ledger entries. + * + * Provides a fluent interface for constructing ledger entries with method chaining. + * Uses STObject internally for flexible ledger entry construction. + * Inherits common field setters from LedgerEntryBuilderBase. + */ +class SponsorshipBuilder : public LedgerEntryBuilderBase +{ +public: + /** + * @brief Construct a new SponsorshipBuilder with required fields. + * @param previousTxnID The sfPreviousTxnID field value. + * @param previousTxnLgrSeq The sfPreviousTxnLgrSeq field value. + * @param owner The sfOwner field value. + * @param sponsee The sfSponsee field value. + * @param ownerNode The sfOwnerNode field value. + * @param sponseeNode The sfSponseeNode field value. + */ + SponsorshipBuilder(std::decay_t const& previousTxnID,std::decay_t const& previousTxnLgrSeq,std::decay_t const& owner,std::decay_t const& sponsee,std::decay_t const& ownerNode,std::decay_t const& sponseeNode) + : LedgerEntryBuilderBase(ltSPONSORSHIP) + { + setPreviousTxnID(previousTxnID); + setPreviousTxnLgrSeq(previousTxnLgrSeq); + setOwner(owner); + setSponsee(sponsee); + setOwnerNode(ownerNode); + setSponseeNode(sponseeNode); + } + + /** + * @brief Construct a SponsorshipBuilder from an existing SLE object. + * @param sle The existing ledger entry to copy from. + * @throws std::runtime_error if the ledger entry type doesn't match. + */ + SponsorshipBuilder(SLE::const_pointer sle) + { + if (sle->at(sfLedgerEntryType) != ltSPONSORSHIP) + { + throw std::runtime_error("Invalid ledger entry type for Sponsorship"); + } + object_ = *sle; + } + + /** @brief Ledger entry-specific field setters */ + + /** + * @brief Set sfPreviousTxnID (SoeRequired) + * @return Reference to this builder for method chaining. + */ + SponsorshipBuilder& + setPreviousTxnID(std::decay_t const& value) + { + object_[sfPreviousTxnID] = value; + return *this; + } + + /** + * @brief Set sfPreviousTxnLgrSeq (SoeRequired) + * @return Reference to this builder for method chaining. + */ + SponsorshipBuilder& + setPreviousTxnLgrSeq(std::decay_t const& value) + { + object_[sfPreviousTxnLgrSeq] = value; + return *this; + } + + /** + * @brief Set sfOwner (SoeRequired) + * @return Reference to this builder for method chaining. + */ + SponsorshipBuilder& + setOwner(std::decay_t const& value) + { + object_[sfOwner] = value; + return *this; + } + + /** + * @brief Set sfSponsee (SoeRequired) + * @return Reference to this builder for method chaining. + */ + SponsorshipBuilder& + setSponsee(std::decay_t const& value) + { + object_[sfSponsee] = value; + return *this; + } + + /** + * @brief Set sfFeeAmount (SoeOptional) + * @return Reference to this builder for method chaining. + */ + SponsorshipBuilder& + setFeeAmount(std::decay_t const& value) + { + object_[sfFeeAmount] = value; + return *this; + } + + /** + * @brief Set sfMaxFee (SoeOptional) + * @return Reference to this builder for method chaining. + */ + SponsorshipBuilder& + setMaxFee(std::decay_t const& value) + { + object_[sfMaxFee] = value; + return *this; + } + + /** + * @brief Set sfRemainingOwnerCount (SoeDefault) + * @return Reference to this builder for method chaining. + */ + SponsorshipBuilder& + setRemainingOwnerCount(std::decay_t const& value) + { + object_[sfRemainingOwnerCount] = value; + return *this; + } + + /** + * @brief Set sfOwnerNode (SoeRequired) + * @return Reference to this builder for method chaining. + */ + SponsorshipBuilder& + setOwnerNode(std::decay_t const& value) + { + object_[sfOwnerNode] = value; + return *this; + } + + /** + * @brief Set sfSponseeNode (SoeRequired) + * @return Reference to this builder for method chaining. + */ + SponsorshipBuilder& + setSponseeNode(std::decay_t const& value) + { + object_[sfSponseeNode] = value; + return *this; + } + + /** + * @brief Build and return the completed Sponsorship wrapper. + * @param index The ledger entry index. + * @return The constructed ledger entry wrapper. + */ + Sponsorship + build(uint256 const& index) + { + return Sponsorship{std::make_shared(std::move(object_), index)}; + } +}; + +} // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/transactions/SponsorshipSet.h b/include/xrpl/protocol_autogen/transactions/SponsorshipSet.h new file mode 100644 index 0000000000..2831c8a077 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/SponsorshipSet.h @@ -0,0 +1,290 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class SponsorshipSetBuilder; + +/** + * @brief Transaction: SponsorshipSet + * + * Type: ttSPONSORSHIP_SET (91) + * Delegable: Delegation::Delegable + * Amendment: featureSponsor + * Privileges: NoPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use SponsorshipSetBuilder to construct new transactions. + */ +class SponsorshipSet : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttSPONSORSHIP_SET; + + /** + * @brief Construct a SponsorshipSet transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit SponsorshipSet(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for SponsorshipSet"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfCounterpartySponsor (SoeOptional) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getCounterpartySponsor() const + { + if (hasCounterpartySponsor()) + { + return this->tx_->at(sfCounterpartySponsor); + } + return std::nullopt; + } + + /** + * @brief Check if sfCounterpartySponsor is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasCounterpartySponsor() const + { + return this->tx_->isFieldPresent(sfCounterpartySponsor); + } + + /** + * @brief Get sfSponsee (SoeOptional) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getSponsee() const + { + if (hasSponsee()) + { + return this->tx_->at(sfSponsee); + } + return std::nullopt; + } + + /** + * @brief Check if sfSponsee is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasSponsee() const + { + return this->tx_->isFieldPresent(sfSponsee); + } + + /** + * @brief Get sfFeeAmount (SoeOptional) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getFeeAmount() const + { + if (hasFeeAmount()) + { + return this->tx_->at(sfFeeAmount); + } + return std::nullopt; + } + + /** + * @brief Check if sfFeeAmount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasFeeAmount() const + { + return this->tx_->isFieldPresent(sfFeeAmount); + } + + /** + * @brief Get sfMaxFee (SoeOptional) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getMaxFee() const + { + if (hasMaxFee()) + { + return this->tx_->at(sfMaxFee); + } + return std::nullopt; + } + + /** + * @brief Check if sfMaxFee is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasMaxFee() const + { + return this->tx_->isFieldPresent(sfMaxFee); + } + + /** + * @brief Get sfRemainingOwnerCount (SoeOptional) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getRemainingOwnerCount() const + { + if (hasRemainingOwnerCount()) + { + return this->tx_->at(sfRemainingOwnerCount); + } + return std::nullopt; + } + + /** + * @brief Check if sfRemainingOwnerCount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasRemainingOwnerCount() const + { + return this->tx_->isFieldPresent(sfRemainingOwnerCount); + } +}; + +/** + * @brief Builder for SponsorshipSet transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses STObject internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class SponsorshipSetBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new SponsorshipSetBuilder with required fields. + * @param account The account initiating the transaction. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + SponsorshipSetBuilder(SF_ACCOUNT::type::value_type account, + std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttSPONSORSHIP_SET, account, sequence, fee) + { + } + + /** + * @brief Construct a SponsorshipSetBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + SponsorshipSetBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttSPONSORSHIP_SET) + { + throw std::runtime_error("Invalid transaction type for SponsorshipSetBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfCounterpartySponsor (SoeOptional) + * @return Reference to this builder for method chaining. + */ + SponsorshipSetBuilder& + setCounterpartySponsor(std::decay_t const& value) + { + object_[sfCounterpartySponsor] = value; + return *this; + } + + /** + * @brief Set sfSponsee (SoeOptional) + * @return Reference to this builder for method chaining. + */ + SponsorshipSetBuilder& + setSponsee(std::decay_t const& value) + { + object_[sfSponsee] = value; + return *this; + } + + /** + * @brief Set sfFeeAmount (SoeOptional) + * @return Reference to this builder for method chaining. + */ + SponsorshipSetBuilder& + setFeeAmount(std::decay_t const& value) + { + object_[sfFeeAmount] = value; + return *this; + } + + /** + * @brief Set sfMaxFee (SoeOptional) + * @return Reference to this builder for method chaining. + */ + SponsorshipSetBuilder& + setMaxFee(std::decay_t const& value) + { + object_[sfMaxFee] = value; + return *this; + } + + /** + * @brief Set sfRemainingOwnerCount (SoeOptional) + * @return Reference to this builder for method chaining. + */ + SponsorshipSetBuilder& + setRemainingOwnerCount(std::decay_t const& value) + { + object_[sfRemainingOwnerCount] = value; + return *this; + } + + /** + * @brief Build and return the SponsorshipSet wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + SponsorshipSet + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return SponsorshipSet{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/SponsorshipTransfer.h b/include/xrpl/protocol_autogen/transactions/SponsorshipTransfer.h new file mode 100644 index 0000000000..bc27f1603b --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/SponsorshipTransfer.h @@ -0,0 +1,179 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class SponsorshipTransferBuilder; + +/** + * @brief Transaction: SponsorshipTransfer + * + * Type: ttSPONSORSHIP_TRANSFER (90) + * Delegable: Delegation::NotDelegable + * Amendment: featureSponsor + * Privileges: NoPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use SponsorshipTransferBuilder to construct new transactions. + */ +class SponsorshipTransfer : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttSPONSORSHIP_TRANSFER; + + /** + * @brief Construct a SponsorshipTransfer transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit SponsorshipTransfer(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for SponsorshipTransfer"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfObjectID (SoeOptional) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getObjectID() const + { + if (hasObjectID()) + { + return this->tx_->at(sfObjectID); + } + return std::nullopt; + } + + /** + * @brief Check if sfObjectID is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasObjectID() const + { + return this->tx_->isFieldPresent(sfObjectID); + } + + /** + * @brief Get sfSponsee (SoeOptional) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getSponsee() const + { + if (hasSponsee()) + { + return this->tx_->at(sfSponsee); + } + return std::nullopt; + } + + /** + * @brief Check if sfSponsee is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasSponsee() const + { + return this->tx_->isFieldPresent(sfSponsee); + } +}; + +/** + * @brief Builder for SponsorshipTransfer transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses STObject internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class SponsorshipTransferBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new SponsorshipTransferBuilder with required fields. + * @param account The account initiating the transaction. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + SponsorshipTransferBuilder(SF_ACCOUNT::type::value_type account, + std::optional sequence = std::nullopt, + std::optional fee = std::nullopt +) + : TransactionBuilderBase(ttSPONSORSHIP_TRANSFER, account, sequence, fee) + { + } + + /** + * @brief Construct a SponsorshipTransferBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + SponsorshipTransferBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttSPONSORSHIP_TRANSFER) + { + throw std::runtime_error("Invalid transaction type for SponsorshipTransferBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfObjectID (SoeOptional) + * @return Reference to this builder for method chaining. + */ + SponsorshipTransferBuilder& + setObjectID(std::decay_t const& value) + { + object_[sfObjectID] = value; + return *this; + } + + /** + * @brief Set sfSponsee (SoeOptional) + * @return Reference to this builder for method chaining. + */ + SponsorshipTransferBuilder& + setSponsee(std::decay_t const& value) + { + object_[sfSponsee] = value; + return *this; + } + + /** + * @brief Build and return the SponsorshipTransfer wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + SponsorshipTransfer + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return SponsorshipTransfer{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/tx/ApplyContext.h b/include/xrpl/tx/ApplyContext.h index ca778387cd..f64957dd35 100644 --- a/include/xrpl/tx/ApplyContext.h +++ b/include/xrpl/tx/ApplyContext.h @@ -127,6 +127,15 @@ public: TER checkInvariants(TER const result, XRPAmount const fee); + ApplyViewContext + getApplyViewContext() + { + XRPL_ASSERT( + view_.has_value(), + "xrpl::ApplyContext::getApplyViewContext : view_ emplaced in constructor"); + return {.view = *view_, .tx = tx}; + } + private: static TER failInvariantCheck(TER const result); diff --git a/include/xrpl/tx/Transactor.h b/include/xrpl/tx/Transactor.h index bc5e8c80e7..bdb51b06ec 100644 --- a/include/xrpl/tx/Transactor.h +++ b/include/xrpl/tx/Transactor.h @@ -10,8 +10,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -126,6 +128,21 @@ struct PreflightResult; // Needed for preflight specialization class Change; +enum class FeePayerType { + Account, + Delegate, + SponsorCoSigned, + SponsorPreFunded, +}; + +struct FeePayer +{ + AccountID id; + Keylet keylet; + SF_AMOUNT const& balanceField; + FeePayerType type{FeePayerType::Account}; +}; + class Transactor { protected: @@ -298,6 +315,10 @@ public: return T::checkGranularSemantics(view, tx, heldGranularPermissions); } + + static NotTEC + checkSponsor(ReadView const& view, STTx const& tx); + ///////////////////////////////////////////////////// // Interface used by AccountDelete @@ -459,6 +480,9 @@ private: std::pair reset(XRPAmount fee); + static FeePayer + getFeePayer(ReadView const& view, STTx const& tx); + TER consumeSeqProxy(SLE::pointer const& sleAccount); TER diff --git a/include/xrpl/tx/invariants/InvariantCheck.h b/include/xrpl/tx/invariants/InvariantCheck.h index d19f34635d..0e6b2a361d 100644 --- a/include/xrpl/tx/invariants/InvariantCheck.h +++ b/include/xrpl/tx/invariants/InvariantCheck.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -441,7 +442,9 @@ using InvariantChecks = std::tuple< ValidMPTPayment, ValidAmounts, ValidMPTTransfer, - ObjectHasPseudoAccount>; + ObjectHasPseudoAccount, + SponsorshipOwnerCountsMatch, + SponsorshipAccountCountMatchesField>; /** * @brief get a tuple of all invariant checks diff --git a/include/xrpl/tx/invariants/SponsorshipInvariant.h b/include/xrpl/tx/invariants/SponsorshipInvariant.h new file mode 100644 index 0000000000..e664685c16 --- /dev/null +++ b/include/xrpl/tx/invariants/SponsorshipInvariant.h @@ -0,0 +1,61 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include + +namespace xrpl { + +/** + * @brief Invariant: Sponsored owner counts are balanced. + * + * The following checks are made for every transaction: + * - The sum of all per-account deltas of `sfSponsoredOwnerCount` equals + * the sum of all per-account deltas of `sfSponsoringOwnerCount`. + * - Account OwnerCount must be greater than or equal to SponsoredOwnerCount. + * - The net delta of sponsored object owner counts (the owner-count + * magnitude of sponsored ledger entries) equals the net delta of + * `sfSponsoredOwnerCount`. + */ +class SponsorshipOwnerCountsMatch +{ + std::int64_t deltaSponsoredOwnerCount_ = 0; + std::int64_t deltaSponsoringOwnerCount_ = 0; + std::int64_t deltaSponsoredObjectOwnerCount_ = 0; + std::uint64_t ownerCountBelowSponsored_ = 0; + +public: + void + visitEntry(bool, SLE::const_ref, SLE::const_ref); + + [[nodiscard]] bool + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; +}; + +/** + * @brief Invariant: Sponsoring account relationships tracked consistently. + * + * The following check is made for every transaction: + * - The net delta of `sfSponsoringAccountCount` across all accounts equals + * the net delta of the count of ltACCOUNT_ROOT entries having + * `sfSponsor` present (presence transitions only: add/remove). + */ +class SponsorshipAccountCountMatchesField +{ + std::int64_t deltaSponsoringAccountCount_ = 0; + std::int64_t deltaSponsorFieldPresence_ = 0; + +public: + void + visitEntry(bool, SLE::const_ref, SLE::const_ref); + + [[nodiscard]] bool + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; +}; + +} // namespace xrpl diff --git a/include/xrpl/tx/paths/AMMOffer.h b/include/xrpl/tx/paths/AMMOffer.h index 40a9cf40b3..6e7a5ceca5 100644 --- a/include/xrpl/tx/paths/AMMOffer.h +++ b/include/xrpl/tx/paths/AMMOffer.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -110,7 +111,10 @@ public: send(Args&&... args) { return accountSend( - std::forward(args)..., WaiveTransferFee::Yes, AllowMPTOverflow::Yes); + std::forward(args)..., + SLE::pointer(), + WaiveTransferFee::Yes, + AllowMPTOverflow::Yes); } [[nodiscard]] bool diff --git a/include/xrpl/tx/paths/Offer.h b/include/xrpl/tx/paths/Offer.h index 164a933ba1..d7daa30cab 100644 --- a/include/xrpl/tx/paths/Offer.h +++ b/include/xrpl/tx/paths/Offer.h @@ -234,7 +234,8 @@ template TER TOffer::send(Args&&... args) { - return accountSend(std::forward(args)..., WaiveTransferFee::No, AllowMPTOverflow::Yes); + return accountSend( + std::forward(args)..., SLE::pointer(), WaiveTransferFee::No, AllowMPTOverflow::Yes); } template diff --git a/include/xrpl/tx/transactors/sponsor/SponsorshipSet.h b/include/xrpl/tx/transactors/sponsor/SponsorshipSet.h new file mode 100644 index 0000000000..3310c995ae --- /dev/null +++ b/include/xrpl/tx/transactors/sponsor/SponsorshipSet.h @@ -0,0 +1,52 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace xrpl { + +class SponsorshipSet : public Transactor +{ +public: + static constexpr auto kConsequencesFactory = ConsequencesFactoryType::Normal; + + explicit SponsorshipSet(ApplyContext& ctx) : Transactor(ctx) + { + } + + static TxConsequences + makeTxConsequences(PreflightContext const& ctx); + + static std::uint32_t + getFlagsMask(PreflightContext const& ctx); + + static NotTEC + preflight(PreflightContext const& ctx); + + static TER + preclaim(PreclaimContext const& ctx); + + TER + doApply() override; + + void + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; + + [[nodiscard]] bool + finalizeInvariants( + STTx const& tx, + TER result, + XRPAmount fee, + ReadView const& view, + beast::Journal const& j) override; +}; + +} // namespace xrpl diff --git a/include/xrpl/tx/transactors/sponsor/SponsorshipTransfer.h b/include/xrpl/tx/transactors/sponsor/SponsorshipTransfer.h new file mode 100644 index 0000000000..cf0022ab72 --- /dev/null +++ b/include/xrpl/tx/transactors/sponsor/SponsorshipTransfer.h @@ -0,0 +1,49 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace xrpl { + +class SponsorshipTransfer : public Transactor +{ +public: + static constexpr auto kConsequencesFactory = ConsequencesFactoryType::Normal; + + explicit SponsorshipTransfer(ApplyContext& ctx) : Transactor(ctx) + { + } + + static std::uint32_t + getFlagsMask(PreflightContext const& ctx); + + static NotTEC + preflight(PreflightContext const& ctx); + + static TER + preclaim(PreclaimContext const& ctx); + + TER + doApply() override; + + void + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; + + [[nodiscard]] bool + finalizeInvariants( + STTx const& tx, + TER result, + XRPAmount fee, + ReadView const& view, + beast::Journal const& j) override; +}; + +} // namespace xrpl diff --git a/include/xrpl/tx/transactors/token/MPTokenIssuanceCreate.h b/include/xrpl/tx/transactors/token/MPTokenIssuanceCreate.h index 045911c7ae..5d35f65f44 100644 --- a/include/xrpl/tx/transactors/token/MPTokenIssuanceCreate.h +++ b/include/xrpl/tx/transactors/token/MPTokenIssuanceCreate.h @@ -76,7 +76,7 @@ public: beast::Journal const& j) override; static std::expected - create(ApplyView& view, beast::Journal journal, MPTCreateArgs const& args); + create(ApplyViewContext ctx, beast::Journal journal, MPTCreateArgs const& args); }; } // namespace xrpl diff --git a/src/libxrpl/ledger/PaymentSandbox.cpp b/src/libxrpl/ledger/PaymentSandbox.cpp index 97e3e53cbf..a7a8962095 100644 --- a/src/libxrpl/ledger/PaymentSandbox.cpp +++ b/src/libxrpl/ledger/PaymentSandbox.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -170,7 +171,7 @@ DeferredCredits::issuerSelfDebitMPT( } void -DeferredCredits::ownerCount(AccountID const& id, std::uint32_t cur, std::uint32_t next) +DeferredCredits::ownerCount(AccountID const& id, OwnerCounts const& cur, OwnerCounts const& next) { auto const v = std::max(cur, next); auto r = ownerCounts_.emplace(id, v); @@ -181,7 +182,7 @@ DeferredCredits::ownerCount(AccountID const& id, std::uint32_t cur, std::uint32_ } } -std::optional +std::optional DeferredCredits::ownerCount(AccountID const& id) const { auto i = ownerCounts_.find(id); @@ -391,10 +392,10 @@ PaymentSandbox::balanceHookSelfIssueMPT(xrpl::MPTIssue const& issue, std::int64_ return STAmount{issue}; } -std::uint32_t -PaymentSandbox::ownerCountHook(AccountID const& account, std::uint32_t count) const +OwnerCounts +PaymentSandbox::ownerCountHook(AccountID const& account, OwnerCounts const& count) const { - std::uint32_t result = count; + OwnerCounts result = count; for (auto curSB = this; curSB != nullptr; curSB = curSB->ps_) { if (auto adj = curSB->tab_.ownerCount(account)) @@ -442,8 +443,8 @@ PaymentSandbox::issuerSelfDebitHookMPT( void PaymentSandbox::adjustOwnerCountHook( AccountID const& account, - std::uint32_t cur, - std::uint32_t next) + OwnerCounts const& cur, + OwnerCounts const& next) { tab_.ownerCount(account, cur, next); } diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index ebac76a754..8116f4f641 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -431,8 +432,7 @@ canWithdraw(ReadView const& view, STTx const& tx) TER doWithdraw( - ApplyView& view, - STTx const& tx, + ApplyViewContext ctx, AccountID const& senderAcct, AccountID const& dstAcct, AccountID const& sourceAcct, @@ -440,23 +440,24 @@ doWithdraw( STAmount const& amount, beast::Journal j) { + auto const dstSle = ctx.view.read(keylet::account(dstAcct)); + // Create trust line or MPToken for the receiving account if (dstAcct == senderAcct) { - if (auto const ter = addEmptyHolding(view, senderAcct, priorBalance, amount.asset(), j); + if (auto const ter = addEmptyHolding(ctx, senderAcct, priorBalance, amount.asset(), j); !isTesSuccess(ter) && ter != tecDUPLICATE) return ter; } else { - auto dstSle = view.read(keylet::account(dstAcct)); - if (auto err = verifyDepositPreauth(tx, view, senderAcct, dstAcct, dstSle, j)) + if (auto err = verifyDepositPreauth(ctx.tx, ctx.view, senderAcct, dstAcct, dstSle, j)) return err; } // Sanity check if (accountHolds( - view, + ctx.view, sourceAcct, amount.asset(), FreezeHandling::IgnoreFreeze, @@ -469,9 +470,18 @@ doWithdraw( // LCOV_EXCL_STOP } + // A reserve sponsor only covers tx.Account's own objects, so resolve the + // sponsor against the destination. accountSend can auto-create a holding + // for dstAcct; keying on the destination ensures a third-party destination's + // holding is never stamped with the tx's reserve sponsor. + auto const sponsorSle = getEffectiveTxReserveSponsor(ctx, dstSle); + if (!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE + // Move the funds directly from the broker's pseudo-account to the // dstAcct - return accountSend(view, sourceAcct, dstAcct, amount, j, WaiveTransferFee::Yes); + return accountSend( + ctx.view, sourceAcct, dstAcct, amount, j, *sponsorSle, WaiveTransferFee::Yes); } TER diff --git a/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp b/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp index 1c4acc7bc4..faca4ebfb6 100644 --- a/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp +++ b/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp @@ -7,7 +7,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -15,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -41,46 +44,226 @@ isGlobalFrozen(ReadView const& view, AccountID const& issuer) return false; } +namespace { + // An owner count cannot be negative. If adjustment would cause a negative // owner count, clamp the owner count at 0. Similarly for overflow. This // adjustment allows the ownerCount to be adjusted up or down in multiple steps. // If id != std::nullopt, then do error reporting. // // Returns adjusted owner count. -static std::uint32_t +std::uint32_t confineOwnerCount( - std::uint32_t current, - std::int32_t adjustment, + std::uint32_t currentOwnerCount, + std::int32_t ownerCountAdj, std::optional const& id = std::nullopt, beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) { - std::uint32_t adjusted{current + adjustment}; - if (adjustment > 0) + std::uint32_t totalOwnerCount{currentOwnerCount + ownerCountAdj}; + if (ownerCountAdj > 0) { // Overflow is well defined on unsigned - if (adjusted < current) + if (totalOwnerCount < currentOwnerCount) { + // LCOV_EXCL_START if (id) { JLOG(j.fatal()) << "Account " << *id << " owner count exceeds max!"; } - adjusted = std::numeric_limits::max(); + totalOwnerCount = std::numeric_limits::max(); + // LCOV_EXCL_STOP } } else { // Underflow is well defined on unsigned - if (adjusted > current) + if (totalOwnerCount > currentOwnerCount) { + // LCOV_EXCL_START if (id) { JLOG(j.fatal()) << "Account " << *id << " owner count set below 0!"; } - adjusted = 0; + totalOwnerCount = 0; XRPL_ASSERT(!id, "xrpl::confineOwnerCount : id is not set"); + // LCOV_EXCL_STOP } } - return adjusted; + return totalOwnerCount; +} + +// Returns the number of account reserves funded by this account: 1 for itself (0 if sponsored by +// another account) plus the count of accounts it sponsors. +std::uint32_t +accountCountImpl(SLE::const_ref sle, std::int32_t accountCountAdj, beast::Journal j) +{ + bool const isSponsored = sle->isFieldPresent(sfSponsor); + std::int64_t const sponsoringAccountCount = sle->getFieldU32(sfSponsoringAccountCount); + std::int64_t const currentAccountCount = (isSponsored ? 0 : 1) + sponsoringAccountCount; + + std::int64_t totalAccountCount{currentAccountCount + accountCountAdj}; + if (totalAccountCount > std::numeric_limits::max()) + { + // LCOV_EXCL_START + JLOG(j.fatal()) << "Reserve count exceeds max!"; + totalAccountCount = std::numeric_limits::max(); + // LCOV_EXCL_STOP + } + else if (totalAccountCount < 0) + { + // LCOV_EXCL_START + UNREACHABLE("xrpl::accountCountImpl : Reserve count set below 0"); + JLOG(j.fatal()) << "Reserve count set below 0"; + totalAccountCount = 0; + // LCOV_EXCL_STOP + } + + return totalAccountCount; +} + +std::uint32_t +adjustOwnerCountImpl( + ApplyView& view, + SLE::ref sle, + SF_UINT32 const& sfield, + AccountID const& accID, + std::int32_t ownerCountAdj, + beast::Journal j) +{ + std::uint32_t const currentOwnerCount = sle->at(sfield); + std::uint32_t const totalOwnerCount = + confineOwnerCount(currentOwnerCount, ownerCountAdj, accID, j); + sle->at(sfield) = totalOwnerCount; + view.update(sle); + return totalOwnerCount; +} + +void +adjustOwnerCountSigned( + ApplyView& view, + SLE::ref accountSle, + SLE::ref sponsorSle, + std::int32_t adjustment, + beast::Journal j) +{ + if (view.rules().enabled(featureSponsor)) + { + XRPL_ASSERT(accountSle, "xrpl::adjustOwnerCountSigned : valid account sle"); + if (!accountSle) + return; // LCOV_EXCL_LINE + + auto const accountID = accountSle->getAccountID(sfAccount); + bool const validType = accountSle->getType() == ltACCOUNT_ROOT; + XRPL_ASSERT(validType, "xrpl::adjustOwnerCountSigned : valid account sle type"); + if (!validType) + return; // LCOV_EXCL_LINE + + XRPL_ASSERT(adjustment, "xrpl::adjustOwnerCountSigned : nonzero adjustment input"); + + OwnerCounts const currentOwnerCount(accountSle); + OwnerCounts totalOwnerCount(currentOwnerCount); + + if (sponsorSle) + { + bool const validSponsorType = sponsorSle->getType() == ltACCOUNT_ROOT; + XRPL_ASSERT(validSponsorType, "xrpl::adjustOwnerCountSigned : valid sponsor sle type"); + if (!validSponsorType) + return; // LCOV_EXCL_LINE + auto const sponsorID = sponsorSle->getAccountID(sfAccount); + + totalOwnerCount.sponsored = adjustOwnerCountImpl( + view, accountSle, sfSponsoredOwnerCount, accountID, adjustment, j); + + { + OwnerCounts const sponsorCurrent(sponsorSle); + OwnerCounts sponsorAdjustment(sponsorCurrent); + sponsorAdjustment.sponsoring = adjustOwnerCountImpl( + view, sponsorSle, sfSponsoringOwnerCount, sponsorID, adjustment, j); + view.adjustOwnerCountHook(sponsorID, sponsorCurrent, sponsorAdjustment); + } + + auto sponsorshipSle = view.peek(keylet::sponsorship(sponsorID, accountID)); + if (sponsorshipSle && adjustment > 0) + { + // Only decrease the pre-funded ReserveCount on Sponsorship if we assign new + // objects. Removing/reassigning ownership of the object doesn't increase + // RemainingOwnerCount back. Don't call hook because this counter is not something + // that requires reserve (like other sf...OwnerCounts do). + adjustOwnerCountImpl( + view, sponsorshipSle, sfRemainingOwnerCount, sponsorID, -adjustment, j); + } + } + + totalOwnerCount.owner = + adjustOwnerCountImpl(view, accountSle, sfOwnerCount, accountID, adjustment, j); + view.adjustOwnerCountHook(accountID, currentOwnerCount, totalOwnerCount); + } + else + { + XRPL_ASSERT(accountSle, "xrpl::adjustOwnerCountSigned : valid account sle"); + if (!accountSle) + return; + // the remaining are only asserts to preserve existing behavior + XRPL_ASSERT(sponsorSle == nullptr, "xrpl::adjustOwnerCountSigned : sponsor not enabled"); + XRPL_ASSERT( + accountSle->getType() == ltACCOUNT_ROOT, + "xrpl::adjustOwnerCountSigned : valid account sle type"); + XRPL_ASSERT(adjustment, "xrpl::adjustOwnerCount : nonzero adjustment input"); + std::uint32_t const current{accountSle->getFieldU32(sfOwnerCount)}; + AccountID const id = (*accountSle)[sfAccount]; + std::uint32_t const adjusted = confineOwnerCount(current, adjustment, id, j); + + OwnerCounts const currentOwnerCount(accountSle); + OwnerCounts finalOwnerCount(currentOwnerCount); + finalOwnerCount.owner = adjusted; + + view.adjustOwnerCountHook(id, currentOwnerCount, finalOwnerCount); + accountSle->at(sfOwnerCount) = adjusted; + view.update(accountSle); + } +} + +} // namespace + +std::uint32_t +ownerCount(SLE::const_ref sle, beast::Journal j, std::int32_t ownerCountAdj) +{ + XRPL_ASSERT(sle && sle->getType() == ltACCOUNT_ROOT, "xrpl::ownerCount : sle is account root"); + + AccountID const id = sle->getAccountID(sfAccount); + std::uint32_t const currentOwnerCount = sle->at(sfOwnerCount); + std::uint32_t const sponsoredOwnerCount = sle->at(sfSponsoredOwnerCount); + std::uint32_t const sponsoringOwnerCount = sle->at(sfSponsoringOwnerCount); + + XRPL_ASSERT( + currentOwnerCount >= sponsoredOwnerCount, + "xrpl::ownerCount : OwnerCount must be greater than or equal to SponsoredOwnerCount"); + + std::int64_t deltaCount = + static_cast(ownerCountAdj) - sponsoredOwnerCount + sponsoringOwnerCount; + + if (deltaCount > std::numeric_limits::max()) + { + // LCOV_EXCL_START + deltaCount = std::numeric_limits::max(); + JLOG(j.fatal()) << "Account " << id << " delta count exceeds max, " + << "adjustment: " << ownerCountAdj + << ", sponsoredCount: " << sponsoredOwnerCount + << ", sponsoringOwnerCount: " << sponsoringOwnerCount; + // LCOV_EXCL_STOP + } + else if (deltaCount < std::numeric_limits::min()) + { + // LCOV_EXCL_START + deltaCount = std::numeric_limits::min(); + JLOG(j.fatal()) << "Account " << id << " delta count is below min, " + << "adjustment: " << ownerCountAdj + << ", sponsoredCount: " << sponsoredOwnerCount + << ", sponsoringCount: " << sponsoringOwnerCount; + // LCOV_EXCL_STOP + } + + return confineOwnerCount(currentOwnerCount, deltaCount); } XRPAmount @@ -91,12 +274,14 @@ xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, return beast::kZero; // Return balance minus reserve - std::uint32_t const ownerCount = - confineOwnerCount(view.ownerCountHook(id, sle->getFieldU32(sfOwnerCount)), ownerCountAdj); + std::uint32_t const currentOwnerCount = + confineOwnerCount(view.ownerCountHook(id, OwnerCounts(sle)).count(), ownerCountAdj); + std::uint32_t const currentAccountCount = accountCountImpl(sle, 0, j); // Pseudo-accounts have no reserve requirement - auto const reserve = - isPseudoAccount(sle) ? XRPAmount{0} : view.fees().accountReserve(ownerCount); + auto const reserve = isPseudoAccount(sle) + ? XRPAmount{0} + : view.fees().accountReserve(currentOwnerCount, currentAccountCount); auto const fullBalance = sle->getFieldAmount(sfBalance); @@ -108,7 +293,7 @@ xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, << " amount=" << amount.getFullText() << " fullBalance=" << fullBalance.getFullText() << " balance=" << balance.getFullText() << " reserve=" << reserve - << " ownerCount=" << ownerCount << " ownerCountAdj=" << ownerCountAdj; + << " ownerCount=" << currentOwnerCount << " ownerCountAdj=" << ownerCountAdj; return amount.xrp(); } @@ -125,19 +310,193 @@ transferRate(ReadView const& view, AccountID const& issuer) } void -adjustOwnerCount(ApplyView& view, SLE::ref sle, std::int32_t amount, beast::Journal j) +increaseOwnerCount( + ApplyView& view, + SLE::ref accountSle, + SLE::ref sponsorSle, + std::uint32_t count, + beast::Journal j) { - if (!sle) - return; - XRPL_ASSERT(amount, "xrpl::adjustOwnerCount : nonzero amount input"); - std::uint32_t const current{sle->getFieldU32(sfOwnerCount)}; - AccountID const id = (*sle)[sfAccount]; - std::uint32_t const adjusted = confineOwnerCount(current, amount, id, j); - view.adjustOwnerCountHook(id, current, adjusted); - sle->at(sfOwnerCount) = adjusted; - view.update(sle); + XRPL_ASSERT( + count != 0 && count <= std::numeric_limits::max(), + "xrpl::increaseOwnerCount : count in signed delta range"); + if (count == 0 || count > std::numeric_limits::max()) + return; // LCOV_EXCL_LINE + + adjustOwnerCountSigned(view, accountSle, sponsorSle, static_cast(count), j); } +void +increaseOwnerCount(ApplyViewContext ctx, SLE::ref accountSle, std::uint32_t count, beast::Journal j) +{ + auto const sponsorExp = getEffectiveTxReserveSponsor(ctx, accountSle); + + // The sponsor's existence is validated by checkReserve/checkSponsor before + // any owner-count mutation, so loading it here cannot fail. + XRPL_ASSERT( + sponsorExp.has_value(), "xrpl::increaseOwnerCount : sponsor validated before mutation"); + + increaseOwnerCount(ctx.view, accountSle, sponsorExp ? *sponsorExp : SLE::pointer(), count, j); +} + +void +decreaseOwnerCount( + ApplyView& view, + SLE::ref accountSle, + SLE::ref sponsorSle, + std::uint32_t count, + beast::Journal j) +{ + XRPL_ASSERT( + count != 0 && count <= std::numeric_limits::max(), + "xrpl::decreaseOwnerCount : count in signed delta range"); + if (count == 0 || count > std::numeric_limits::max()) + return; // LCOV_EXCL_LINE + + adjustOwnerCountSigned(view, accountSle, sponsorSle, -static_cast(count), j); +} + +void +decreaseOwnerCountForObject( + ApplyView& view, + SLE::ref accountSle, + SLE::ref objectSle, + std::uint32_t count, + beast::Journal j) +{ + XRPL_ASSERT(objectSle, "xrpl::decreaseOwnerCountForObject : valid object sle"); + if (!objectSle) + return; // LCOV_EXCL_LINE + + bool const validObjectType = objectSle->getType() != ltACCOUNT_ROOT; + XRPL_ASSERT(validObjectType, "xrpl::decreaseOwnerCountForObject : valid object sle type"); + if (!validObjectType) + return; // LCOV_EXCL_LINE + + SLE::ref sponsorSle = getLedgerEntryReserveSponsor(view, objectSle); + decreaseOwnerCount(view, accountSle, sponsorSle, count, j); +} + +void +adjustLoanBrokerOwnerCount( + ApplyView& view, + SLE::ref brokerSle, + std::int32_t delta, + beast::Journal j) +{ + XRPL_ASSERT( + brokerSle && brokerSle->getType() == ltLOAN_BROKER, + "xrpl::adjustLoanBrokerOwnerCount : valid loan broker sle"); + if (!brokerSle || brokerSle->getType() != ltLOAN_BROKER) + return; // LCOV_EXCL_LINE + + XRPL_ASSERT(delta != 0, "xrpl::adjustLoanBrokerOwnerCount : nonzero delta input"); + if (delta == 0) + return; // LCOV_EXCL_LINE + + adjustOwnerCountImpl( + view, brokerSle, sfOwnerCount, brokerSle->getAccountID(sfAccount), delta, j); +} + +XRPAmount +accountReserve(ReadView const& view, SLE::const_ref sle, beast::Journal j, Adjustment adj) +{ + XRPL_ASSERT(sle && sle->getType() == ltACCOUNT_ROOT, "xrpl::accountReserve : valid sle"); + + if (!view.rules().enabled(featureSponsor)) + { + XRPL_ASSERT(adj.accountCountDelta == 0, "xrpl::accountReserve : no account count delta"); + return view.fees().accountReserve(sle->getFieldU32(sfOwnerCount) + adj.ownerCountDelta, 1); + } + std::uint32_t const currentOwnerCount = ownerCount(sle, j, adj.ownerCountDelta); + std::uint32_t const currentAccountCount = accountCountImpl(sle, adj.accountCountDelta, j); + + return view.fees().accountReserve(currentOwnerCount, currentAccountCount); +} + +TER +checkReserve( + ApplyViewContext ctx, + SLE::const_ref accSle, + XRPAmount accBalance, + SLE::const_ref sponsorSle, + Adjustment adj, + beast::Journal j, + TER insufReserveCode) +{ + // TODO: swap to assert after fixCleanup3_2_0 is retired + if (!accSle || accSle->getType() != ltACCOUNT_ROOT) + return tefINTERNAL; // LCOV_EXCL_LINE + XRPL_ASSERT( + !isTesSuccess(insufReserveCode), "xrpl::checkReserve : insufReserveCode is not tesSUCCESS"); + if (ctx.view.rules().enabled(featureSponsor)) + { + if (sponsorSle) + { + if (sponsorSle->getType() != ltACCOUNT_ROOT) + return tefINTERNAL; // LCOV_EXCL_LINE + + auto const sle = ctx.view.read( + keylet::sponsorship( + sponsorSle->getAccountID(sfAccount), accSle->getAccountID(sfAccount))); + + // A reserve-sponsored tx must carry a sponsor signature + // (cosigning path) and/or have a pre-existing sponsorship SLE + // (prefunded path). Absence of both is an internal invariant break. + if (isReserveSponsored(ctx.tx) && !sle && !ctx.tx.isFieldPresent(sfSponsorSignature)) + return tecINTERNAL; // LCOV_EXCL_LINE + + if (sle) + { + auto const ownerCountAllowed = sle->getFieldU32(sfRemainingOwnerCount); + if (adj.ownerCountDelta > 0 && + ownerCountAllowed < static_cast(adj.ownerCountDelta)) + return insufReserveCode; + } + + auto const sponsorBalance = sponsorSle->getFieldAmount(sfBalance).xrp(); + XRPAmount const sponsorReserve = accountReserve(ctx.view, sponsorSle, j, adj); + + if (sponsorBalance < sponsorReserve) + return insufReserveCode; + } + else + { + XRPAmount const reserve = accountReserve(ctx.view, accSle, j, adj); + if (accBalance < reserve) + return insufReserveCode; + } + } + else + { + XRPL_ASSERT( + !sponsorSle, + "xrpl::checkReserve : featureSponsor disabled and sponsorSle not provided"); + XRPL_ASSERT(adj.accountCountDelta == 0, "xrpl::checkReserve : accountCountDelta is 0"); + auto const reserve = ctx.view.fees().accountReserve( + accSle->getFieldU32(sfOwnerCount) + adj.ownerCountDelta, 1); + if (accBalance < reserve) + return insufReserveCode; + } + return tesSUCCESS; +} + +TER +checkReserve( + ApplyViewContext ctx, + SLE::const_ref accSle, + XRPAmount accBalance, + Adjustment adj, + beast::Journal j) +{ + auto const sponsorExp = getEffectiveTxReserveSponsor(ctx, accSle); + if (!sponsorExp) + return sponsorExp.error(); // LCOV_EXCL_LINE + return checkReserve(ctx, accSle, accBalance, *sponsorExp, adj, j); +} + +// ---------------------------------------------------- + AccountID pseudoAccountAddress(ReadView const& view, uint256 const& pseudoOwnerKey) { diff --git a/src/libxrpl/ledger/helpers/CredentialHelpers.cpp b/src/libxrpl/ledger/helpers/CredentialHelpers.cpp index 6fc2faf03e..226ea100e9 100644 --- a/src/libxrpl/ledger/helpers/CredentialHelpers.cpp +++ b/src/libxrpl/ledger/helpers/CredentialHelpers.cpp @@ -97,7 +97,7 @@ deleteSLE(ApplyView& view, SLE::ref sleCredential, beast::Journal j) } if (isOwner) - adjustOwnerCount(view, sleAccount, -1, j); + decreaseOwnerCountForObject(view, sleAccount, sleCredential, 1, j); return tesSUCCESS; }; diff --git a/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp b/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp index 3c4e55a16e..ac14c3b66d 100644 --- a/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp +++ b/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -11,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -125,29 +125,29 @@ canAddHolding(ReadView const& view, MPTIssue const& mptIssue) [[nodiscard]] TER addEmptyHolding( - ApplyView& view, + ApplyViewContext ctx, AccountID const& accountID, XRPAmount priorBalance, MPTIssue const& mptIssue, beast::Journal journal) { auto const& mptID = mptIssue.getMptID(); - auto const mpt = view.peek(keylet::mptokenIssuance(mptID)); + auto const mpt = ctx.view.peek(keylet::mptokenIssuance(mptID)); if (!mpt) return tefINTERNAL; // LCOV_EXCL_LINE if (mpt->isFlag(lsfMPTLocked)) return tefINTERNAL; // LCOV_EXCL_LINE - if (view.peek(keylet::mptoken(mptID, accountID))) + if (ctx.view.peek(keylet::mptoken(mptID, accountID))) return tecDUPLICATE; if (accountID == mptIssue.getIssuer()) return tesSUCCESS; - return authorizeMPToken(view, priorBalance, mptID, accountID, journal); + return authorizeMPToken(ctx, priorBalance, mptID, accountID, journal); } [[nodiscard]] TER authorizeMPToken( - ApplyView& view, + ApplyViewContext ctx, XRPAmount const& priorBalance, MPTID const& mptIssuanceID, AccountID const& account, @@ -155,7 +155,7 @@ authorizeMPToken( std::uint32_t flags, std::optional holderID) { - auto const sleAcct = view.peek(keylet::account(account)); + auto const sleAcct = ctx.view.peek(keylet::account(account)); if (!sleAcct) return tecINTERNAL; // LCOV_EXCL_LINE @@ -170,19 +170,19 @@ authorizeMPToken( if ((flags & tfMPTUnauthorize) != 0u) { auto const mptokenKey = keylet::mptoken(mptIssuanceID, account); - auto const sleMpt = view.peek(mptokenKey); + auto const sleMpt = ctx.view.peek(mptokenKey); if (!sleMpt || (*sleMpt)[sfMPTAmount] != 0 || - (view.rules().enabled(fixCleanup3_1_3) && + (ctx.view.rules().enabled(fixCleanup3_1_3) && (*sleMpt)[~sfLockedAmount].valueOr(0) != 0)) return tecINTERNAL; // LCOV_EXCL_LINE - if (!view.dirRemove( + if (!ctx.view.dirRemove( keylet::ownerDir(account), (*sleMpt)[sfOwnerNode], sleMpt->key(), false)) return tecINTERNAL; // LCOV_EXCL_LINE - adjustOwnerCount(view, sleAcct, -1, journal); + decreaseOwnerCountForObject(ctx.view, sleAcct, sleMpt, 1, journal); - view.erase(sleMpt); + ctx.view.erase(sleMpt); return tesSUCCESS; } @@ -190,47 +190,57 @@ authorizeMPToken( // - add the new mptokenKey to the owner directory // - create the MPToken object for the holder + // A reserve sponsor only covers tx.Account's own objects. + auto const sponsorExp = getEffectiveTxReserveSponsor(ctx, sleAcct); + if (!sponsorExp) + return sponsorExp.error(); // LCOV_EXCL_LINE + auto const sponsorSle = *sponsorExp; + // The reserve that is required to create the MPToken. Note // that although the reserve increases with every item // an account owns, in the case of MPTokens we only // *enforce* a reserve if the user owns more than two // items. This is similar to the reserve requirements of trust lines. - std::uint32_t const uOwnerCount = sleAcct->getFieldU32(sfOwnerCount); - XRPAmount const reserveCreate( - (uOwnerCount < 2) ? XRPAmount(beast::kZero) - : view.fees().accountReserve(uOwnerCount + 1)); - - if (priorBalance < reserveCreate) - return tecINSUFFICIENT_RESERVE; + // The "free-tier" shortcut (ownerCount < 2) does not apply once a sponsor is on + // the tx — the sponsor must always cover the reserve (via balance or prefunded + // budget), so this check always runs for sponsored transactions. + if (sponsorSle || ownerCount(sleAcct, journal) >= 2) + { + if (auto const ret = checkReserve( + ctx, sleAcct, priorBalance, sponsorSle, {.ownerCountDelta = 1}, journal); + !isTesSuccess(ret)) + return ret; + } // Defensive check before we attempt to create MPToken for the issuer - auto const mpt = view.read(keylet::mptokenIssuance(mptIssuanceID)); + auto const mpt = ctx.view.read(keylet::mptokenIssuance(mptIssuanceID)); if (!mpt || mpt->getAccountID(sfIssuer) == account) { // LCOV_EXCL_START UNREACHABLE("xrpl::authorizeMPToken : invalid issuance or issuers token"); - if (view.rules().enabled(featureLendingProtocol)) + if (ctx.view.rules().enabled(featureLendingProtocol)) return tecINTERNAL; // LCOV_EXCL_STOP } auto const mptokenKey = keylet::mptoken(mptIssuanceID, account); auto mptoken = std::make_shared(mptokenKey); - if (auto ter = dirLink(view, account, mptoken)) + if (auto ter = dirLink(ctx.view, account, mptoken)) return ter; // LCOV_EXCL_LINE (*mptoken)[sfAccount] = account; (*mptoken)[sfMPTokenIssuanceID] = mptIssuanceID; (*mptoken)[sfFlags] = 0; - view.insert(mptoken); + ctx.view.insert(mptoken); // Update owner count. - adjustOwnerCount(view, sleAcct, 1, journal); + increaseOwnerCount(ctx.view, sleAcct, sponsorSle, 1, journal); + addSponsorToLedgerEntry(mptoken, sponsorSle); return tesSUCCESS; } - auto const sleMptIssuance = view.read(keylet::mptokenIssuance(mptIssuanceID)); + auto const sleMptIssuance = ctx.view.read(keylet::mptokenIssuance(mptIssuanceID)); if (!sleMptIssuance) return tecINTERNAL; // LCOV_EXCL_LINE @@ -240,7 +250,7 @@ authorizeMPToken( if (account != (*sleMptIssuance)[sfIssuer]) return tecINTERNAL; // LCOV_EXCL_LINE - auto const sleMpt = view.peek(keylet::mptoken(mptIssuanceID, *holderID)); + auto const sleMpt = ctx.view.peek(keylet::mptoken(mptIssuanceID, *holderID)); if (!sleMpt) return tecINTERNAL; // LCOV_EXCL_LINE @@ -263,13 +273,13 @@ authorizeMPToken( if (flagsIn != flagsOut) sleMpt->setFieldU32(sfFlags, flagsOut); - view.update(sleMpt); + ctx.view.update(sleMpt); return tesSUCCESS; } [[nodiscard]] TER removeEmptyHolding( - ApplyView& view, + ApplyViewContext ctx, AccountID const& accountID, MPTIssue const& mptIssue, beast::Journal journal) @@ -279,7 +289,7 @@ removeEmptyHolding( // a token does exist, it will get deleted. If not, return success. bool const accountIsIssuer = accountID == mptIssue.getIssuer(); auto const& mptID = mptIssue.getMptID(); - auto const mptoken = view.peek(keylet::mptoken(mptID, accountID)); + auto const mptoken = ctx.view.peek(keylet::mptoken(mptID, accountID)); if (!mptoken) return accountIsIssuer ? (TER)tesSUCCESS : (TER)tecOBJECT_NOT_FOUND; // Unlike a trust line, if the account is the issuer, and the token has a @@ -287,7 +297,7 @@ removeEmptyHolding( // accounting out of balance, so fail. Since this should be impossible // anyway, I'm not going to put any effort into it. if (mptoken->at(sfMPTAmount) != 0 || - (view.rules().enabled(fixCleanup3_1_3) && (*mptoken)[~sfLockedAmount].valueOr(0) != 0)) + (ctx.view.rules().enabled(fixCleanup3_1_3) && (*mptoken)[~sfLockedAmount].valueOr(0) != 0)) return tecHAS_OBLIGATIONS; // Don't delete if the token still has confidential balances @@ -300,7 +310,7 @@ removeEmptyHolding( } return authorizeMPToken( - view, + ctx, {}, // priorBalance mptID, accountID, @@ -419,13 +429,13 @@ requireAuth( [[nodiscard]] TER enforceMPTokenAuthorization( - ApplyView& view, + ApplyViewContext ctx, MPTID const& mptIssuanceID, AccountID const& account, XRPAmount const& priorBalance, // for MPToken authorization beast::Journal j) { - auto const sleIssuance = view.read(keylet::mptokenIssuance(mptIssuanceID)); + auto const sleIssuance = ctx.view.read(keylet::mptokenIssuance(mptIssuanceID)); if (!sleIssuance) return tefINTERNAL; // LCOV_EXCL_LINE @@ -437,7 +447,7 @@ enforceMPTokenAuthorization( return tefINTERNAL; // LCOV_EXCL_LINE auto const keylet = keylet::mptoken(mptIssuanceID, account); - auto const sleToken = view.read(keylet); // NOTE: might be null + auto const sleToken = ctx.view.read(keylet); // NOTE: might be null auto const maybeDomainID = sleIssuance->at(~sfDomainID); bool expired = false; bool const authorizedByDomain = [&]() -> bool { @@ -445,7 +455,7 @@ enforceMPTokenAuthorization( if (!maybeDomainID.has_value()) return false; // LCOV_EXCL_LINE - auto const ter = verifyValidDomain(view, account, *maybeDomainID, j); + auto const ter = verifyValidDomain(ctx.view, account, *maybeDomainID, j); if (isTesSuccess(ter)) return true; if (ter == tecEXPIRED) @@ -500,7 +510,7 @@ enforceMPTokenAuthorization( maybeDomainID.has_value() && sleToken == nullptr, "xrpl::enforceMPTokenAuthorization : new MPToken for domain"); if (auto const err = authorizeMPToken( - view, + ctx, priorBalance, // priorBalance mptIssuanceID, // mptIssuanceID account, // account @@ -915,6 +925,7 @@ createMPToken( ApplyView& view, MPTID const& mptIssuanceID, AccountID const& account, + SLE::ref sponsorSle, std::uint32_t const flags) { auto const mptokenKey = keylet::mptoken(mptIssuanceID, account); @@ -931,6 +942,8 @@ createMPToken( (*mptoken)[sfFlags] = flags; (*mptoken)[sfOwnerNode] = *ownerNode; + addSponsorToLedgerEntry(mptoken, sponsorSle); + view.insert(mptoken); return tesSUCCESS; @@ -941,6 +954,7 @@ checkCreateMPT( xrpl::ApplyView& view, xrpl::MPTIssue const& mptIssue, xrpl::AccountID const& holder, + SLE::ref sponsorSle, beast::Journal j) { if (mptIssue.getIssuer() == holder) @@ -950,7 +964,7 @@ checkCreateMPT( auto const mptokenID = keylet::mptoken(mptIssuanceID.key, holder); if (!view.exists(mptokenID)) { - if (auto const err = createMPToken(view, mptIssue.getMptID(), holder, 0); + if (auto const err = createMPToken(view, mptIssue.getMptID(), holder, sponsorSle, 0); !isTesSuccess(err)) { return err; @@ -960,7 +974,8 @@ checkCreateMPT( { return tecINTERNAL; } - adjustOwnerCount(view, sleAcct, 1, j); + + increaseOwnerCount(view, sleAcct, sponsorSle, 1, j); } return tesSUCCESS; } diff --git a/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp b/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp index 6dca715a8c..c67ab6775d 100644 --- a/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp +++ b/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp @@ -269,11 +269,7 @@ insertToken(ApplyView& view, AccountID owner, STObject&& nft) // the NFT. SLE::pointer const page = getPageForToken(view, owner, nft[sfNFTokenID], [](ApplyView& view, AccountID const& owner) { - adjustOwnerCount( - view, - view.peek(keylet::account(owner)), - 1, - beast::Journal{beast::Journal::getNullSink()}); + increaseOwnerCount(view, owner, {}, 1, beast::Journal{beast::Journal::getNullSink()}); }); if (!page) @@ -411,21 +407,17 @@ removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID, S curr->setFieldArray(sfNFTokens, arr); view.update(curr); - int cnt = 0; + std::uint32_t cnt = 0; if (prev && mergePages(view, prev, curr)) - cnt--; + ++cnt; if (next && mergePages(view, curr, next)) - cnt--; + ++cnt; if (cnt != 0) { - adjustOwnerCount( - view, - view.peek(keylet::account(owner)), - cnt, - beast::Journal{beast::Journal::getNullSink()}); + decreaseOwnerCount(view, owner, {}, cnt, beast::Journal{beast::Journal::getNullSink()}); } return tesSUCCESS; @@ -460,11 +452,7 @@ removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID, S curr->makeFieldAbsent(sfPreviousPageMin); } - adjustOwnerCount( - view, - view.peek(keylet::account(owner)), - -1, - beast::Journal{beast::Journal::getNullSink()}); + decreaseOwnerCount(view, owner, {}, 1, beast::Journal{beast::Journal::getNullSink()}); view.update(curr); view.erase(prev); @@ -502,7 +490,7 @@ removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID, S view.erase(curr); - int cnt = 1; + uint32_t cnt = 1; // Since we're here, try to consolidate the previous and current pages // of the page we removed (if any) into one. mergePages() _should_ @@ -519,11 +507,7 @@ removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID, S view.peek(Keylet(ltNFTOKEN_PAGE, next->key())))) cnt++; - adjustOwnerCount( - view, - view.peek(keylet::account(owner)), - -1 * cnt, - beast::Journal{beast::Journal::getNullSink()}); + decreaseOwnerCount(view, owner, {}, cnt, beast::Journal{beast::Journal::getNullSink()}); return tesSUCCESS; } @@ -639,8 +623,7 @@ deleteTokenOffer(ApplyView& view, SLE::ref offer) false)) return false; - adjustOwnerCount( - view, view.peek(keylet::account(owner)), -1, beast::Journal{beast::Journal::getNullSink()}); + decreaseOwnerCount(view, owner, {}, 1, beast::Journal{beast::Journal::getNullSink()}); view.erase(offer); return true; @@ -743,9 +726,11 @@ repairNFTokenDirectoryLinks(ApplyView& view, AccountID const& owner) auto const newPrev = view.peek(Keylet(ltNFTOKEN_PAGE, *prevLink)); if (!newPrev) { + // LCOV_EXCL_START Throw( "NFTokenPage directory for " + to_string(owner) + - " cannot be repaired. Unexpected link problem."); + " cannot be repaired. Unexpected link problem."); + // LCOV_EXCL_STOP } newPrev->at(sfNextPageMin) = nextPage->key(); view.update(newPrev); @@ -931,7 +916,7 @@ tokenOfferCreateApply( { Keylet const acctKeylet = keylet::account(acctID); if (auto const acct = view.read(acctKeylet); - priorBalance < view.fees().accountReserve((*acct)[sfOwnerCount] + 1)) + priorBalance < accountReserve(view, acct, j, {.ownerCountDelta = 1})) return tecINSUFFICIENT_RESERVE; auto const offerID = keylet::nftokenOffer(acctID, seqProxy.value()); @@ -983,7 +968,7 @@ tokenOfferCreateApply( } // Update owner count. - adjustOwnerCount(view, view.peek(acctKeylet), 1, j); + increaseOwnerCount(view, acctID, {}, 1, j); return tesSUCCESS; } diff --git a/src/libxrpl/ledger/helpers/OfferHelpers.cpp b/src/libxrpl/ledger/helpers/OfferHelpers.cpp index 6e72b71564..caa6ffb8ae 100644 --- a/src/libxrpl/ledger/helpers/OfferHelpers.cpp +++ b/src/libxrpl/ledger/helpers/OfferHelpers.cpp @@ -55,7 +55,7 @@ offerDelete(ApplyView& view, SLE::ref sle, beast::Journal j) } } - adjustOwnerCount(view, view.peek(keylet::account(owner)), -1, j); + decreaseOwnerCountForObject(view, owner, sle, 1, j); view.erase(sle); diff --git a/src/libxrpl/ledger/helpers/PaymentChannelHelpers.cpp b/src/libxrpl/ledger/helpers/PaymentChannelHelpers.cpp index 03b68e8860..d5a0b25681 100644 --- a/src/libxrpl/ledger/helpers/PaymentChannelHelpers.cpp +++ b/src/libxrpl/ledger/helpers/PaymentChannelHelpers.cpp @@ -58,7 +58,7 @@ closeChannel(SLE::ref slep, ApplyView& view, uint256 const& key, beast::Journal XRPL_ASSERT( (*slep)[sfAmount] >= (*slep)[sfBalance], "xrpl::closeChannel : minimum channel amount"); (*sle)[sfBalance] = (*sle)[sfBalance] + (*slep)[sfAmount] - (*slep)[sfBalance]; - adjustOwnerCount(view, sle, -1, j); + decreaseOwnerCountForObject(view, sle, slep, 1, j); view.update(sle); // Remove PayChan from ledger diff --git a/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp b/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp index 5a2995c030..868c9fb26d 100644 --- a/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp +++ b/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -195,6 +196,7 @@ trustCreate( // Issuer should be the account being set. std::uint32_t uQualityIn, std::uint32_t uQualityOut, + SLE::ref sponsorSle, beast::Journal j) { JLOG(j.trace()) << "trustCreate: " << to_string(uSrcAccountID) << ", " @@ -281,7 +283,9 @@ trustCreate( } sleRippleState->setFieldU32(sfFlags, uFlags); - adjustOwnerCount(view, sleAccount, 1, j); + increaseOwnerCount(view, sleAccount, sponsorSle, 1, j); + + addSponsorToLedgerEntry(sleRippleState, sponsorSle, bSetHigh ? sfHighSponsor : sfLowSponsor); // ONLY: Create ripple balance. sleRippleState->setFieldAmount(sfBalance, bSetHigh ? -saBalance : saBalance); @@ -317,6 +321,9 @@ trustDelete( return tefBAD_LEDGER; // LCOV_EXCL_LINE } + removeSponsorFromLedgerEntry(sleRippleState, sfHighSponsor); + removeSponsorFromLedgerEntry(sleRippleState, sfLowSponsor); + JLOG(j.trace()) << "trustDelete: Deleting ripple line: state"; view.erase(sleRippleState); @@ -369,11 +376,15 @@ updateTrustLine( { // VFALCO Where is the line being deleted? // Clear the reserve of the sender, possibly delete the line! - adjustOwnerCount(view, sle, -1, j); + auto const currentSponsor = + getLedgerEntryReserveSponsor(view, state, bSenderHigh ? sfHighSponsor : sfLowSponsor); + decreaseOwnerCount(view, sle, currentSponsor, 1, j); // Clear reserve flag. state->clearFlag(senderReserveFlag); + removeSponsorFromLedgerEntry(state, !bSenderHigh ? sfLowSponsor : sfHighSponsor); + // Balance is zero, receiver reserve is clear. if (!after && !state->isFlag(receiverReserveFlag)) return true; @@ -381,12 +392,14 @@ updateTrustLine( return false; } +// Only used in tests TER issueIOU( ApplyView& view, AccountID const& account, STAmount const& amount, Issue const& issue, + SLE::ref sponsorSle, beast::Journal j) { XRPL_ASSERT( @@ -472,6 +485,7 @@ issueIOU( limit, 0, 0, + sponsorSle, j); } @@ -620,7 +634,7 @@ canTransfer(ReadView const& view, Issue const& issue, AccountID const& from, Acc TER addEmptyHolding( - ApplyView& view, + ApplyViewContext ctx, AccountID const& accountID, XRPAmount priorBalance, Issue const& issue, @@ -632,30 +646,45 @@ addEmptyHolding( auto const& issuerId = issue.getIssuer(); auto const& currency = issue.currency; - if (isGlobalFrozen(view, issuerId)) + if (isGlobalFrozen(ctx.view, issuerId)) return tecFROZEN; // LCOV_EXCL_LINE auto const& srcId = issuerId; auto const& dstId = accountID; auto const high = srcId > dstId; auto const index = keylet::trustLine(srcId, dstId, currency); - auto const sleSrc = view.peek(keylet::account(srcId)); - auto const sleDst = view.peek(keylet::account(dstId)); + auto const sleSrc = ctx.view.peek(keylet::account(srcId)); + auto const sleDst = ctx.view.peek(keylet::account(dstId)); if (!sleDst || !sleSrc) return tefINTERNAL; // LCOV_EXCL_LINE if (!sleSrc->isFlag(lsfDefaultRipple)) return tecINTERNAL; // LCOV_EXCL_LINE // If the line already exists, don't create it again. - if (view.read(index)) + if (ctx.view.read(index)) return tecDUPLICATE; + // A reserve sponsor only covers tx.Account's own objects. + auto const sponsorExp = getEffectiveTxReserveSponsor(ctx, sleDst); + if (!sponsorExp) + return sponsorExp.error(); // LCOV_EXCL_LINE + auto const sponsorSle = *sponsorExp; + // Can the account cover the trust line reserve ? - std::uint32_t const ownerCount = sleDst->at(sfOwnerCount); - if (priorBalance < view.fees().accountReserve(ownerCount + 1)) - return tecNO_LINE_INSUF_RESERVE; + if (auto const ret = checkReserve( + ctx, + sleDst, + priorBalance, + sponsorSle, + {.ownerCountDelta = 1}, + journal, + tecNO_LINE_INSUF_RESERVE); + !isTesSuccess(ret)) + { + return ret; + } return trustCreate( - view, + ctx.view, high, srcId, dstId, @@ -669,19 +698,20 @@ addEmptyHolding( /*saLimit=*/STAmount{Issue{currency, dstId}}, /*uQualityIn=*/0, /*uQualityOut=*/0, + sponsorSle, journal); } TER removeEmptyHolding( - ApplyView& view, + ApplyViewContext ctx, AccountID const& accountID, Issue const& issue, beast::Journal journal) { if (issue.native()) { - auto const sle = view.read(keylet::account(accountID)); + auto const sle = ctx.view.read(keylet::account(accountID)); if (!sle) return tecINTERNAL; // LCOV_EXCL_LINE @@ -696,7 +726,7 @@ removeEmptyHolding( // If the account is the issuer, then no line should exist. Check anyway. // If a line does exist, it will get deleted. If not, return success. bool const accountIsIssuer = accountID == issue.account; - auto const line = view.peek(keylet::trustLine(accountID, issue)); + auto const line = ctx.view.peek(keylet::trustLine(accountID, issue)); if (!line) return accountIsIssuer ? (TER)tesSUCCESS : (TER)tecOBJECT_NOT_FOUND; if (!accountIsIssuer && line->at(sfBalance)->iou() != beast::kZero) @@ -706,33 +736,43 @@ removeEmptyHolding( if (line->isFlag(lsfLowReserve)) { // Clear reserve for low account. - auto sleLowAccount = view.peek(keylet::account(line->at(sfLowLimit)->getIssuer())); + auto sleLowAccount = ctx.view.peek(keylet::account(line->at(sfLowLimit)->getIssuer())); if (!sleLowAccount) return tecINTERNAL; // LCOV_EXCL_LINE - adjustOwnerCount(view, sleLowAccount, -1, journal); + auto const currentLowSponsor = getLedgerEntryReserveSponsor(ctx.view, line, sfLowSponsor); + + decreaseOwnerCount(ctx.view, sleLowAccount, currentLowSponsor, 1, journal); // It's not really necessary to clear the reserve flag, since the line // is about to be deleted, but this will make the metadata reflect an // accurate state at the time of deletion. line->clearFlag(lsfLowReserve); + removeSponsorFromLedgerEntry(line, sfLowSponsor); } if (line->isFlag(lsfHighReserve)) { // Clear reserve for high account. - auto sleHighAccount = view.peek(keylet::account(line->at(sfHighLimit)->getIssuer())); + auto sleHighAccount = ctx.view.peek(keylet::account(line->at(sfHighLimit)->getIssuer())); if (!sleHighAccount) return tecINTERNAL; // LCOV_EXCL_LINE - adjustOwnerCount(view, sleHighAccount, -1, journal); + auto const currentHighSponsor = getLedgerEntryReserveSponsor(ctx.view, line, sfHighSponsor); + + decreaseOwnerCount(ctx.view, sleHighAccount, currentHighSponsor, 1, journal); // It's not really necessary to clear the reserve flag, since the line // is about to be deleted, but this will make the metadata reflect an // accurate state at the time of deletion. line->clearFlag(lsfHighReserve); + removeSponsorFromLedgerEntry(line, sfHighSponsor); } return trustDelete( - view, line, line->at(sfLowLimit)->getIssuer(), line->at(sfHighLimit)->getIssuer(), journal); + ctx.view, + line, + line->at(sfLowLimit)->getIssuer(), + line->at(sfHighLimit)->getIssuer(), + journal); } TER @@ -768,6 +808,9 @@ deleteAMMTrustLine( if (ammAccountID && (low != *ammAccountID && high != *ammAccountID)) return terNO_AMM; + auto const sponsorSle = + getLedgerEntryReserveSponsor(view, sleState, !ammLow ? sfLowSponsor : sfHighSponsor); + if (auto const ter = trustDelete(view, sleState, low, high, j); !isTesSuccess(ter)) { JLOG(j.error()) << "deleteAMMTrustLine: failed to delete the trustline."; @@ -778,7 +821,7 @@ deleteAMMTrustLine( if (!sleState->isFlag(uFlags)) return tecINTERNAL; // LCOV_EXCL_LINE - adjustOwnerCount(view, !ammLow ? sleLow : sleHigh, -1, j); + decreaseOwnerCount(view, !ammLow ? sleLow : sleHigh, sponsorSle, 1, j); return tesSUCCESS; } diff --git a/src/libxrpl/ledger/helpers/SponsorHelpers.cpp b/src/libxrpl/ledger/helpers/SponsorHelpers.cpp new file mode 100644 index 0000000000..7e0c041854 --- /dev/null +++ b/src/libxrpl/ledger/helpers/SponsorHelpers.cpp @@ -0,0 +1,345 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace xrpl { + +bool +isReserveSponsorAllowed(TxType txType) +{ + // Transaction types explicitly allow-listed for reserve sponsorship, for + // v1. Lazily-initialized function-local static: constructed once on first + // use, with no startup cost paid by clients that never call this. + static std::unordered_set const kReserveSponsorAllowed = { + ttDELEGATE_SET, + ttDEPOSIT_PREAUTH, + ttPAYMENT, + ttSIGNER_LIST_SET, + ttCHECK_CANCEL, + ttCHECK_CASH, + ttCHECK_CREATE, + ttESCROW_CANCEL, + ttESCROW_CREATE, + ttESCROW_FINISH, + ttPAYCHAN_CLAIM, + ttPAYCHAN_CREATE, + ttPAYCHAN_FUND, + ttCLAWBACK, + ttMPTOKEN_AUTHORIZE, + ttMPTOKEN_ISSUANCE_CREATE, + ttMPTOKEN_ISSUANCE_DESTROY, + ttMPTOKEN_ISSUANCE_SET, + ttTRUST_SET, + ttCREDENTIAL_ACCEPT, + ttCREDENTIAL_CREATE, + ttCREDENTIAL_DELETE, + ttACCOUNT_SET, + ttREGULAR_KEY_SET, + ttSPONSORSHIP_TRANSFER, + }; + return kReserveSponsorAllowed.contains(txType); +} + +std::optional +getTxReserveSponsorID(STTx const& tx) +{ + if (tx.isFieldPresent(sfSponsor) && isReserveSponsored(tx)) + { + XRPL_ASSERT( + getCurrentTransactionRules()->enabled( // NOLINT(bugprone-unchecked-optional-access) + featureSponsor), + "xrpl::getTxReserveSponsorID : sponsor exists + Sponsor enabled"); + return tx.getAccountID(sfSponsor); + } + return {}; +} + +std::expected +getTxReserveSponsor(ApplyViewContext ctx) +{ + auto const sponsorID = getTxReserveSponsorID(ctx.tx); + if (sponsorID) + { + XRPL_ASSERT( + ctx.view.rules().enabled(featureSponsor), + "xrpl::getTxReserveSponsor : sponsor exists + Sponsor enabled"); + auto sle = ctx.view.peek(keylet::account(*sponsorID)); + + // already checked in Transactor::checkSponsor + if (!sle) + return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE + return sle; + } + return SLE::pointer(); +} + +std::expected +getTxReserveSponsor(ReadView const& view, STTx const& tx) +{ + auto const sponsorID = getTxReserveSponsorID(tx); + if (sponsorID) + { + XRPL_ASSERT( + view.rules().enabled(featureSponsor), + "xrpl::getTxReserveSponsor : sponsor exists + Sponsor enabled"); + auto sle = view.read(keylet::account(*sponsorID)); + + // already checked in Transactor::checkSponsor + if (!sle) + return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE + return sle; + } + return SLE::pointer(); +} + +std::expected +getEffectiveTxReserveSponsor(ApplyViewContext ctx, SLE::const_ref accountSle) +{ + // A reserve sponsor only covers tx.Account's own objects. + if (ctx.view.rules().enabled(fixCleanup3_2_0)) + { + XRPL_ASSERT( + accountSle && accountSle->getType() == ltACCOUNT_ROOT, + "xrpl::getEffectiveTxReserveSponsor : accountSle exists and is account type"); + } + else + { + XRPL_ASSERT( + accountSle && + ((accountSle->getType() == ltACCOUNT_ROOT) || (accountSle->getType() == ltESCROW)), + "xrpl::getEffectiveTxReserveSponsor : accountSle exists and is account type"); + } + + if (isPseudoAccount(accountSle) || accountSle->getAccountID(sfAccount) != ctx.tx[sfAccount]) + return SLE::pointer(); + return getTxReserveSponsor(ctx); +} + +std::optional +getLedgerEntryReserveSponsorID(SLE::const_ref sle, SF_ACCOUNT const& field) +{ + XRPL_ASSERT( + (sle && + ((sle->getType() == ltRIPPLE_STATE && (field == sfHighSponsor || field == sfLowSponsor)) || + (sle->getType() != ltRIPPLE_STATE && field == sfSponsor))), + "xrpl::getLedgerEntryReserveSponsorID : correct sfield"); + + if (sle->isFieldPresent(field)) + return sle->getAccountID(field); + return {}; +} + +SLE::pointer +getLedgerEntryReserveSponsor(ApplyView& view, SLE::const_ref sle, SF_ACCOUNT const& field) +{ + auto const sponsorID = getLedgerEntryReserveSponsorID(sle, field); + if (sponsorID) + { + XRPL_ASSERT( + view.rules().enabled(featureSponsor), + "xrpl::getLedgerEntryReserveSponsor : sponsor exists + Sponsor enabled"); + return view.peek(keylet::account(*sponsorID)); + } + return {}; +} + +void +addSponsorToLedgerEntry(SLE::ref sle, SLE::const_ref sponsorSle, SF_ACCOUNT const& field) +{ + XRPL_ASSERT( + (sle->getType() == ltRIPPLE_STATE && (field == sfHighSponsor || field == sfLowSponsor)) || + (sle->getType() != ltRIPPLE_STATE && field == sfSponsor), + "addSponsorToLedgerEntry : Invalid field to the LedgerEntry"); + if (sponsorSle) + { + XRPL_ASSERT( + getCurrentTransactionRules()->enabled( // NOLINT(bugprone-unchecked-optional-access) + featureSponsor), + "xrpl::addSponsorToLedgerEntry : sponsor exists + Sponsor enabled"); + sle->setAccountID(field, sponsorSle->getAccountID(sfAccount)); + } +} + +void +addSponsorToLedgerEntry(ApplyViewContext ctx, SLE::ref sle, SF_ACCOUNT const& field) +{ + // getTxReserveSponsor yields a null pointer when the tx is not + // reserve-sponsored, so addSponsorToLedgerEntry becomes a no-op then. The + // error case (tecINTERNAL) is an already-checked invariant; skip stamping. + auto const sponsorSle = getTxReserveSponsor(ctx); + if (sponsorSle && *sponsorSle) + { + XRPL_ASSERT( + ctx.view.rules().enabled(featureSponsor), + "xrpl::addSponsorToLedgerEntry : sponsor exists + Sponsor enabled"); + addSponsorToLedgerEntry(sle, *sponsorSle, field); + } +} + +void +removeSponsorFromLedgerEntry(SLE::ref sle, SF_ACCOUNT const& field) +{ + XRPL_ASSERT( + (sle->getType() == ltRIPPLE_STATE && (field == sfHighSponsor || field == sfLowSponsor)) || + (sle->getType() != ltRIPPLE_STATE && field == sfSponsor), + "removeSponsorFromLedgerEntry : Invalid field to the LedgerEntry"); + if (sle->isFieldPresent(field)) + { + XRPL_ASSERT( + getCurrentTransactionRules()->enabled( // NOLINT(bugprone-unchecked-optional-access) + featureSponsor), + "xrpl::removeSponsorFromLedgerEntry : sponsor exists + Sponsor enabled"); + sle->makeFieldAbsent(field); + } +} + +bool +isLedgerEntryOwner(ReadView const& view, SLE const& sle, AccountID const& account) +{ + switch (sle.getType()) + { + case ltCHECK: + case ltESCROW: + case ltPAYCHAN: + case ltMPTOKEN: + case ltDELEGATE: + case ltDEPOSIT_PREAUTH: + return sle.getAccountID(sfAccount) == account; + case ltMPTOKEN_ISSUANCE: + return sle.getAccountID(sfIssuer) == account; + case ltSIGNER_LIST: { + auto const signerList = view.read(keylet::signerList(account)); + if (!signerList) + return false; + return signerList->key() == sle.key(); + } + case ltCREDENTIAL: { + auto const& ownerField = sle.isFlag(lsfAccepted) ? sfSubject : sfIssuer; + return sle.getAccountID(ownerField) == account; + } + case ltRIPPLE_STATE: { + if (sle.isFlag(lsfHighReserve)) + { + auto const highAccount = sle.getFieldAmount(sfHighLimit).getIssuer(); + if (highAccount == account) + return true; + } + if (sle.isFlag(lsfLowReserve)) + { + auto const lowAccount = sle.getFieldAmount(sfLowLimit).getIssuer(); + if (lowAccount == account) + return true; + } + // Reachable: the sponsee may be a third party or the side of the + // line that holds no reserve (e.g. the issuer). Callers map this + // to tecNO_PERMISSION. + return false; + } + default: + // LCOV_EXCL_START + UNREACHABLE("xrpl::isLedgerEntryOwner : object is not supported by sponsorship."); + return false; + // LCOV_EXCL_STOP + }; +} + +bool +isLedgerEntrySupportedBySponsorship(SLE const& sle) +{ + switch (sle.getType()) + { + case ltCHECK: + case ltESCROW: + case ltPAYCHAN: + case ltMPTOKEN: + case ltDELEGATE: + case ltDEPOSIT_PREAUTH: + case ltMPTOKEN_ISSUANCE: + case ltSIGNER_LIST: + case ltCREDENTIAL: + case ltRIPPLE_STATE: + return true; + default: + return false; + }; +} + +std::uint32_t +getLedgerEntryOwnerCount(SLE const& sle) +{ + switch (sle.getType()) + { + case ltORACLE: { + return calculateOracleReserve(sle.getFieldArray(sfPriceDataSeries)); + } + // Vaults require 2 owner counts (the vault and a pseudo-account) + case ltVAULT: + return 2; + case ltSIGNER_LIST: { + // Mirror SignerListSet's owner-count accounting so that create and + // delete agree. Modern lists (post-MultiSignReserve) carry the + // lsfOneOwnerCount flag and cost a single owner count. Legacy + // pre-MultiSignReserve lists cost 2 + signer_count owner counts + if (sle.isFlag(lsfOneOwnerCount)) + return 1; + return 2 + static_cast(sle.getFieldArray(sfSignerEntries).size()); + } + case ltACCOUNT_ROOT: + // LCOV_EXCL_START + UNREACHABLE("AccountRoots are not supported by object sponsorship."); + return 0; + // LCOV_EXCL_STOP + default: + return 1; + } +} + +SF_ACCOUNT const& +getLedgerEntrySponsorField(SLE const& sle, AccountID const& owner) +{ + switch (sle.getType()) + { + case ltRIPPLE_STATE: { + if (sle.isFlag(lsfHighReserve)) + { + auto const highAccount = sle.getFieldAmount(sfHighLimit).getIssuer(); + if (highAccount == owner) + return sfHighSponsor; + } + if (sle.isFlag(lsfLowReserve)) + { + auto const lowAccount = sle.getFieldAmount(sfLowLimit).getIssuer(); + if (lowAccount == owner) + return sfLowSponsor; + } + // LCOV_EXCL_START + UNREACHABLE("xrpl::getLedgerEntrySponsorField : unknown owner for RippleState"); + return sfSponsor; + // LCOV_EXCL_STOP + } + default: + return sfSponsor; + } +} + +} // namespace xrpl diff --git a/src/libxrpl/ledger/helpers/TokenHelpers.cpp b/src/libxrpl/ledger/helpers/TokenHelpers.cpp index 0ab97a4b60..06e366847d 100644 --- a/src/libxrpl/ledger/helpers/TokenHelpers.cpp +++ b/src/libxrpl/ledger/helpers/TokenHelpers.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -571,7 +572,7 @@ canAddHolding(ReadView const& view, Asset const& asset) TER addEmptyHolding( - ApplyView& view, + ApplyViewContext ctx, AccountID const& accountID, XRPAmount priorBalance, Asset const& asset, @@ -579,21 +580,21 @@ addEmptyHolding( { return std::visit( [&](TIss const& issue) -> TER { - return addEmptyHolding(view, accountID, priorBalance, issue, journal); + return addEmptyHolding(ctx, accountID, priorBalance, issue, journal); }, asset.value()); } TER removeEmptyHolding( - ApplyView& view, + ApplyViewContext ctx, AccountID const& accountID, Asset const& asset, beast::Journal journal) { return std::visit( [&](TIss const& issue) -> TER { - return removeEmptyHolding(view, accountID, issue, journal); + return removeEmptyHolding(ctx, accountID, issue, journal); }, asset.value()); } @@ -647,6 +648,7 @@ directSendNoFeeIOU( AccountID const& uReceiverID, STAmount const& saAmount, bool bCheckIssuer, + SLE::ref sponsorSle, beast::Journal j) { AccountID const& issuer = saAmount.getIssuer(); @@ -717,7 +719,12 @@ directSendNoFeeIOU( // Sender quality out is 0. { // Clear the reserve of the sender, possibly delete the line! - adjustOwnerCount(view, view.peek(keylet::account(uSenderID)), -1, j); + auto const currentSponsor = getLedgerEntryReserveSponsor( + view, sleRippleState, !bSenderHigh ? sfLowSponsor : sfHighSponsor); + decreaseOwnerCount(view, view.peek(keylet::account(uSenderID)), currentSponsor, 1, j); + + removeSponsorFromLedgerEntry( + sleRippleState, !bSenderHigh ? sfLowSponsor : sfHighSponsor); // Clear reserve flag. sleRippleState->clearFlag(senderReserveFlag); @@ -780,6 +787,7 @@ directSendNoFeeIOU( saReceiverLimit, 0, 0, + sponsorSle, j); } @@ -794,6 +802,7 @@ directSendNoLimitIOU( STAmount const& saAmount, STAmount& saActual, beast::Journal j, + SLE::ref sponsorSle, WaiveTransferFee waiveFee) { auto const& issuer = saAmount.getIssuer(); @@ -806,7 +815,8 @@ directSendNoLimitIOU( if (uSenderID == issuer || uReceiverID == issuer || issuer == noAccount()) { // Direct send: redeeming IOUs and/or sending own IOUs. - auto const ter = directSendNoFeeIOU(view, uSenderID, uReceiverID, saAmount, false, j); + auto const ter = + directSendNoFeeIOU(view, uSenderID, uReceiverID, saAmount, false, sponsorSle, j); if (!isTesSuccess(ter)) return ter; saActual = saAmount; @@ -824,10 +834,12 @@ directSendNoLimitIOU( << to_string(uReceiverID) << " : deliver=" << saAmount.getFullText() << " cost=" << saActual.getFullText(); - TER terResult = directSendNoFeeIOU(view, issuer, uReceiverID, saAmount, true, j); + TER terResult = directSendNoFeeIOU(view, issuer, uReceiverID, saAmount, true, sponsorSle, j); if (tesSUCCESS == terResult) - terResult = directSendNoFeeIOU(view, uSenderID, issuer, saActual, true, j); + { + terResult = directSendNoFeeIOU(view, uSenderID, issuer, saActual, true, sponsorSle, j); + } return terResult; } @@ -870,7 +882,8 @@ directSendNoLimitMultiIOU( if (senderID == issuer || receiverID == issuer || issuer == noAccount()) { // Direct send: redeeming IOUs and/or sending own IOUs. - if (auto const ter = directSendNoFeeIOU(view, senderID, receiverID, amount, false, j); + if (auto const ter = + directSendNoFeeIOU(view, senderID, receiverID, amount, false, {}, j); !isTesSuccess(ter)) return ter; actual += amount; @@ -894,14 +907,14 @@ directSendNoLimitMultiIOU( << to_string(receiverID) << " : deliver=" << amount.getFullText() << " cost=" << actual.getFullText(); - if (TER const terResult = directSendNoFeeIOU(view, issuer, receiverID, amount, true, j)) + if (TER const terResult = directSendNoFeeIOU(view, issuer, receiverID, amount, true, {}, j)) return terResult; } if (senderID != issuer && takeFromSender) { if (TER const terResult = - directSendNoFeeIOU(view, senderID, issuer, takeFromSender, true, j)) + directSendNoFeeIOU(view, senderID, issuer, takeFromSender, true, {}, j)) return terResult; } @@ -915,6 +928,7 @@ accountSendIOU( AccountID const& uReceiverID, STAmount const& saAmount, beast::Journal j, + SLE::ref sponsorSle, WaiveTransferFee waiveFee) { if (view.rules().enabled(fixAMMv1_1)) @@ -946,7 +960,8 @@ accountSendIOU( JLOG(j.trace()) << "accountSendIOU: " << to_string(uSenderID) << " -> " << to_string(uReceiverID) << " : " << saAmount.getFullText(); - return directSendNoLimitIOU(view, uSenderID, uReceiverID, saAmount, saActual, j, waiveFee); + return directSendNoLimitIOU( + view, uSenderID, uReceiverID, saAmount, saActual, j, sponsorSle, waiveFee); } /* XRP send which does not check reserve and can do pure adjustment. @@ -1475,7 +1490,7 @@ directSendNoFee( { return saAmount.asset().visit( [&](Issue const&) { - return directSendNoFeeIOU(view, uSenderID, uReceiverID, saAmount, bCheckIssuer, j); + return directSendNoFeeIOU(view, uSenderID, uReceiverID, saAmount, bCheckIssuer, {}, j); }, [&](MPTIssue const&) { XRPL_ASSERT(!bCheckIssuer, "xrpl::directSendNoFee : not checking issuer"); @@ -1490,12 +1505,13 @@ accountSend( AccountID const& uReceiverID, STAmount const& saAmount, beast::Journal j, + SLE::ref sponsorSle, WaiveTransferFee waiveFee, AllowMPTOverflow allowOverflow) { return saAmount.asset().visit( [&](Issue const&) { - return accountSendIOU(view, uSenderID, uReceiverID, saAmount, j, waiveFee); + return accountSendIOU(view, uSenderID, uReceiverID, saAmount, j, sponsorSle, waiveFee); }, [&](MPTIssue const&) { return accountSendMPT( diff --git a/src/libxrpl/protocol/Indexes.cpp b/src/libxrpl/protocol/Indexes.cpp index 76ff868f9e..ced18d4db6 100644 --- a/src/libxrpl/protocol/Indexes.cpp +++ b/src/libxrpl/protocol/Indexes.cpp @@ -84,6 +84,7 @@ enum class LedgerNameSpace : std::uint16_t { Vault = 'V', LoanBroker = 'l', // lower-case L Loan = 'L', + Sponsorship = '>', // No longer used or supported. Left here to reserve the space to avoid accidental reuse. Contract [[deprecated]] = 'c', @@ -326,6 +327,12 @@ signerList(AccountID const& account) noexcept return signerList(account, 0); } +Keylet +sponsorship(AccountID const& sponsor, AccountID const& sponsee) noexcept +{ + return {ltSPONSORSHIP, indexHash(LedgerNameSpace::Sponsorship, sponsor, sponsee)}; +} + Keylet check(AccountID const& id, std::uint32_t seq) noexcept { diff --git a/src/libxrpl/protocol/InnerObjectFormats.cpp b/src/libxrpl/protocol/InnerObjectFormats.cpp index 66b2822a42..0bdb217771 100644 --- a/src/libxrpl/protocol/InnerObjectFormats.cpp +++ b/src/libxrpl/protocol/InnerObjectFormats.cpp @@ -160,6 +160,14 @@ InnerObjectFormats::InnerObjectFormats() {sfTxnSignature, SoeOptional}, {sfSigners, SoeOptional}, }); + + add(sfSponsorSignature.jsonName.cStr(), + sfSponsorSignature.getCode(), + { + {sfSigningPubKey, SoeOptional}, + {sfTxnSignature, SoeOptional}, + {sfSigners, SoeOptional}, + }); } InnerObjectFormats const& diff --git a/src/libxrpl/protocol/LedgerFormats.cpp b/src/libxrpl/protocol/LedgerFormats.cpp index 8b91bb7930..a29ea49266 100644 --- a/src/libxrpl/protocol/LedgerFormats.cpp +++ b/src/libxrpl/protocol/LedgerFormats.cpp @@ -15,6 +15,7 @@ LedgerFormats::getCommonFields() {sfLedgerIndex, SoeOptional}, {sfLedgerEntryType, SoeRequired}, {sfFlags, SoeRequired}, + {sfSponsor, SoeOptional}, }; return kCommonFields; } diff --git a/src/libxrpl/protocol/STObject.cpp b/src/libxrpl/protocol/STObject.cpp index 869700baed..e86b3fb6e6 100644 --- a/src/libxrpl/protocol/STObject.cpp +++ b/src/libxrpl/protocol/STObject.cpp @@ -634,16 +634,16 @@ STObject::getAccountID(SField const& field) const } AccountID -STObject::getFeePayer() const +STObject::getInitiator() const { - // If sfDelegate is present, the delegate account is the payer + // If sfDelegate is present, the delegate account is the initiator // note: if a delegate is specified, its authorization to act on behalf of the account is // enforced in `Transactor::invokeCheckPermission` // cryptographic signature validity is checked separately (e.g., in `Transactor::checkSign`) if (isFieldPresent(sfDelegate)) return getAccountID(sfDelegate); - // Default payer + // Default initiator return getAccountID(sfAccount); } diff --git a/src/libxrpl/protocol/STTx.cpp b/src/libxrpl/protocol/STTx.cpp index 8438d3498b..be4ba2e8e5 100644 --- a/src/libxrpl/protocol/STTx.cpp +++ b/src/libxrpl/protocol/STTx.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -271,6 +272,13 @@ STTx::checkSign(Rules const& rules) const return std::unexpected("Counterparty: " + ret.error()); } + if (isFieldPresent(sfSponsorSignature)) + { + auto const sponsorSignatureObj = getFieldObject(sfSponsorSignature); + if (auto const ret = checkSign(rules, sponsorSignatureObj); !ret) + return std::unexpected("Sponsor: " + ret.error()); + } + // Verify the batch signer signatures here too, so they are cached with the // rest of signature checking (checkValidity / SF_SIGGOOD) and stay out of // the transaction engine. Gated on a batch (batchTxnIds_ seated) that @@ -551,7 +559,7 @@ STTx::checkMultiSign(Rules const& rules, STObject const& sigObject) const // For delegated transactions sfDelegate is the account whose signer list is checked, // the delegate account itself can not be among the signers. auto const txnAccountID = - &sigObject != this ? std::nullopt : std::optional(getFeePayer()); + &sigObject != this ? std::nullopt : std::optional(getInitiator()); // We can ease the computational load inside the loop a bit by // pre-constructing part of the data that we hash. Fill a Serializer @@ -603,6 +611,15 @@ STTx::getBatchTransactionIDs() const return *batchTxnIds_; } +AccountID +STTx::getFeePayerID() const +{ + if (isFieldPresent(sfSponsor) && ((getFieldU32(sfSponsorFlags) & spfSponsorFee) != 0u)) + return at(sfSponsor); + + return getInitiator(); +} + //------------------------------------------------------------------------------ static bool diff --git a/src/libxrpl/protocol/TER.cpp b/src/libxrpl/protocol/TER.cpp index a6f8192a2f..c2167d58ce 100644 --- a/src/libxrpl/protocol/TER.cpp +++ b/src/libxrpl/protocol/TER.cpp @@ -107,6 +107,7 @@ transResults() MAKE_ERROR(tecPSEUDO_ACCOUNT, "This operation is not allowed against a pseudo-account."), MAKE_ERROR(tecPRECISION_LOSS, "The amounts used by the transaction cannot interact."), MAKE_ERROR(tecBAD_PROOF, "Proof cannot be verified"), + MAKE_ERROR(tecNO_SPONSOR_PERMISSION, "Sponsor has not authorized this transaction."), MAKE_ERROR(tefALREADY, "The exact transaction was already in this ledger."), MAKE_ERROR(tefBAD_ADD_AUTH, "Not authorized to add account."), @@ -220,6 +221,7 @@ transResults() MAKE_ERROR(terADDRESS_COLLISION, "Failed to allocate an unique account address."), MAKE_ERROR(terNO_DELEGATE_PERMISSION, "Delegated account lacks permission to perform this transaction."), MAKE_ERROR(terLOCKED, "Fund is locked."), + MAKE_ERROR(terNO_PERMISSION, "No permission to perform requested operation."), MAKE_ERROR(tesSUCCESS, "The transaction was applied. Only final in a validated ledger."), }; diff --git a/src/libxrpl/protocol/TxFormats.cpp b/src/libxrpl/protocol/TxFormats.cpp index b926bdf0e5..e4d4c4b03c 100644 --- a/src/libxrpl/protocol/TxFormats.cpp +++ b/src/libxrpl/protocol/TxFormats.cpp @@ -30,6 +30,9 @@ TxFormats::getCommonFields() {sfSigners, SoeOptional}, // submit_multisigned {sfNetworkID, SoeOptional}, {sfDelegate, SoeOptional}, + {sfSponsor, SoeOptional}, + {sfSponsorFlags, SoeOptional}, + {sfSponsorSignature, SoeOptional}, }; return kCommonFields; } diff --git a/src/libxrpl/tx/Transactor.cpp b/src/libxrpl/tx/Transactor.cpp index 0f6988543e..065dead1fd 100644 --- a/src/libxrpl/tx/Transactor.cpp +++ b/src/libxrpl/tx/Transactor.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +42,7 @@ #include #include +#include #include #include #include @@ -167,6 +169,58 @@ preflightCheckSimulateKeys(ApplyFlags flags, STObject const& sigObject, beast::J } // namespace detail +static NotTEC +preflight1Sponsor(PreflightContext const& ctx) +{ + bool const hasSponsor = ctx.tx.isFieldPresent(sfSponsor); + bool const hasSponsorFlags = ctx.tx.isFieldPresent(sfSponsorFlags); + bool const hasSponsorSig = ctx.tx.isFieldPresent(sfSponsorSignature); + + if ((hasSponsor || hasSponsorFlags || hasSponsorSig) && !ctx.rules.enabled(featureSponsor)) + return temDISABLED; + + if (hasSponsor != hasSponsorFlags) + { + JLOG(ctx.j.debug()) << "preflight1: sponsor and sponsor flags mismatch"; + return temINVALID_FLAG; + } + if (hasSponsorSig && (!hasSponsor || !hasSponsorFlags)) + { + JLOG(ctx.j.debug()) << "preflight1: sponsor signature without sponsor definition"; + return temMALFORMED; + } + + if (hasSponsorFlags) + { + auto const sponsorFlags = ctx.tx.getFieldU32(sfSponsorFlags); + if (((sponsorFlags & spfSponsorFlagMask) != 0u) || sponsorFlags == 0) + { + JLOG(ctx.j.debug()) << "preflight1: invalid sponsor flags"; + return temINVALID_FLAG; + } + + // Reserve sponsorship is only permitted for an explicit allow-list of + // transaction types, for v1. All other tx types reject spfSponsorReserve here. + if (isReserveSponsored(ctx.tx)) + { + if (!isReserveSponsorAllowed(ctx.tx.getTxnType())) + { + JLOG(ctx.j.debug()) + << "preflight1: spfSponsorReserve not allowed for this transaction type"; + return temINVALID_FLAG; + } + } + } + + if (hasSponsor && ctx.tx.getAccountID(sfSponsor) == ctx.tx.getAccountID(sfAccount)) + { + JLOG(ctx.j.debug()) << "preflight1: Sponsor account cannot be the same as the account"; + return temMALFORMED; + } + + return tesSUCCESS; +} + /** Performs early sanity checks on the account and fee fields */ NotTEC Transactor::preflight1(PreflightContext const& ctx, std::uint32_t flagMask) @@ -230,6 +284,9 @@ Transactor::preflight1(PreflightContext const& ctx, std::uint32_t flagMask) if (ctx.tx.isFlag(tfInnerBatchTxn) != ctx.parentBatchId.has_value()) return temINVALID_INNER_BATCH; + if (auto const ter = preflight1Sponsor(ctx); !isTesSuccess(ter)) + return ter; + return tesSUCCESS; } @@ -343,6 +400,44 @@ Transactor::checkPermission( return tesSUCCESS; } +NotTEC +Transactor::checkSponsor(ReadView const& view, STTx const& tx) +{ + if (!tx.isFieldPresent(sfSponsor)) + return tesSUCCESS; + + // Reserve sponsorship with permissioned delegation is disallowed. + if (tx.isFieldPresent(sfDelegate) && isReserveSponsored(tx)) + return temINVALID; + + if (!view.exists(keylet::account(tx.getAccountID(sfSponsor)))) + return terNO_ACCOUNT; + + // Skip Sponsorship existence checks if the sponsor has signed the transaction - this + // transaction is valid regardless of the Sponsorship object. + // The use of the Sponsorship object is properly handled in + // getFeePayer/checkReserve/increaseOwnerCount/decreaseOwnerCount. + if (tx.isFieldPresent(sfSponsorSignature)) + return tesSUCCESS; + + // If the transaction contains sfDelegate, the Sponsorship object should be + // between the sponsor and the delegate. + auto const sponsorshipSle = + view.read(keylet::sponsorship(tx.getAccountID(sfSponsor), tx.getInitiator())); + + // sponsorship object missing for pre-funded (no co-signing) tx + if (!sponsorshipSle) + return terNO_PERMISSION; + + if (isFeeSponsored(tx) && sponsorshipSle->isFlag(lsfSponsorshipRequireSignForFee)) + return terNO_PERMISSION; + + if (isReserveSponsored(tx) && sponsorshipSle->isFlag(lsfSponsorshipRequireSignForReserve)) + return terNO_PERMISSION; + + return tesSUCCESS; +} + XRPAmount Transactor::calculateBaseFee(ReadView const& view, STTx const& tx) { @@ -351,6 +446,7 @@ Transactor::calculateBaseFee(ReadView const& view, STTx const& tx) // The computation has two parts: // * The base fee, which is the same for most transactions. // * The additional cost of each multisignature on the transaction. + // * The additional cost of each multisignature on the sponsor. XRPAmount const baseFee = view.fees().base; // Each signer adds one more baseFee to the minimum required fee @@ -358,7 +454,15 @@ Transactor::calculateBaseFee(ReadView const& view, STTx const& tx) std::size_t const signerCount = tx.isFieldPresent(sfSigners) ? tx.getFieldArray(sfSigners).size() : 0; - return baseFee + (signerCount * baseFee); + std::size_t sponsorSignerCount = 0; + if (tx.isFieldPresent(sfSponsorSignature)) + { + auto const sponsorObj = tx.getFieldObject(sfSponsorSignature); + if (sponsorObj.isFieldPresent(sfSigners)) + sponsorSignerCount += sponsorObj.getFieldArray(sfSigners).size(); + } + + return baseFee + ((signerCount + sponsorSignerCount) * baseFee); } XRPAmount @@ -437,12 +541,51 @@ Transactor::checkFee(PreclaimContext const& ctx, XRPAmount baseFee) if (feePaid == beast::kZero) return tesSUCCESS; - auto const id = ctx.tx.getFeePayer(); - auto const sle = ctx.view.read(keylet::account(id)); - if (!sle) - return terNO_ACCOUNT; + auto const feePayer = getFeePayer(ctx.view, ctx.tx); + auto const payerSle = ctx.view.read(feePayer.keylet); - auto const balance = (*sle)[sfBalance].xrp(); + if (!payerSle) + { + if (feePayer.type == FeePayerType::SponsorPreFunded) + { + // Sanity check: already checked in checkSponsor + return tefINTERNAL; // LCOV_EXCL_LINE + } + + return terNO_ACCOUNT; + } + + XRPAmount maxSpendable = beast::kZero; + + if (feePayer.type == FeePayerType::SponsorPreFunded) + { + if (payerSle->getType() != ltSPONSORSHIP) + return tefINTERNAL; // LCOV_EXCL_LINE + + if (payerSle->isFieldPresent(feePayer.balanceField)) + maxSpendable = payerSle->getFieldAmount(feePayer.balanceField).xrp(); + + if (payerSle->isFieldPresent(sfMaxFee)) + { + auto const cap = payerSle->getFieldAmount(sfMaxFee).xrp(); + maxSpendable = std::min(maxSpendable, cap); + } + } + else + { + if (payerSle->getType() != ltACCOUNT_ROOT) + return tefINTERNAL; // LCOV_EXCL_LINE + + if (feePayer.type == FeePayerType::SponsorCoSigned) + { + auto const sponsorReserve = accountReserve(ctx.view, payerSle, ctx.j); + maxSpendable = payerSle->getFieldAmount(sfBalance).xrp() - sponsorReserve; + } + else + { + maxSpendable = payerSle->getFieldAmount(feePayer.balanceField).xrp(); + } + } // NOTE: Because preclaim evaluates against a static readview, it // does not reflect fee deductions from other transactions paid by @@ -451,12 +594,12 @@ Transactor::checkFee(PreclaimContext const& ctx, XRPAmount baseFee) // transactions, this check may pass optimistically. // The fee shortfall will be handled by the Transactor::reset mechanism, // which caps the fee to the remaining actual balance. - if (balance < feePaid) + if (maxSpendable < feePaid) { - JLOG(ctx.j.trace()) << "Insufficient balance:" << " balance=" << to_string(balance) + JLOG(ctx.j.trace()) << "Insufficient balance:" << " balance=" << to_string(maxSpendable) << " paid=" << to_string(feePaid); - if ((balance > beast::kZero) && !ctx.view.open()) + if ((maxSpendable > beast::kZero) && !ctx.view.open()) { // Closed ledger, non-zero balance, less than fee return tecINSUFF_FEE; @@ -473,16 +616,72 @@ Transactor::payFee() { auto const feePaid = ctx_.tx[sfFee].xrp(); - auto const feePayer = ctx_.tx.getFeePayer(); - auto const sle = view().peek(keylet::account(feePayer)); + auto const feePayer = getFeePayer(view(), ctx_.tx); + auto const sle = view().peek(feePayer.keylet); + + JLOG(j_.trace()) << "Fee payer: " + to_string(feePayer.id); + if (!sle) return tefINTERNAL; // LCOV_EXCL_LINE - // Deduct the fee, so it's not available during the transaction. - // Will only write the account back if the transaction succeeds. - sle->setFieldAmount(sfBalance, sle->getFieldAmount(sfBalance) - feePaid); - if (feePayer != accountID_) - view().update(sle); // done in `apply()` for the account + if (feePaid == beast::kZero) + return tesSUCCESS; + + XRPAmount balance = beast::kZero; + if (sle->isFieldPresent(feePayer.balanceField)) + { + balance = sle->getFieldAmount(feePayer.balanceField).xrp(); + } + else if (feePayer.balanceField != sfFeeAmount) + { + return tefINTERNAL; // LCOV_EXCL_LINE + } + + // A co-signed sponsor pays the fee out of its own account balance, but must + // never be charged into its account reserve, and a pre-funded sponsorship's + // fee is capped by sfMaxFee. Mirror the spendable amount computed in + // checkFee() so both limits are enforced on the apply path too. + XRPAmount spendable = balance; + if (feePayer.type == FeePayerType::SponsorCoSigned) + { + auto const sponsorReserve = accountReserve(view(), sle, j_); + // max(balance - reserve, 0) with overflow handling + spendable = balance > sponsorReserve ? balance - sponsorReserve : beast::kZero; + } + else if (feePayer.type == FeePayerType::SponsorPreFunded && sle->isFieldPresent(sfMaxFee)) + { + auto const cap = sle->getFieldAmount(sfMaxFee).xrp(); + spendable = std::min(spendable, cap); + } + + // Only sponsor fee-payers reject here on insufficient funds. For an + // ordinary account, the fee falls through and is capped by reset(), which + // caps to the account's balance. That capping is wrong for sponsors: a + // co-signed sponsor would be charged into its own reserve, and a prefunded + // sponsorship's fee amount should be rejected rather than partially spent. + if (feePaid > spendable && + (feePayer.type == FeePayerType::SponsorPreFunded || + feePayer.type == FeePayerType::SponsorCoSigned)) + { + if ((spendable > beast::kZero) && !view().open()) + return tecINSUFF_FEE; + + return terINSUF_FEE_B; + } + + auto const feeAmountAfter = balance - feePaid; + + if (feeAmountAfter == beast::kZero && feePayer.balanceField == sfFeeAmount) + { + // Because ltSponsorship.sfFeeAmount is soeOptional + sle->makeFieldAbsent(feePayer.balanceField); + } + else + { + sle->setFieldAmount(feePayer.balanceField, feeAmountAfter); + } + + view().update(sle); // VFALCO Should we call view().rawDestroyXRP() here as well? return tesSUCCESS; @@ -656,7 +855,7 @@ Transactor::ticketDelete( } // Update the Ticket owner's reserve. - adjustOwnerCount(view, sleAccount, -1, j); + decreaseOwnerCountForObject(view, sleAccount, sleTicket, 1, j); // Remove Ticket from ledger. view.erase(sleTicket); @@ -750,6 +949,21 @@ Transactor::checkSign( return tesSUCCESS; } + if (sigObject.isFieldPresent(sfSponsorSignature)) + { + // Co-signed sponsorship + + // Sanity check: already checked in preflight1 + if (!sigObject.isFieldPresent(sfSponsor)) + return tefINTERNAL; // LCOV_EXCL_LINE + + auto const sponsorID = sigObject.getAccountID(sfSponsor); + auto const sponsorSignature = sigObject.getFieldObject(sfSponsorSignature); + if (auto const ret = checkSign(view, flags, std::nullopt, sponsorID, sponsorSignature, j); + !isTesSuccess(ret)) + return ret; + } + // If the pk is empty and not simulate or simulate and signers, // then we must be multi-signing. if (sigObject.isFieldPresent(sfSigners)) @@ -1079,11 +1293,38 @@ Transactor::reset(XRPAmount fee) if (!txnAcct) return {tefINTERNAL, beast::kZero}; - auto const payerSle = view().peek(keylet::account(ctx_.tx.getFeePayer())); + auto const feePayer = getFeePayer(view(), ctx_.tx); + auto const payerSle = view().peek(feePayer.keylet); + if (!payerSle) return {tefINTERNAL, beast::kZero}; // LCOV_EXCL_LINE - auto const balance = payerSle->getFieldAmount(sfBalance).xrp(); + XRPAmount balance = beast::kZero; + if (payerSle->isFieldPresent(feePayer.balanceField)) + { + balance = payerSle->getFieldAmount(feePayer.balanceField).xrp(); + } + else if (feePayer.balanceField != sfFeeAmount) + { + return {tefINTERNAL, beast::kZero}; // LCOV_EXCL_LINE + } + + if (feePayer.type == FeePayerType::SponsorPreFunded && payerSle->isFieldPresent(sfMaxFee)) + { + auto const cap = payerSle->getFieldAmount(sfMaxFee).xrp(); + fee = std::min(fee, cap); + } + + // A co-signed sponsor must never be charged into its own account reserve, + // so the fee is capped to the balance above the reserve rather than to the + // full balance. + XRPAmount spendable = balance; + if (feePayer.type == FeePayerType::SponsorCoSigned) + { + auto const sponsorReserve = accountReserve(view(), payerSle, j_); + // max(balance - reserve, 0) with overflow handling + spendable = balance > sponsorReserve ? balance - sponsorReserve : beast::kZero; + } // balance should have already been checked in checkFee / preFlight. XRPL_ASSERT( @@ -1093,8 +1334,8 @@ Transactor::reset(XRPAmount fee) // We retry/reject the transaction if the account balance is zero or // we're applying against an open ledger and the balance is less than // the fee - if (fee > balance) - fee = balance; + if (fee > spendable) + fee = spendable; // Since we reset the context, we need to charge the fee and update // the account's sequence number (or consume the Ticket) again. @@ -1102,7 +1343,17 @@ Transactor::reset(XRPAmount fee) // If for some reason we are unable to consume the ticket or sequence // then the ledger is corrupted. Rather than make things worse we // reject the transaction. - payerSle->setFieldAmount(sfBalance, balance - fee); + auto const feeAmountAfter = balance - fee; + if (feeAmountAfter == beast::kZero && feePayer.balanceField == sfFeeAmount) + { + // Because ltSponsorship.sfFeeAmount is soeOptional + payerSle->makeFieldAbsent(feePayer.balanceField); + } + else + { + payerSle->setFieldAmount(feePayer.balanceField, feeAmountAfter); + } + TER const ter{consumeSeqProxy(txnAcct)}; XRPL_ASSERT(isTesSuccess(ter), "xrpl::Transactor::reset : result is tesSUCCESS"); @@ -1116,6 +1367,48 @@ Transactor::reset(XRPAmount fee) return {ter, fee}; } +FeePayer +Transactor::getFeePayer(ReadView const& view, STTx const& tx) +{ + if (tx.isFieldPresent(sfSponsor) && isFeeSponsored(tx)) + { + auto const sponsorID = tx.getAccountID(sfSponsor); + auto const sponseeID = tx.getInitiator(); + auto const sponsorshipKeylet = keylet::sponsorship(sponsorID, sponseeID); + + // if pre-funded sponsorship exists, prefer it + if (view.exists(sponsorshipKeylet)) + { + // pre funded + return FeePayer{ + .id = sponsorID, + .keylet = sponsorshipKeylet, + .balanceField = sfFeeAmount, + .type = FeePayerType::SponsorPreFunded}; + } + + // Checked in Transactor::checkSponsor + XRPL_ASSERT( + tx.isFieldPresent(sfSponsorSignature), + "xrpl::getFeePayer has sponsor signature without a sponsorship object"); + + // co-signed + return FeePayer{ + .id = sponsorID, + .keylet = keylet::account(sponsorID), + .balanceField = sfBalance, + .type = FeePayerType::SponsorCoSigned}; + } + + AccountID const payerID = tx.getInitiator(); + auto const payerAccountKeylet = keylet::account(payerID); + auto const payerType = + tx.isFieldPresent(sfDelegate) ? FeePayerType::Delegate : FeePayerType::Account; + + return FeePayer{ + .id = payerID, .keylet = payerAccountKeylet, .balanceField = sfBalance, .type = payerType}; +} + // The sole purpose of this function is to provide a convenient, named // location to set a breakpoint, to be used when replaying transactions. void diff --git a/src/libxrpl/tx/applySteps.cpp b/src/libxrpl/tx/applySteps.cpp index caaacfd010..5af4f621a7 100644 --- a/src/libxrpl/tx/applySteps.cpp +++ b/src/libxrpl/tx/applySteps.cpp @@ -181,6 +181,9 @@ invokePreclaim(PreclaimContext const& ctx) if (NotTEC const result = T::checkPriorTxAndLastLedger(ctx)) return result; + if (NotTEC const result = T::checkSponsor(ctx.view, ctx.tx)) + return result; + if (NotTEC const result = Transactor::invokeCheckPermission(ctx.view, ctx.tx)) return result; diff --git a/src/libxrpl/tx/invariants/InvariantCheck.cpp b/src/libxrpl/tx/invariants/InvariantCheck.cpp index 32df44a96b..220a7a8e41 100644 --- a/src/libxrpl/tx/invariants/InvariantCheck.cpp +++ b/src/libxrpl/tx/invariants/InvariantCheck.cpp @@ -148,6 +148,15 @@ XRPNotCreated::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref a if (isXRP((*before)[sfAmount])) drops_ -= (*before)[sfAmount].xrp().drops(); break; + case ltSPONSORSHIP: + if (before->isFieldPresent(sfFeeAmount)) + { + XRPL_ASSERT( + isXRP((*before)[sfFeeAmount]), + "XRPNotCreated::visitEntry : Sponsorship.FeeAmount is XRP"); + drops_ -= (*before)[sfFeeAmount].xrp().drops(); + } + break; default: break; } @@ -168,6 +177,15 @@ XRPNotCreated::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref a if (!isDelete && isXRP((*after)[sfAmount])) drops_ += (*after)[sfAmount].xrp().drops(); break; + case ltSPONSORSHIP: + if (!isDelete && after->isFieldPresent(sfFeeAmount)) + { + XRPL_ASSERT( + isXRP((*after)[sfFeeAmount]), + "XRPNotCreated::visitEntry : Sponsorship.FeeAmount is XRP"); + drops_ += (*after)[sfFeeAmount].xrp().drops(); + } + break; default: break; } @@ -515,6 +533,20 @@ AccountRootsDeletedClean::finalize( if (enforce) return false; } + // An account should not be deleted with sponsorship fields + if (after->isFieldPresent(sfSponsoredOwnerCount) || + after->isFieldPresent(sfSponsoringOwnerCount) || + after->isFieldPresent(sfSponsoringAccountCount) || after->isFieldPresent(sfSponsor)) + { + JLOG(j.fatal()) << "Invariant failed: account deletion left " + "behind a sponsorship field"; + XRPL_ASSERT( + enforce, + "xrpl::AccountRootsDeletedClean::finalize : " + "deleted account has no sponsorship fields"); + if (enforce) + return false; + } // Simple types for (auto const& [keyletfunc, _1, _2] : kDirectAccountKeylets) { @@ -881,8 +913,10 @@ ValidPseudoAccounts::visitEntry(bool isDelete, SLE::const_ref before, SLE::const // 1. Exactly one of the pseudo-account fields is set. // 2. The sequence number is not changed. // 3. The lsfDisableMaster, lsfDefaultRipple, and lsfDepositAuth - // flags are set. + // flags are set. // 4. The RegularKey is not set. + // 5. The SponsoredOwnerCount, SponsoringOwnerCount, SponsoringAccountCount, Sponsor + // fields are not set. { std::vector const& fields = getPseudoAccountFields(); @@ -908,6 +942,12 @@ ValidPseudoAccounts::visitEntry(bool isDelete, SLE::const_ref before, SLE::const { errors_.emplace_back("pseudo-account has a regular key"); } + if (after->isFieldPresent(sfSponsoredOwnerCount) || + after->isFieldPresent(sfSponsoringOwnerCount) || after->isFieldPresent(sfSponsor) || + after->isFieldPresent(sfSponsoringAccountCount)) + { + errors_.emplace_back("pseudo-account has a sponsorship field"); + } } } } diff --git a/src/libxrpl/tx/invariants/SponsorshipInvariant.cpp b/src/libxrpl/tx/invariants/SponsorshipInvariant.cpp new file mode 100644 index 0000000000..bdf37492f3 --- /dev/null +++ b/src/libxrpl/tx/invariants/SponsorshipInvariant.cpp @@ -0,0 +1,158 @@ +#include +// +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace xrpl { + +// Add new sponsorship-related invariants implementations +void +SponsorshipOwnerCountsMatch::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) +{ + auto getSponsored = [](SLE::const_ref sle) -> std::uint32_t { + if (sle && sle->getType() == ltACCOUNT_ROOT) + return sle->getFieldU32(sfSponsoredOwnerCount); + return 0; + }; + auto getSponsoring = [](SLE::const_ref sle) -> std::uint32_t { + if (sle && sle->getType() == ltACCOUNT_ROOT) + return sle->getFieldU32(sfSponsoringOwnerCount); + return 0; + }; + + auto getOwnerCount = [](SLE::const_ref sle) -> std::uint32_t { + if (sle && sle->getType() == ltACCOUNT_ROOT) + return sle->getFieldU32(sfOwnerCount); + return 0; + }; + + auto getSponsoredObjectOwnerCount = [&](SLE::const_ref sle) -> std::uint32_t { + if (!sle) + return 0; + switch (sle->getType()) + { + case ltACCOUNT_ROOT: + return 0; + case ltRIPPLE_STATE: { + // A trust line can be reserve-sponsored independently on each + // side, so it may contribute up to two sponsored owner counts. + uint32_t ownerCount = 0; + if (sle->isFieldPresent(sfHighSponsor)) + ownerCount++; + if (sle->isFieldPresent(sfLowSponsor)) + ownerCount++; + return ownerCount; + } + default: + // Every other supported type carries a single sfSponsor field + // and contributes its full owner-count magnitude only when it is + // sponsored. + if (!sle->isFieldPresent(sfSponsor)) + return 0; + return getLedgerEntryOwnerCount(*sle); + } + }; + + // The values are implicitly casted to std::int64_t to calculate deltas. + std::int64_t const beforeSponsored = getSponsored(before); + std::int64_t const afterSponsored = getSponsored(after); + std::int64_t const beforeSponsoring = getSponsoring(before); + std::int64_t const afterSponsoring = getSponsoring(after); + + std::int64_t const beforeSponsoredObjectOwnerCount = getSponsoredObjectOwnerCount(before); + std::int64_t const afterSponsoredObjectOwnerCount = + isDelete ? 0 : getSponsoredObjectOwnerCount(after); + + deltaSponsoredOwnerCount_ += (afterSponsored - beforeSponsored); + deltaSponsoringOwnerCount_ += (afterSponsoring - beforeSponsoring); + + deltaSponsoredObjectOwnerCount_ += + (afterSponsoredObjectOwnerCount - beforeSponsoredObjectOwnerCount); + + if (getOwnerCount(after) < getSponsored(after)) + ownerCountBelowSponsored_ += 1; +} + +bool +SponsorshipOwnerCountsMatch::finalize( + STTx const&, + TER const, + XRPAmount const, + ReadView const&, + beast::Journal const& j) const +{ + if (deltaSponsoredOwnerCount_ != deltaSponsoringOwnerCount_) + { + JLOG(j.fatal()) << "Invariant failed: SponsoredOwnerCount does not " + "equal SponsoringOwnerCount delta."; + return false; + } + + if (ownerCountBelowSponsored_ > 0) + { + JLOG(j.fatal()) + << "Invariant failed: OwnerCount must be greater than or equal to SponsoredOwnerCount."; + return false; + } + + if (deltaSponsoredObjectOwnerCount_ != deltaSponsoredOwnerCount_) + { + JLOG(j.fatal()) << "Invariant failed: SponsoredObjectOwnerCount does not " + "equal SponsoredOwnerCount delta."; + return false; + } + + return true; +} + +void +SponsorshipAccountCountMatchesField::visitEntry(bool, SLE::const_ref before, SLE::const_ref after) +{ + auto getSponsoringAccountCount = [](SLE::const_ref sle) -> std::uint32_t { + if (sle && sle->getType() == ltACCOUNT_ROOT) + return sle->getFieldU32(sfSponsoringAccountCount); + return 0; + }; + + auto hasSponsorField = [](SLE::const_ref sle) -> bool { + return sle && sle->getType() == ltACCOUNT_ROOT && sle->isFieldPresent(sfSponsor); + }; + + std::int64_t const beforeCount = getSponsoringAccountCount(before); + std::int64_t const afterCount = getSponsoringAccountCount(after); + deltaSponsoringAccountCount_ += (afterCount - beforeCount); + + int const beforePresent = hasSponsorField(before) ? 1 : 0; + int const afterPresent = hasSponsorField(after) ? 1 : 0; + deltaSponsorFieldPresence_ += (afterPresent - beforePresent); +} + +bool +SponsorshipAccountCountMatchesField::finalize( + STTx const&, + TER const, + XRPAmount const, + ReadView const&, + beast::Journal const& j) const +{ + if (deltaSponsoringAccountCount_ != deltaSponsorFieldPresence_) + { + JLOG(j.fatal()) << "Invariant failed: Net delta of SponsoringAccountCount does not " + "match net delta of sfSponsor presence."; + return false; + } + + return true; +} + +} // namespace xrpl diff --git a/src/libxrpl/tx/invariants/VaultInvariant.cpp b/src/libxrpl/tx/invariants/VaultInvariant.cpp index 84814977db..a9ba0ec874 100644 --- a/src/libxrpl/tx/invariants/VaultInvariant.cpp +++ b/src/libxrpl/tx/invariants/VaultInvariant.cpp @@ -222,7 +222,10 @@ ValidVault::deltaAssetsTxAccount(STTx const& tx, XRPAmount fee) const if (!ret.has_value() || !vaultAsset.native()) return ret; - if (auto const delegate = tx[~sfDelegate]; delegate.has_value() && *delegate != tx[sfAccount]) + // Only add the fee back if tx[sfAccount] actually paid it. When the fee is + // paid by someone else (a delegate or a fee sponsor), the + // account's XRP balance moved only by the vault amount. + if (tx.getFeePayerID() != tx[sfAccount]) return ret; ret->delta += fee.drops(); @@ -1044,10 +1047,8 @@ ValidVault::finalize( case ttLOAN_SET: case ttLOAN_MANAGE: - case ttLOAN_PAY: { - // TBD + case ttLOAN_PAY: return true; - } default: // LCOV_EXCL_START diff --git a/src/libxrpl/tx/paths/BookStep.cpp b/src/libxrpl/tx/paths/BookStep.cpp index adb73f126e..bf24c66ee1 100644 --- a/src/libxrpl/tx/paths/BookStep.cpp +++ b/src/libxrpl/tx/paths/BookStep.cpp @@ -731,7 +731,7 @@ BookStep::forEachOffer( // Create MPToken for the offer's owner. No need to check // for the reserve since the offer is removed if it is consumed. // Therefore, the owner count remains the same. - if (auto const err = checkCreateMPT(sb, assetIn.get(), owner, j_); + if (auto const err = checkCreateMPT(sb, assetIn.get(), owner, {}, j_); !isTesSuccess(err)) { return true; diff --git a/src/libxrpl/tx/paths/MPTEndpointStep.cpp b/src/libxrpl/tx/paths/MPTEndpointStep.cpp index a47cfa15a5..0a0f6a9f27 100644 --- a/src/libxrpl/tx/paths/MPTEndpointStep.cpp +++ b/src/libxrpl/tx/paths/MPTEndpointStep.cpp @@ -410,7 +410,8 @@ MPTEndpointOfferCrossingStep::checkCreateMPT(ApplyView& view, xrpl::DebtDirectio // for the reserve since the offer doesn't go on the books // if crossed. Insufficient reserve is allowed if the offer // crossed. See CreateOffer::applyGuts() for reserve check. - if (auto const err = xrpl::checkCreateMPT(view, mptIssue_, dst_, j_); !isTesSuccess(err)) + if (auto const err = xrpl::checkCreateMPT(view, mptIssue_, dst_, {}, j_); + !isTesSuccess(err)) { JLOG(j_.trace()) << "MPTEndpointStep::checkCreateMPT: failed create MPT"; resetCache(srcDebtDir); diff --git a/src/libxrpl/tx/transactors/account/AccountDelete.cpp b/src/libxrpl/tx/transactors/account/AccountDelete.cpp index 254733a618..0055fce403 100644 --- a/src/libxrpl/tx/transactors/account/AccountDelete.cpp +++ b/src/libxrpl/tx/transactors/account/AccountDelete.cpp @@ -35,7 +35,6 @@ #include namespace xrpl { - bool AccountDelete::checkExtraFeatures(PreflightContext const& ctx) { @@ -268,6 +267,15 @@ AccountDelete::preclaim(PreclaimContext const& ctx) if (cp) return tecHAS_OBLIGATIONS; + if (sleAccount->isFieldPresent(sfSponsor)) + { + if (dst != sleAccount->getAccountID(sfSponsor)) + return tecNO_SPONSOR_PERMISSION; + } + if (sleAccount->isFieldPresent(sfSponsoringOwnerCount) || + sleAccount->isFieldPresent(sfSponsoringAccountCount)) + return tecHAS_OBLIGATIONS; + // We don't allow an account to be deleted if its sequence number // is within 256 of the current ledger. This prevents replay of old // transactions if this account is resurrected after it is deleted. @@ -394,6 +402,35 @@ AccountDelete::doApply() (*src)[sfBalance] = (*src)[sfBalance] - remainingBalance; ctx_.deliver(remainingBalance); + if (src->isFieldPresent(sfSponsor)) + { + auto const sponsorID = src->getAccountID(sfSponsor); + auto sponsorSle = view().peek(keylet::account(sponsorID)); + + if (!sponsorSle) + return tefINTERNAL; // LCOV_EXCL_LINE + + auto const sponsoringAccountCount = sponsorSle->getFieldU32(sfSponsoringAccountCount); + + XRPL_ASSERT( + sponsoringAccountCount != 0, + "xrpl::AccountDelete::doApply : sponsoring account count is present"); + if (sponsoringAccountCount == 0) + { + // sanity check + // Since sfSponsoringAccountCount is set to soeDEFAULT, the field will not be + // present with a value of 0. + return tefINTERNAL; // LCOV_EXCL_LINE + } + sponsorSle->at(sfSponsoringAccountCount) = sponsoringAccountCount - 1; + view().update(sponsorSle); + + // Following line might look redundant, but without it, sfSponsor + // would end up remaining in after-ltAccountRoot during the + // InvariantCheck. + src->makeFieldAbsent(sfSponsor); + } + XRPL_ASSERT( (*src)[sfBalance] == XRPAmount(0), "xrpl::AccountDelete::doApply : source balance is zero"); diff --git a/src/libxrpl/tx/transactors/account/SignerListSet.cpp b/src/libxrpl/tx/transactors/account/SignerListSet.cpp index 2f95d32664..799c292bfc 100644 --- a/src/libxrpl/tx/transactors/account/SignerListSet.cpp +++ b/src/libxrpl/tx/transactors/account/SignerListSet.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -147,9 +148,7 @@ SignerListSet::preCompute() Transactor::preCompute(); } -// The return type is signed so it is compatible with the 3rd argument -// of adjustOwnerCount() (which must be signed). -static int +static std::uint32_t signerCountBasedOwnerCountDelta(std::size_t entryCount, Rules const& rules) { // We always compute the full change in OwnerCount, taking into account: @@ -165,7 +164,7 @@ signerCountBasedOwnerCountDelta(std::size_t entryCount, Rules const& rules) // units. A SignerList with 8 entries would cost 10 OwnerCount units. // // The static_cast should always be safe since entryCount should always - // be in the range from 1 to 32. + // be in the range from 1 to 32, so the result is always positive. // We've got a lot of room to grow. XRPL_ASSERT( entryCount >= STTx::kMinMultiSigners, @@ -196,12 +195,11 @@ removeSignersFromLedger( // There are two different ways that the OwnerCount could be managed. // If the lsfOneOwnerCount bit is set then remove just one owner count. // Otherwise use the pre-MultiSignReserve amendment calculation. - int removeFromOwnerCount = -1; + std::uint32_t removeFromOwnerCount = 1; if (!signers->isFlag(lsfOneOwnerCount)) { STArray const& actualList = signers->getFieldArray(sfSignerEntries); - removeFromOwnerCount = - signerCountBasedOwnerCountDelta(actualList.size(), view.rules()) * -1; + removeFromOwnerCount = signerCountBasedOwnerCountDelta(actualList.size(), view.rules()); } // Remove the node from the account directory. @@ -215,8 +213,8 @@ removeSignersFromLedger( // LCOV_EXCL_STOP } - adjustOwnerCount( - view, view.peek(accountKeylet), removeFromOwnerCount, registry.getJournal("View")); + decreaseOwnerCountForObject( + view, view.peek(accountKeylet), signers, removeFromOwnerCount, registry.getJournal("View")); view.erase(signers); @@ -315,19 +313,20 @@ SignerListSet::replaceSignerList() if (!sle) return tefINTERNAL; // LCOV_EXCL_LINE - // Compute new reserve. Verify the account has funds to meet the reserve. - std::uint32_t const oldOwnerCount{(*sle)[sfOwnerCount]}; - static constexpr int kAddedOwnerCount = 1; std::uint32_t const flags{lsfOneOwnerCount}; - XRPAmount const newReserve{view().fees().accountReserve(oldOwnerCount + kAddedOwnerCount)}; - // We check the reserve against the starting balance because we want to // allow dipping into the reserve to pay fees. This behavior is consistent // with TicketCreate. - if (preFeeBalance_ < newReserve) - return tecINSUFFICIENT_RESERVE; + if (auto const ret = checkReserve( + ctx_.getApplyViewContext(), + sle, + preFeeBalance_, + {.ownerCountDelta = kAddedOwnerCount}, + ctx_.journal); + !isTesSuccess(ret)) + return ret; // Everything's ducky. Add the ltSIGNER_LIST to the ledger. auto signerList = std::make_shared(signerListKeylet); @@ -349,7 +348,8 @@ SignerListSet::replaceSignerList() // If we succeeded, the new entry counts against the // creator's reserve. - adjustOwnerCount(view(), sle, kAddedOwnerCount, viewJ); + increaseOwnerCount(ctx_.getApplyViewContext(), sle, kAddedOwnerCount, viewJ); + addSponsorToLedgerEntry(ctx_.getApplyViewContext(), signerList); return tesSUCCESS; } diff --git a/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp b/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp index f3ea97a341..92a8dccdb9 100644 --- a/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp +++ b/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp @@ -435,8 +435,7 @@ transferHelper( return tecINTERNAL; // LCOV_EXCL_LINE { - auto const ownerCount = sleSrc->getFieldU32(sfOwnerCount); - auto const reserve = psb.fees().accountReserve(ownerCount); + auto const reserve = accountReserve(psb, sleSrc, j); auto const availableBalance = [&]() -> STAmount { STAmount curBal = (*sleSrc)[sfBalance]; @@ -726,9 +725,8 @@ finalizeClaimHelper( } // Remove the claim id from the ledger + decreaseOwnerCountForObject(outerSb, sleOwner, sleClaimID, 1, j); outerSb.erase(sleClaimID); - - adjustOwnerCount(outerSb, sleOwner, -1, j); } } @@ -1028,7 +1026,7 @@ applyCreateAccountAttestations( // Check reserve auto const balance = (*sleDoor)[sfBalance]; - auto const reserve = psb.fees().accountReserve((*sleDoor)[sfOwnerCount] + 1); + auto const reserve = accountReserve(psb, sleDoor, j, {.ownerCountDelta = 1}); if (balance < reserve) return std::unexpected(tecINSUFFICIENT_RESERVE); @@ -1137,7 +1135,7 @@ applyCreateAccountAttestations( return tecINTERNAL; // LCOV_EXCL_LINE // Reserve was already checked - adjustOwnerCount(psb, sleDoor, 1, j); + increaseOwnerCount(psb, sleDoor, {}, 1, j); psb.insert(createdSleClaimID); psb.update(sleDoor); } @@ -1439,7 +1437,7 @@ XChainCreateBridge::preclaim(PreclaimContext const& ctx) return terNO_ACCOUNT; auto const balance = (*sleAcc)[sfBalance]; - auto const reserve = ctx.view.fees().accountReserve((*sleAcc)[sfOwnerCount] + 1); + auto const reserve = accountReserve(ctx.view, sleAcc, ctx.j, {.ownerCountDelta = 1}); if (balance < reserve) return tecINSUFFICIENT_RESERVE; @@ -1484,7 +1482,7 @@ XChainCreateBridge::doApply() (*sleBridge)[sfOwnerNode] = *page; } - adjustOwnerCount(ctx_.view(), sleAcct, 1, ctx_.journal); + increaseOwnerCount(ctx_.view(), sleAcct, {}, 1, ctx_.journal); ctx_.view().insert(sleBridge); ctx_.view().update(sleAcct); @@ -1987,8 +1985,7 @@ XChainCreateClaimID::preclaim(PreclaimContext const& ctx) return terNO_ACCOUNT; auto const balance = (*sleAcc)[sfBalance]; - auto const reserve = ctx.view.fees().accountReserve((*sleAcc)[sfOwnerCount] + 1); - + auto const reserve = accountReserve(ctx.view, sleAcc, ctx.j, {.ownerCountDelta = 1}); if (balance < reserve) return tecINSUFFICIENT_RESERVE; } @@ -2046,7 +2043,7 @@ XChainCreateClaimID::doApply() (*sleClaimID)[sfOwnerNode] = *page; } - adjustOwnerCount(ctx_.view(), sleAcct, 1, ctx_.journal); + increaseOwnerCount(ctx_.view(), sleAcct, {}, 1, ctx_.journal); ctx_.view().insert(sleClaimID); ctx_.view().update(sleBridge); diff --git a/src/libxrpl/tx/transactors/check/CheckCancel.cpp b/src/libxrpl/tx/transactors/check/CheckCancel.cpp index d966772191..ed60817224 100644 --- a/src/libxrpl/tx/transactors/check/CheckCancel.cpp +++ b/src/libxrpl/tx/transactors/check/CheckCancel.cpp @@ -91,8 +91,7 @@ CheckCancel::doApply() } // If we succeeded, update the check owner's reserve. - auto const sleSrc = view().peek(keylet::account(srcId)); - adjustOwnerCount(view(), sleSrc, -1, viewJ); + decreaseOwnerCountForObject(view(), srcId, sleCheck, 1, viewJ); // Remove check from ledger. view().erase(sleCheck); diff --git a/src/libxrpl/tx/transactors/check/CheckCash.cpp b/src/libxrpl/tx/transactors/check/CheckCash.cpp index 80ef742f52..4e810d94cf 100644 --- a/src/libxrpl/tx/transactors/check/CheckCash.cpp +++ b/src/libxrpl/tx/transactors/check/CheckCash.cpp @@ -3,11 +3,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -30,7 +32,7 @@ #include #include -#include +#include #include namespace xrpl { @@ -175,7 +177,7 @@ CheckCash::preclaim(PreclaimContext const& ctx) // once the check is cashed, since the check's reserve will no // longer be required. So, if we're dealing in XRP, we add one // reserve's worth to the available funds. - if (value.native()) + if (value.native() && !sleCheck->isFieldPresent(sfSponsor)) availableFunds += XRPAmount{ctx.view.fees().increment}; if (value > availableFunds) @@ -307,6 +309,8 @@ CheckCash::doApply() // LCOV_EXCL_STOP } + auto const sponsorCheckSle = getLedgerEntryReserveSponsor(psb, sleCheck); + // Preclaim already checked that source has at least the requested // funds. // @@ -335,7 +339,7 @@ CheckCash::doApply() // from src's directory, we allow them to send that additional // incremental reserve amount in the transfer. Hence the -1 // argument. - STAmount const srcLiquid{xrpLiquid(psb, srcId, -1, viewJ)}; + STAmount const srcLiquid{xrpLiquid(psb, srcId, sponsorCheckSle ? 0 : -1, viewJ)}; // Now, how much do they need in order to be successful? STAmount const xrpDeliver{ @@ -385,14 +389,25 @@ CheckCash::doApply() STAmount const flowDeliver{ optDeliverMin ? maxDeliverMin() : ctx_.tx.getFieldAmount(sfAmount)}; + auto applyViewContext = ApplyViewContext({.view = psb, .tx = ctx_.tx}); + auto const sponsorSle = getTxReserveSponsor(applyViewContext); + if (!sponsorSle) + return sponsorSle.error(); // LCOV_EXCL_LINE + // Check reserve. Return destination account SLE if enough reserve, // otherwise return nullptr. - auto checkReserve = [&]() -> SLE::pointer { + auto checkDstReserve = [&]() -> SLE::pointer { auto sleDst = psb.peek(keylet::account(accountID_)); // Can the account cover the trust line's or MPT reserve? - if (std::uint32_t const ownerCount = {sleDst->at(sfOwnerCount)}; - preFeeBalance_ < psb.fees().accountReserve(ownerCount + 1)) + if (auto const ret = checkReserve( + applyViewContext, + sleDst, + preFeeBalance_, + *sponsorSle, + {.ownerCountDelta = 1}, + j_); + !isTesSuccess(ret)) { JLOG(j_.trace()) << "Trust line does not exist. " "Insufficient reserve to create line."; @@ -426,7 +441,7 @@ CheckCash::doApply() // a. this (destination) account and // b. issuing account (not sending account). - auto const sleDst = checkReserve(); + auto const sleDst = checkDstReserve(); if (sleDst == nullptr) return tecNO_LINE_INSUF_RESERVE; @@ -449,6 +464,7 @@ CheckCash::doApply() Issue(currency, accountID_), // limit of zero 0, // quality in 0, // quality out + *sponsorSle, // sponsor viewJ); // journal !isTesSuccess(ter)) { @@ -491,11 +507,12 @@ CheckCash::doApply() auto const mptokenKey = keylet::mptoken(mptID, accountID_); if (!psb.exists(mptokenKey)) { - auto sleDst = checkReserve(); + auto sleDst = checkDstReserve(); if (sleDst == nullptr) return tecINSUFFICIENT_RESERVE; - if (auto const err = checkCreateMPT(psb, mptID, accountID_, j_); + if (auto const err = + checkCreateMPT(psb, mptID, accountID_, *sponsorSle, j_); !isTesSuccess(err)) { return err; @@ -581,7 +598,7 @@ CheckCash::doApply() } // If we succeeded, update the check owner's reserve. - adjustOwnerCount(psb, psb.peek(keylet::account(srcId)), -1, viewJ); + decreaseOwnerCountForObject(psb, srcId, sleCheck, 1, viewJ); // Remove check from ledger. psb.erase(sleCheck); diff --git a/src/libxrpl/tx/transactors/check/CheckCreate.cpp b/src/libxrpl/tx/transactors/check/CheckCreate.cpp index 595bcc4ab1..cb1d81ba4a 100644 --- a/src/libxrpl/tx/transactors/check/CheckCreate.cpp +++ b/src/libxrpl/tx/transactors/check/CheckCreate.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -194,13 +195,10 @@ CheckCreate::doApply() // A check counts against the reserve of the issuing account, but we // check the starting balance because we want to allow dipping into the // reserve to pay fees. - { - STAmount const reserve{view().fees().accountReserve(sle->getFieldU32(sfOwnerCount) + 1)}; - - if (preFeeBalance_ < reserve) - return tecINSUFFICIENT_RESERVE; - } - + if (auto const ret = checkReserve( + ctx_.getApplyViewContext(), sle, preFeeBalance_, {.ownerCountDelta = 1}, ctx_.journal); + !isTesSuccess(ret)) + return ret; // Note that we use the value from the sequence or ticket as the // Check sequence. For more explanation see comments in SeqProxy.h. std::uint32_t const seq = ctx_.tx.getSeqValue(); @@ -253,7 +251,9 @@ CheckCreate::doApply() sleCheck->setFieldU64(sfOwnerNode, *page); } // If we succeeded, the new entry counts against the creator's reserve. - adjustOwnerCount(view(), sle, 1, viewJ); + + increaseOwnerCount(ctx_.getApplyViewContext(), sle, 1, viewJ); + addSponsorToLedgerEntry(ctx_.getApplyViewContext(), sleCheck); return tesSUCCESS; } diff --git a/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp b/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp index e0ebdd893a..c0ef7ea9e8 100644 --- a/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp +++ b/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -11,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -93,12 +93,19 @@ CredentialAccept::doApply() if (!sleSubject || !sleIssuer) return tefINTERNAL; // LCOV_EXCL_LINE - { - STAmount const reserve{ - view().fees().accountReserve(sleSubject->getFieldU32(sfOwnerCount) + 1)}; - if (preFeeBalance_ < reserve) - return tecINSUFFICIENT_RESERVE; - } + auto txSponsorSle = getTxReserveSponsor(ctx_.getApplyViewContext()); + if (!txSponsorSle) + return txSponsorSle.error(); // LCOV_EXCL_LINE + + if (auto const ret = checkReserve( + ctx_.getApplyViewContext(), + sleSubject, + preFeeBalance_, + *txSponsorSle, + {.ownerCountDelta = 1}, + j_); + !isTesSuccess(ret)) + return ret; auto const credType(ctx_.tx[sfCredentialType]); Keylet const credentialKey = keylet::credential(accountID_, issuer, credType); @@ -115,10 +122,16 @@ CredentialAccept::doApply() } sleCred->setFieldU32(sfFlags, lsfAccepted); - view().update(sleCred); - adjustOwnerCount(view(), sleIssuer, -1, j_); - adjustOwnerCount(view(), sleSubject, 1, j_); + // Release the original creation sponsor from the credential (it covered + // the issuer's reserve), then assign the accept tx's sponsor (if any) so + // the credential reflects whoever is now covering the subject's reserve. + decreaseOwnerCountForObject(view(), sleIssuer, sleCred, 1, j_); + removeSponsorFromLedgerEntry(sleCred); + + addSponsorToLedgerEntry(ctx_.getApplyViewContext(), sleCred); + increaseOwnerCount(ctx_.getApplyViewContext(), sleSubject, 1, j_); + view().update(sleCred); return tesSUCCESS; } diff --git a/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp b/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp index acca408a95..e902ee73a6 100644 --- a/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp +++ b/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp @@ -7,13 +7,13 @@ #include #include // IWYU pragma: keep #include +#include #include #include #include #include #include #include -#include #include #include #include @@ -130,12 +130,10 @@ CredentialCreate::doApply() if (!sleIssuer) return tefINTERNAL; // LCOV_EXCL_LINE - { - STAmount const reserve{ - view().fees().accountReserve(sleIssuer->getFieldU32(sfOwnerCount) + 1)}; - if (preFeeBalance_ < reserve) - return tecINSUFFICIENT_RESERVE; - } + if (auto const ret = checkReserve( + ctx_.getApplyViewContext(), sleIssuer, preFeeBalance_, {.ownerCountDelta = 1}, j_); + !isTesSuccess(ret)) + return ret; sleCred->setAccountID(sfSubject, subject); sleCred->setAccountID(sfIssuer, accountID_); @@ -153,7 +151,8 @@ CredentialCreate::doApply() return tecDIR_FULL; sleCred->setFieldU64(sfIssuerNode, *page); - adjustOwnerCount(view(), sleIssuer, 1, j_); + increaseOwnerCount(ctx_.getApplyViewContext(), sleIssuer, 1, j_); + addSponsorToLedgerEntry(ctx_.getApplyViewContext(), sleCred); } if (subject == accountID_) diff --git a/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp b/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp index 32a51555b1..96e6c9e443 100644 --- a/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp +++ b/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp @@ -5,10 +5,10 @@ #include #include #include +#include #include #include #include -#include #include #include #include @@ -98,11 +98,14 @@ DelegateSet::doApply() if (permissions.empty()) return tecINTERNAL; // LCOV_EXCL_LINE - STAmount const reserve{ - ctx_.view().fees().accountReserve(sleOwner->getFieldU32(sfOwnerCount) + 1)}; - - if (preFeeBalance_ < reserve) - return tecINSUFFICIENT_RESERVE; + if (auto const ret = checkReserve( + ctx_.getApplyViewContext(), + sleOwner, + preFeeBalance_, + {.ownerCountDelta = 1}, + ctx_.journal); + !isTesSuccess(ret)) + return ret; sle = std::make_shared(delegateKey); sle->setAccountID(sfAccount, accountID_); @@ -130,7 +133,8 @@ DelegateSet::doApply() (*sle)[sfDestinationNode] = *destPage; ctx_.view().insert(sle); - adjustOwnerCount(ctx_.view(), sleOwner, 1, ctx_.journal); + increaseOwnerCount(ctx_.getApplyViewContext(), sleOwner, 1, ctx_.journal); + addSponsorToLedgerEntry(ctx_.getApplyViewContext(), sle); return tesSUCCESS; } @@ -170,7 +174,7 @@ DelegateSet::deleteDelegate(ApplyView& view, SLE::ref sle, beast::Journal j) if (!sleOwner) return tecINTERNAL; // LCOV_EXCL_LINE - adjustOwnerCount(view, sleOwner, -1, j); + decreaseOwnerCountForObject(view, sleOwner, sle, 1, j); view.erase(sle); diff --git a/src/libxrpl/tx/transactors/dex/AMMCreate.cpp b/src/libxrpl/tx/transactors/dex/AMMCreate.cpp index 66d059c54d..7c7d35497a 100644 --- a/src/libxrpl/tx/transactors/dex/AMMCreate.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMCreate.cpp @@ -332,17 +332,30 @@ applyCreate(ApplyContext& ctx, Sandbox& sb, AccountID const& account, beast::Jou return err; } - if (auto const err = createMPToken(sb, mptID, accountId, flags); !isTesSuccess(err)) + if (auto const err = createMPToken(sb, mptID, accountId, {}, flags); + !isTesSuccess(err)) return err; // Don't adjust AMM owner count. // It's irrelevant for pseudo-account like AMM. return accountSend( - sb, account, accountId, amount, ctx.journal, WaiveTransferFee::Yes); + sb, + account, + accountId, + amount, + ctx.journal, + {}, // don't sponsor for AMM Trustline + WaiveTransferFee::Yes); }, // Set AMM flag on AMM trustline [&](Issue const& issue) -> TER { if (auto const res = accountSend( - sb, account, accountId, amount, ctx.journal, WaiveTransferFee::Yes)) + sb, + account, + accountId, + amount, + ctx.journal, + {}, // don't sponsor for AMM Trustline + WaiveTransferFee::Yes)) return res; // Set AMM flag on AMM trustline if (!isXRP(amount)) diff --git a/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp b/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp index ff7fec019a..6a9600d060 100644 --- a/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp @@ -618,7 +618,13 @@ AMMDeposit::deposit( } auto res = accountSend( - view, accountID_, ammAccount, amountDepositActual, ctx_.journal, WaiveTransferFee::Yes); + view, + accountID_, + ammAccount, + amountDepositActual, + ctx_.journal, + {}, // don't sponsor for AMM Trustline + WaiveTransferFee::Yes); if (!isTesSuccess(res)) { JLOG(ctx_.journal.debug()) << "AMM Deposit: failed to deposit " << amountDepositActual; @@ -642,6 +648,7 @@ AMMDeposit::deposit( ammAccount, *amount2DepositActual, ctx_.journal, + {}, // don't sponsor for AMM Trustline WaiveTransferFee::Yes); if (!isTesSuccess(res)) { diff --git a/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp b/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp index 14ed5a4646..ff27a58ad7 100644 --- a/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -658,15 +659,15 @@ AMMWithdraw::withdraw( auto sleAccount = view.peek(keylet::account(account)); if (!sleAccount) return tecINTERNAL; // LCOV_EXCL_LINE - STAmount const balance = (*sleAccount)[sfBalance]; - std::uint32_t const ownerCount = sleAccount->at(sfOwnerCount); + auto const balance = (*sleAccount)[sfBalance]->xrp(); // See also TrustSet::doApply() and MPTokenAuthorize::authorize() XRPAmount const reserve( - (ownerCount < 2) ? XRPAmount(beast::kZero) - : view.fees().accountReserve(ownerCount + 1)); + (ownerCount(sleAccount, journal) < 2) + ? XRPAmount(beast::kZero) + : accountReserve(view, sleAccount, journal, {.ownerCountDelta = 1})); - auto const balanceAdj = isIssue ? std::max(priorBalance, balance.xrp()) : priorBalance; + auto const balanceAdj = isIssue ? std::max(priorBalance, balance) : priorBalance; if (balanceAdj < reserve) return tecINSUFFICIENT_RESERVE; } @@ -683,7 +684,7 @@ AMMWithdraw::withdraw( !isTesSuccess(err)) return err; - if (auto const err = checkCreateMPT(view, mptIssue, account, journal); + if (auto const err = checkCreateMPT(view, mptIssue, account, {}, journal); !isTesSuccess(err)) { return err; @@ -700,7 +701,7 @@ AMMWithdraw::withdraw( // Withdraw amountWithdraw auto res = accountSend( - view, ammAccount, account, amountWithdrawActual, journal, WaiveTransferFee::Yes); + view, ammAccount, account, amountWithdrawActual, journal, {}, WaiveTransferFee::Yes); if (!isTesSuccess(res)) { // LCOV_EXCL_START @@ -719,7 +720,7 @@ AMMWithdraw::withdraw( return {res, STAmount{}, STAmount{}, STAmount{}}; res = accountSend( - view, ammAccount, account, *amount2WithdrawActual, journal, WaiveTransferFee::Yes); + view, ammAccount, account, *amount2WithdrawActual, journal, {}, WaiveTransferFee::Yes); if (!isTesSuccess(res)) { // LCOV_EXCL_START diff --git a/src/libxrpl/tx/transactors/dex/OfferCreate.cpp b/src/libxrpl/tx/transactors/dex/OfferCreate.cpp index a915bb60d1..fb47cf0f97 100644 --- a/src/libxrpl/tx/transactors/dex/OfferCreate.cpp +++ b/src/libxrpl/tx/transactors/dex/OfferCreate.cpp @@ -833,9 +833,7 @@ OfferCreate::applyGuts(Sandbox& sb, Sandbox& sbCancel) return {tefINTERNAL, false}; { - XRPAmount const reserve = - sb.fees().accountReserve(sleCreator->getFieldU32(sfOwnerCount) + 1); - + XRPAmount const reserve = accountReserve(sb, sleCreator, viewJ, {.ownerCountDelta = 1}); if (preFeeBalance_ < reserve) { // If we are here, the signing account had an insufficient reserve @@ -869,7 +867,7 @@ OfferCreate::applyGuts(Sandbox& sb, Sandbox& sbCancel) } // Update owner count. - adjustOwnerCount(sb, sleCreator, 1, viewJ); + increaseOwnerCount(sb, sleCreator, {}, 1, viewJ); JLOG(j_.trace()) << "adding to book: " << to_string(saTakerPays.asset()) << " : " << to_string(saTakerGets.asset()) diff --git a/src/libxrpl/tx/transactors/did/DIDDelete.cpp b/src/libxrpl/tx/transactors/did/DIDDelete.cpp index 90aa21d8a1..a2af4c1100 100644 --- a/src/libxrpl/tx/transactors/did/DIDDelete.cpp +++ b/src/libxrpl/tx/transactors/did/DIDDelete.cpp @@ -50,8 +50,7 @@ DIDDelete::deleteSLE(ApplyView& view, SLE::pointer sle, AccountID const owner, b if (!sleOwner) return tecINTERNAL; // LCOV_EXCL_LINE - adjustOwnerCount(view, sleOwner, -1, j); - view.update(sleOwner); + decreaseOwnerCountForObject(view, sleOwner, sle, 1, j); // Remove object from ledger view.erase(sle); diff --git a/src/libxrpl/tx/transactors/did/DIDSet.cpp b/src/libxrpl/tx/transactors/did/DIDSet.cpp index 1392581bb0..2659460b8d 100644 --- a/src/libxrpl/tx/transactors/did/DIDSet.cpp +++ b/src/libxrpl/tx/transactors/did/DIDSet.cpp @@ -72,7 +72,8 @@ addSLE(ApplyContext& ctx, SLE::ref sle, AccountID const& owner) // Check reserve availability for new object creation { auto const balance = STAmount((*sleAccount)[sfBalance]).xrp(); - auto const reserve = ctx.view().fees().accountReserve((*sleAccount)[sfOwnerCount] + 1); + auto const reserve = + accountReserve(ctx.view(), sleAccount, ctx.journal, {.ownerCountDelta = 1}); if (balance < reserve) return tecINSUFFICIENT_RESERVE; @@ -89,7 +90,7 @@ addSLE(ApplyContext& ctx, SLE::ref sle, AccountID const& owner) return tecDIR_FULL; // LCOV_EXCL_LINE (*sle)[sfOwnerNode] = *page; } - adjustOwnerCount(ctx.view(), sleAccount, 1, ctx.journal); + increaseOwnerCount(ctx.view(), sleAccount, {}, 1, ctx.journal); ctx.view().update(sleAccount); return tesSUCCESS; diff --git a/src/libxrpl/tx/transactors/escrow/EscrowCancel.cpp b/src/libxrpl/tx/transactors/escrow/EscrowCancel.cpp index 55a9133e1e..feed43d410 100644 --- a/src/libxrpl/tx/transactors/escrow/EscrowCancel.cpp +++ b/src/libxrpl/tx/transactors/escrow/EscrowCancel.cpp @@ -181,7 +181,7 @@ EscrowCancel::doApply() if (auto const ret = std::visit( [&](T const&) { return escrowUnlockApplyHelper( - ctx_.view(), + ctx_.getApplyViewContext(), kParityRate, ctx_.view().rules().enabled(fixCleanup3_2_0) ? sle : slep, preFeeBalance_, @@ -209,8 +209,7 @@ EscrowCancel::doApply() } } - adjustOwnerCount(ctx_.view(), sle, -1, ctx_.journal); - ctx_.view().update(sle); + decreaseOwnerCountForObject(ctx_.view(), sle, slep, 1, ctx_.journal); // Remove escrow from ledger ctx_.view().erase(slep); diff --git a/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp b/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp index 531098bd1c..50f2e8b859 100644 --- a/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp +++ b/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -434,16 +435,33 @@ EscrowCreate::doApply() // Check reserve and funds availability STAmount const amount{ctx_.tx[sfAmount]}; - auto const reserve = ctx_.view().fees().accountReserve((*sle)[sfOwnerCount] + 1); - auto const balance = sle->getFieldAmount(sfBalance).xrp(); - if (balance < reserve) - return tecINSUFFICIENT_RESERVE; + // First check: whoever is on the hook for the new owner increment + // can cover it. When sponsored this hits the sponsor branch and + // validates the sponsor's reserve + remaining credit. When + // unsponsored this hits the source branch and validates the + // source's pre-lock balance against base + (currentOC+1)*increment. + if (auto const ret = + checkReserve(ctx_.getApplyViewContext(), sle, balance, {.ownerCountDelta = 1}, j_); + !isTesSuccess(ret)) + return ret; - // Check reserve and funds availability if (isXRP(amount)) { - if (balance < reserve + STAmount(amount).xrp()) + // Second check (XRP escrow only): after locking the escrowed + // amount, the source must still meet its own reserve floor. This is + // always the source's own balance against the source's own reserve — + // the sponsor's reserve was already validated above, and a sponsor + // never covers the locked funds. We compare directly (rather than via + // checkReserve) because that helper diverts to the sponsor's balance + // when a sponsor is present and would ignore the source's post-lock + // balance entirely. ownerCountDelta differs by case: + // - sponsored: 0 — sponsor covers the new owner increment, so the + // source only owes reserve for its current owners. + // - unsponsored: 1 — source owes reserve including the new increment. + auto const sourceReserve = accountReserve( + ctx_.view(), sle, j_, {.ownerCountDelta = getTxReserveSponsorID(ctx_.tx) ? 0 : 1}); + if (balance - STAmount(amount).xrp() < sourceReserve) return tecUNFUNDED; } @@ -535,7 +553,8 @@ EscrowCreate::doApply() } // increment owner count - adjustOwnerCount(ctx_.view(), sle, 1, ctx_.journal); + increaseOwnerCount(ctx_.getApplyViewContext(), sle, 1, ctx_.journal); + addSponsorToLedgerEntry(ctx_.getApplyViewContext(), slep); ctx_.view().update(sle); return tesSUCCESS; } diff --git a/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp b/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp index 7f1f9ec078..8bc98c7aa8 100644 --- a/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp +++ b/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp @@ -340,6 +340,16 @@ EscrowFinish::doApply() } } + // With the Sponsor amendment, release the escrow reserve before delivery. + // Token delivery can auto-create a destination holding, and the same + // sponsor (or the same account, for a self-escrow) may cover both the + // escrow being removed and the holding being created. Without the + // amendment, keep the legacy order: releasing early changes the reserve + // arithmetic for self-escrows and would break consensus if not gated. + bool const sponsorEnabled = ctx_.view().rules().enabled(featureSponsor); + if (sponsorEnabled) + decreaseOwnerCountForObject(ctx_.view(), account, slep, 1, ctx_.journal); + STAmount const amount = slep->getFieldAmount(sfAmount); // Transfer amount to destination if (isXRP(amount)) @@ -359,7 +369,7 @@ EscrowFinish::doApply() if (auto const ret = std::visit( [&](T const&) { return escrowUnlockApplyHelper( - ctx_.view(), + ctx_.getApplyViewContext(), lockedRate, sled, preFeeBalance_, @@ -389,10 +399,9 @@ EscrowFinish::doApply() ctx_.view().update(sled); - // Adjust source owner count - auto const sle = ctx_.view().peek(keylet::account(account)); - adjustOwnerCount(ctx_.view(), sle, -1, ctx_.journal); - ctx_.view().update(sle); + // Adjust source owner count (legacy position, pre-Sponsor) + if (!sponsorEnabled) + decreaseOwnerCountForObject(ctx_.view(), account, slep, 1, ctx_.journal); // Remove escrow from ledger ctx_.view().erase(slep); diff --git a/src/libxrpl/tx/transactors/lending/LoanBrokerCoverClawback.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverClawback.cpp index 9bb84e878e..b914f3cf24 100644 --- a/src/libxrpl/tx/transactors/lending/LoanBrokerCoverClawback.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverClawback.cpp @@ -372,7 +372,7 @@ LoanBrokerCoverClawback::doApply() associateAsset(*sleBroker, vaultAsset); // Transfer assets from pseudo-account to depositor. - return accountSend(view(), brokerPseudoID, account, clawAmount, j_, WaiveTransferFee::Yes); + return accountSend(view(), brokerPseudoID, account, clawAmount, j_, {}, WaiveTransferFee::Yes); } void diff --git a/src/libxrpl/tx/transactors/lending/LoanBrokerCoverDeposit.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverDeposit.cpp index 69b28b57af..09ab03347a 100644 --- a/src/libxrpl/tx/transactors/lending/LoanBrokerCoverDeposit.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverDeposit.cpp @@ -176,7 +176,7 @@ LoanBrokerCoverDeposit::doApply() // Transfer assets from depositor to pseudo-account. if (auto ter = - accountSend(view(), accountID_, brokerPseudoID, amount, j_, WaiveTransferFee::Yes)) + accountSend(view(), accountID_, brokerPseudoID, amount, j_, {}, WaiveTransferFee::Yes)) return ter; // Increase the LoanBroker's CoverAvailable by Amount diff --git a/src/libxrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.cpp index 008857a4ad..498f3c99eb 100644 --- a/src/libxrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.cpp @@ -211,7 +211,14 @@ LoanBrokerCoverWithdraw::doApply() associateAsset(*broker, vaultAsset); - return doWithdraw(view(), tx, accountID_, dstAcct, brokerPseudoID, preFeeBalance_, amount, j_); + return doWithdraw( + ctx_.getApplyViewContext(), + accountID_, + dstAcct, + brokerPseudoID, + preFeeBalance_, + amount, + j_); } void diff --git a/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp index 141cc2cf56..b36977d225 100644 --- a/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp @@ -155,11 +155,11 @@ LoanBrokerDelete::doApply() { auto const coverAvailable = STAmount{vaultAsset, broker->at(sfCoverAvailable)}; if (auto const ter = accountSend( - view(), brokerPseudoID, accountID_, coverAvailable, j_, WaiveTransferFee::Yes)) + view(), brokerPseudoID, accountID_, coverAvailable, j_, {}, WaiveTransferFee::Yes)) return ter; } - if (auto ter = removeEmptyHolding(view(), brokerPseudoID, vaultAsset, j_)) + if (auto ter = removeEmptyHolding(ctx_.getApplyViewContext(), brokerPseudoID, vaultAsset, j_)) return ter; auto brokerPseudoSLE = view().peek(keylet::account(brokerPseudoID)); @@ -186,8 +186,6 @@ LoanBrokerDelete::doApply() view().erase(brokerPseudoSLE); - view().erase(broker); - { auto owner = view().peek(keylet::account(accountID_)); if (!owner) @@ -195,9 +193,11 @@ LoanBrokerDelete::doApply() // Decreases the owner count by two: one for the LoanBroker object, and // one for the pseudo-account. - adjustOwnerCount(view(), owner, -2, j_); + decreaseOwnerCountForObject(view(), owner, broker, 2, j_); } + view().erase(broker); + associateAsset(*broker, vaultAsset); return tesSUCCESS; diff --git a/src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp index ab1c8f22dc..e9c153404c 100644 --- a/src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp @@ -238,9 +238,8 @@ LoanBrokerSet::doApply() // Increases the owner count by two: one for the LoanBroker object, and // one for the pseudo-account. - adjustOwnerCount(view, owner, 2, j_); - auto const ownerCount = owner->at(sfOwnerCount); - if (preFeeBalance_ < view.fees().accountReserve(ownerCount)) + increaseOwnerCount(view, owner, {}, 2, j_); + if (preFeeBalance_ < accountReserve(view, owner, j_)) return tecINSUFFICIENT_RESERVE; auto maybePseudo = createPseudoAccount(view, broker->key(), sfLoanBrokerID); @@ -249,7 +248,8 @@ LoanBrokerSet::doApply() auto& pseudo = *maybePseudo; auto pseudoId = pseudo->at(sfAccount); - if (auto ter = addEmptyHolding(view, pseudoId, preFeeBalance_, sleVault->at(sfAsset), j_)) + if (auto ter = addEmptyHolding( + ctx_.getApplyViewContext(), pseudoId, preFeeBalance_, sleVault->at(sfAsset), j_)) return ter; // Initialize data fields: diff --git a/src/libxrpl/tx/transactors/lending/LoanDelete.cpp b/src/libxrpl/tx/transactors/lending/LoanDelete.cpp index 3459119379..1a77489b4b 100644 --- a/src/libxrpl/tx/transactors/lending/LoanDelete.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanDelete.cpp @@ -107,9 +107,8 @@ LoanDelete::doApply() view.erase(loanSle); // Decrement the LoanBroker's owner count. - // The broker's owner count is solely for the number of outstanding loans, - // and is distinct from the broker's pseudo-account's owner count - adjustOwnerCount(view, brokerSle, -1, j_); + adjustLoanBrokerOwnerCount(view, brokerSle, -1, j_); + // If there are no loans left, then any remaining debt must be forgiven, // because there is no other way to pay it back. if (brokerSle->at(sfOwnerCount) == 0) @@ -129,7 +128,7 @@ LoanDelete::doApply() } } // Decrement the borrower's owner count - adjustOwnerCount(view, borrowerSle, -1, j_); + decreaseOwnerCountForObject(view, borrowerSle, loanSle, 1, j_); // These associations shouldn't do anything, but do them just to be safe associateAsset(*loanSle, vaultAsset); diff --git a/src/libxrpl/tx/transactors/lending/LoanManage.cpp b/src/libxrpl/tx/transactors/lending/LoanManage.cpp index 830eb30272..a0aa948876 100644 --- a/src/libxrpl/tx/transactors/lending/LoanManage.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanManage.cpp @@ -292,6 +292,7 @@ LoanManage::defaultLoan( vaultSle->at(sfAccount), STAmount{vaultAsset, defaultCovered}, j, + {}, WaiveTransferFee::Yes); } diff --git a/src/libxrpl/tx/transactors/lending/LoanPay.cpp b/src/libxrpl/tx/transactors/lending/LoanPay.cpp index fcda2a9fff..54ee85b186 100644 --- a/src/libxrpl/tx/transactors/lending/LoanPay.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanPay.cpp @@ -625,7 +625,11 @@ LoanPay::doApply() { // The broker may have deleted their holding. Recreate it if needed if (auto const ter = addEmptyHolding( - view, brokerPayee, brokerPayeeSle->at(sfBalance).value().xrp(), asset, j_); + ctx_.getApplyViewContext(), + brokerPayee, + brokerPayeeSle->at(sfBalance).value().xrp(), + asset, + j_); ter && ter != tecDUPLICATE) { // ignore tecDUPLICATE. That means the holding already exists, diff --git a/src/libxrpl/tx/transactors/lending/LoanSet.cpp b/src/libxrpl/tx/transactors/lending/LoanSet.cpp index 1ac387d1b1..694d01c69f 100644 --- a/src/libxrpl/tx/transactors/lending/LoanSet.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanSet.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -56,6 +57,12 @@ LoanSet::preflight(PreflightContext const& ctx) auto const& tx = ctx.tx; + if (tx.isFieldPresent(sfSponsorFlags) && isReserveSponsored(tx)) + { + JLOG(ctx.j.debug()) << "LoanSet: reserve sponsorship is not allowed."; + return temINVALID_FLAG; + } + // Special case for Batch inner transactions if (tx.isFlag(tfInnerBatchTxn) && ctx.rules.enabled(featureBatchV1_1) && !tx.isFieldPresent(sfCounterparty)) @@ -512,12 +519,12 @@ LoanSet::doApply() } } - adjustOwnerCount(view, borrowerSle, 1, j_); + increaseOwnerCount(view, borrowerSle, {}, 1, j_); + { - auto const ownerCount = borrowerSle->at(sfOwnerCount); auto const balance = accountID_ == borrower ? preFeeBalance_ : borrowerSle->at(sfBalance).value().xrp(); - if (balance < view.fees().accountReserve(ownerCount)) + if (balance < accountReserve(view, borrowerSle, j_)) return tecINSUFFICIENT_RESERVE; } @@ -531,8 +538,9 @@ LoanSet::doApply() borrower == accountID_ || borrower == counterparty, "xrpl::LoanSet::doApply", "borrower signed transaction"); + auto applyViewContext = ctx_.getApplyViewContext(); if (auto const ter = addEmptyHolding( - view, borrower, borrowerSle->at(sfBalance).value().xrp(), vaultAsset, j_); + applyViewContext, borrower, borrowerSle->at(sfBalance).value().xrp(), vaultAsset, j_); ter && ter != tecDUPLICATE) { // ignore tecDUPLICATE. That means the holding already exists, and @@ -555,7 +563,11 @@ LoanSet::doApply() "broker owner signed transaction"); if (auto const ter = addEmptyHolding( - view, brokerOwner, brokerOwnerSle->at(sfBalance).value().xrp(), vaultAsset, j_); + applyViewContext, + brokerOwner, + brokerOwnerSle->at(sfBalance).value().xrp(), + vaultAsset, + j_); ter && ter != tecDUPLICATE) { // ignore tecDUPLICATE. That means the holding already exists, @@ -631,9 +643,7 @@ LoanSet::doApply() // Update the balances in the loan broker adjustImpreciseNumber(brokerSle->at(sfDebtTotal), newDebtDelta, vaultAsset, vaultScale); - // The broker's owner count is solely for the number of outstanding loans, - // and is distinct from the broker's pseudo-account's owner count - adjustOwnerCount(view, brokerSle, 1, j_); + adjustLoanBrokerOwnerCount(view, brokerSle, 1, j_); loanSequenceProxy += 1; // The sequence should be extremely unlikely to roll over, but fail if it // does diff --git a/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp b/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp index 14bf3646c0..41bb051768 100644 --- a/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -387,8 +388,7 @@ NFTokenAcceptOffer::transferNFToken( auto const buyerOwnerCountAfter = sleBuyer->getFieldU32(sfOwnerCount); if (buyerOwnerCountAfter > buyerOwnerCountBefore) { - if (auto const reserve = view().fees().accountReserve(buyerOwnerCountAfter); - buyerBalance < reserve) + if (buyerBalance < accountReserve(view(), sleBuyer, j_)) return tecINSUFFICIENT_RESERVE; } diff --git a/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp b/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp index d8e5a7b235..5490c2de3a 100644 --- a/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -331,12 +332,14 @@ NFTokenMint::doApply() // allows NFTs to be added to the page (and burn fees) without // requiring the reserve to be met each time. The reserve is // only managed when a new NFT page or sell offer is added. - if (auto const ownerCountAfter = - view().read(keylet::account(accountID_))->getFieldU32(sfOwnerCount); + auto const sleAccount = view().read(keylet::account(accountID_)); + if (!sleAccount) + return tefINTERNAL; // LCOV_EXCL_LINE + + if (auto const ownerCountAfter = sleAccount->getFieldU32(sfOwnerCount); ownerCountAfter > ownerCountBefore) { - if (auto const reserve = view().fees().accountReserve(ownerCountAfter); - preFeeBalance_ < reserve) + if (preFeeBalance_ < accountReserve(view(), sleAccount, j_)) return tecINSUFFICIENT_RESERVE; } return tesSUCCESS; diff --git a/src/libxrpl/tx/transactors/oracle/OracleDelete.cpp b/src/libxrpl/tx/transactors/oracle/OracleDelete.cpp index 9a26816155..b4c12b9514 100644 --- a/src/libxrpl/tx/transactors/oracle/OracleDelete.cpp +++ b/src/libxrpl/tx/transactors/oracle/OracleDelete.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -13,6 +14,8 @@ #include #include +#include + namespace xrpl { NotTEC @@ -68,10 +71,8 @@ OracleDelete::deleteOracle( if (!sleOwner) return tecINTERNAL; // LCOV_EXCL_LINE - auto const count = sle->getFieldArray(sfPriceDataSeries).size() > 5 ? -2 : -1; - - adjustOwnerCount(view, sleOwner, count, j); - + std::uint32_t const count = calculateOracleReserve(sle); + decreaseOwnerCountForObject(view, sleOwner, sle, count, j); view.erase(sle); return tesSUCCESS; diff --git a/src/libxrpl/tx/transactors/oracle/OracleSet.cpp b/src/libxrpl/tx/transactors/oracle/OracleSet.cpp index 12d826e54d..ead26824e5 100644 --- a/src/libxrpl/tx/transactors/oracle/OracleSet.cpp +++ b/src/libxrpl/tx/transactors/oracle/OracleSet.cpp @@ -1,9 +1,11 @@ #include #include +#include #include #include #include +#include #include #include #include @@ -22,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -150,8 +153,9 @@ OracleSet::preclaim(PreclaimContext const& ctx) if (!pairsDel.empty()) return tecTOKEN_PAIR_NOT_FOUND; - auto const oldCount = sle->getFieldArray(sfPriceDataSeries).size() > 5 ? 2 : 1; - auto const newCount = pairs.size() > 5 ? 2 : 1; + auto const oldCount = calculateOracleReserve(sle); + auto const newCount = calculateOracleReserve(pairs); + adjustReserve = newCount - oldCount; } else @@ -160,7 +164,7 @@ OracleSet::preclaim(PreclaimContext const& ctx) if (!ctx.tx.isFieldPresent(sfProvider) || !ctx.tx.isFieldPresent(sfAssetClass)) return temMALFORMED; - adjustReserve = pairs.size() > 5 ? 2 : 1; + adjustReserve = calculateOracleReserve(pairs); } if (pairs.empty()) @@ -169,7 +173,7 @@ OracleSet::preclaim(PreclaimContext const& ctx) return tecARRAY_TOO_LARGE; auto const reserve = - ctx.view.fees().accountReserve(sleSetter->getFieldU32(sfOwnerCount) + adjustReserve); + accountReserve(ctx.view, sleSetter, ctx.j, {.ownerCountDelta = adjustReserve}); auto const& balance = sleSetter->getFieldAmount(sfBalance); if (balance < reserve) @@ -179,11 +183,22 @@ OracleSet::preclaim(PreclaimContext const& ctx) } static bool -adjustOwnerCount(ApplyContext& ctx, int count) +adjustOracleOwnerCount(ApplyContext& ctx, int count) { + XRPL_ASSERT(std::abs(count) <= 2, "xrpl::adjustOracleOwnerCount abs(counter) <= 2"); + if (auto const sleAccount = ctx.view().peek(keylet::account(ctx.tx[sfAccount]))) { - adjustOwnerCount(ctx.view(), sleAccount, count, ctx.journal); + if (count > 0) + { + increaseOwnerCount( + ctx.view(), sleAccount, {}, static_cast(count), ctx.journal); + } + else if (count < 0) + { + decreaseOwnerCount( + ctx.view(), sleAccount, {}, static_cast(-count), ctx.journal); + } return true; } @@ -228,7 +243,7 @@ OracleSet::doApply() priceData.setFieldCurrency(sfQuoteAsset, entry.getFieldCurrency(sfQuoteAsset)); pairs.emplace(tokenPairKey(entry), std::move(priceData)); } - auto const oldCount = pairs.size() > 5 ? 2 : 1; + auto const oldCount = calculateOracleReserve(pairs); // update/add/delete pairs for (auto const& entry : ctx_.tx.getFieldArray(sfPriceDataSeries)) { @@ -266,9 +281,10 @@ OracleSet::doApply() (*sle)[sfOracleDocumentID] = ctx_.tx[sfOracleDocumentID]; } - auto const newCount = pairs.size() > 5 ? 2 : 1; - auto const adjust = newCount - oldCount; - if (adjust != 0 && !adjustOwnerCount(ctx_, adjust)) + auto const newCount = calculateOracleReserve(pairs); + int32_t const adjust = newCount - oldCount; + + if (adjust != 0 && !adjustOracleOwnerCount(ctx_, adjust)) return tefINTERNAL; // LCOV_EXCL_LINE ctx_.view().update(sle); @@ -317,8 +333,8 @@ OracleSet::doApply() (*sle)[sfOwnerNode] = *page; - auto const count = series.size() > 5 ? 2 : 1; - if (!adjustOwnerCount(ctx_, count)) + auto const count = calculateOracleReserve(series); + if (!adjustOracleOwnerCount(ctx_, count)) return tefINTERNAL; // LCOV_EXCL_LINE ctx_.view().insert(sle); diff --git a/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp b/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp index 7e950dc743..d3e2af86ef 100644 --- a/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp +++ b/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp @@ -7,13 +7,13 @@ #include #include #include +#include #include #include #include #include #include #include -#include #include #include #include @@ -151,6 +151,7 @@ DepositPreauth::preclaim(PreclaimContext const& ctx) TER DepositPreauth::doApply() { + auto applyViewContext = ctx_.getApplyViewContext(); if (ctx_.tx.isFieldPresent(sfAuthorize)) { auto const sleOwner = view().peek(keylet::account(accountID_)); @@ -160,13 +161,10 @@ DepositPreauth::doApply() // A preauth counts against the reserve of the issuing account, but we // check the starting balance because we want to allow dipping into the // reserve to pay fees. - { - STAmount const reserve{ - view().fees().accountReserve(sleOwner->getFieldU32(sfOwnerCount) + 1)}; - - if (preFeeBalance_ < reserve) - return tecINSUFFICIENT_RESERVE; - } + if (auto const ret = checkReserve( + applyViewContext, sleOwner, preFeeBalance_, {.ownerCountDelta = 1}, j_); + !isTesSuccess(ret)) + return ret; // Preclaim already verified that the Preauth entry does not yet exist. // Create and populate the Preauth entry. @@ -190,7 +188,8 @@ DepositPreauth::doApply() slePreauth->setFieldU64(sfOwnerNode, *page); // If we succeeded, the new entry counts against the creator's reserve. - adjustOwnerCount(view(), sleOwner, 1, j_); + increaseOwnerCount(applyViewContext, sleOwner, 1, j_); + addSponsorToLedgerEntry(applyViewContext, slePreauth); } else if (ctx_.tx.isFieldPresent(sfUnauthorize)) { @@ -207,13 +206,10 @@ DepositPreauth::doApply() // A preauth counts against the reserve of the issuing account, but we // check the starting balance because we want to allow dipping into the // reserve to pay fees. - { - STAmount const reserve{ - view().fees().accountReserve(sleOwner->getFieldU32(sfOwnerCount) + 1)}; - - if (preFeeBalance_ < reserve) - return tecINSUFFICIENT_RESERVE; - } + if (auto const ret = checkReserve( + applyViewContext, sleOwner, preFeeBalance_, {.ownerCountDelta = 1}, j_); + !isTesSuccess(ret)) + return ret; // Preclaim already verified that the Preauth entry does not yet exist. // Create and populate the Preauth entry. @@ -251,7 +247,8 @@ DepositPreauth::doApply() slePreauth->setFieldU64(sfOwnerNode, *page); // If we succeeded, the new entry counts against the creator's reserve. - adjustOwnerCount(view(), sleOwner, 1, j_); + increaseOwnerCount(applyViewContext, sleOwner, 1, j_); + addSponsorToLedgerEntry(applyViewContext, slePreauth); } else if (ctx_.tx.isFieldPresent(sfUnauthorizeCredentials)) { @@ -289,8 +286,7 @@ DepositPreauth::removeFromLedger(ApplyView& view, uint256 const& preauthIndex, b if (!sleOwner) return tefINTERNAL; // LCOV_EXCL_LINE - adjustOwnerCount(view, sleOwner, -1, j); - + decreaseOwnerCountForObject(view, sleOwner, slePreauth, 1, j); // Remove DepositPreauth from ledger. view.erase(slePreauth); diff --git a/src/libxrpl/tx/transactors/payment/Payment.cpp b/src/libxrpl/tx/transactors/payment/Payment.cpp index 6e3883572a..17c96a1919 100644 --- a/src/libxrpl/tx/transactors/payment/Payment.cpp +++ b/src/libxrpl/tx/transactors/payment/Payment.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -37,6 +38,7 @@ #include #include +#include #include #include #include @@ -120,6 +122,21 @@ Payment::preflight(PreflightContext const& ctx) if (!ctx.rules.enabled(featureMPTokensV1) && isDstMPT) return temDISABLED; + if (tx.isFlag(tfSponsorCreatedAccount)) + { + if (!ctx.rules.enabled(featureSponsor)) + return temDISABLED; + + if (tx.isFlag(tfNoRippleDirect) || tx.isFlag(tfPartialPayment) || tx.isFlag(tfLimitQuality)) + return temINVALID_FLAG; + + if (tx.isFieldPresent(sfSendMax) || tx.isFieldPresent(sfPaths)) + return temINVALID; + + if (!dstAmount.native()) + return temBAD_AMOUNT; + } + if (!mpTokensV2 && isDstMPT && ctx.tx.isFieldPresent(sfPaths)) return temMALFORMED; @@ -383,15 +400,28 @@ Payment::preclaim(PreclaimContext const& ctx) { // accountReserve is the minimum amount that an account can have. // Reserve is not scaled by load. - JLOG(ctx.j.trace()) << "Delay transaction: Destination account does not exist. " - << "Insufficent payment to create account."; + if (!ctx.tx.isFlag(tfSponsorCreatedAccount)) + { + // The minimum amount when creating a Sponsored Account is 1 drop. + // Since the reserve is covered by the sponsor, you don't need to hold the + // 1-increment reserve yourself. + JLOG(ctx.j.trace()) << "Delay transaction: Destination account does not exist. " + << "Insufficient payment to create account."; - // TODO: de-dupe - // Another transaction could create the account and then this - // transaction would succeed. - return tecNO_DST_INSUF_XRP; + // TODO: de-dupe + // Another transaction could create the account and then this + // transaction would succeed. + return tecNO_DST_INSUF_XRP; + } } } + else if (ctx.tx.isFlag(tfSponsorCreatedAccount)) + { + // The tfSponsorCreatedAccount flag is specific to account creation via + // sponsorship. If the destination account already exists, applying this + // flag is invalid. + return tecNO_SPONSOR_PERMISSION; + } else if (sleDst->isFlag(lsfRequireDestTag) && !ctx.tx.isFieldPresent(sfDestinationTag)) { // The tag is basically account-specific information we don't @@ -470,6 +500,27 @@ Payment::doApply() sleDst->setFieldU32(sfSequence, view().seq()); sleDst->setFieldAmount(sfBalance, XRPAmount(beast::kZero)); + if (ctx_.tx.isFlag(tfSponsorCreatedAccount)) + { + auto const sponsor = view().peek(keylet::account(accountID_)); + if (!sponsor) + return tefINTERNAL; // LCOV_EXCL_LINE + auto const currentSponsoringAccountCount = + sponsor->getFieldU32(sfSponsoringAccountCount); + if (currentSponsoringAccountCount == std::numeric_limits::max()) + { + // LCOV_EXCL_START + JLOG(j_.fatal()) << "Sponsoring account count overflow for account " + << to_string(accountID_); + return tecINTERNAL; + // LCOV_EXCL_STOP + } + sponsor->setFieldU32(sfSponsoringAccountCount, currentSponsoringAccountCount + 1); + + addSponsorToLedgerEntry(sleDst, sponsor); + view().update(sponsor); + } + view().insert(sleDst); } else @@ -636,16 +687,12 @@ Payment::doApply() if (!sleSrc) return tefINTERNAL; // LCOV_EXCL_LINE - // ownerCount is the number of entries in this ledger for this - // account that require a reserve. - auto const ownerCount = sleSrc->getFieldU32(sfOwnerCount); + // the number of reserves in this ledger for this account that require a + // reserve. + auto const reserve = accountReserve(view(), sleSrc, j_); - // This is the total reserve in drops. - auto const reserve = view().fees().accountReserve(ownerCount); - - // In a delegated payment, the fee payer is the delegated account, - // not the source account (accountID_). - bool const accountIsPayer = (ctx_.tx.getFeePayer() == accountID_); + // In a delegated / fee sponsored payment, the fee payer is not the source account (accountID_). + bool const accountIsPayer = ctx_.tx.getFeePayerID() == accountID_; // preFeeBalance_ is the balance on the source account (accountID_) BEFORE the fees // were charged. If source account is the fee payer, it must also cover the fee. diff --git a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelCreate.cpp b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelCreate.cpp index 977da56861..b17430948a 100644 --- a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelCreate.cpp +++ b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelCreate.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -77,9 +78,10 @@ PaymentChannelCreate::preclaim(PreclaimContext const& ctx) return terNO_ACCOUNT; // Check reserve and funds availability + if (!ctx.view.rules().enabled(featureSponsor)) { auto const balance = (*sle)[sfBalance]; - auto const reserve = ctx.view.fees().accountReserve((*sle)[sfOwnerCount] + 1); + auto const reserve = ctx.view.fees().accountReserve((*sle)[sfOwnerCount] + 1, 1); if (balance < reserve) return tecINSUFFICIENT_RESERVE; @@ -131,6 +133,36 @@ PaymentChannelCreate::doApply() return tecEXPIRED; } + if (ctx_.view().rules().enabled(featureSponsor)) + { + // First check: whoever is on the hook for the new owner increment + // can cover it. When sponsored this hits the sponsor branch and + // validates the sponsor's reserve + remaining credit. When + // unsponsored this hits the source branch and validates the + // source's pre-lock balance against base + (currentOC+1)*increment. + if (auto const ret = checkReserve( + ctx_.getApplyViewContext(), sle, preFeeBalance_, {.ownerCountDelta = 1}, j_); + !isTesSuccess(ret)) + return ret; + + // Second check: after locking sfAmount in the channel, the source + // must still meet its own reserve floor. This is always the + // source's own balance against the source's own reserve — the + // sponsor's reserve was already validated above, and a sponsor + // never covers the locked funds. We compare directly (rather than + // via checkReserve) because that helper diverts to the + // sponsor's balance when a sponsor is present and would ignore the + // source's post-lock balance entirely. ownerCountDelta differs by + // case: + // - sponsored: 0 — sponsor covers the new owner increment, so + // the source only owes reserve for its current owners. + // - unsponsored: 1 — source owes reserve including the new increment. + auto const sourceReserve = accountReserve( + ctx_.view(), sle, j_, {.ownerCountDelta = getTxReserveSponsorID(ctx_.tx) ? 0 : 1}); + if (preFeeBalance_ - ctx_.tx[sfAmount].xrp() < sourceReserve) + return tecUNFUNDED; + } + auto const dst = ctx_.tx[sfDestination]; // Create PayChan in ledger. @@ -178,7 +210,8 @@ PaymentChannelCreate::doApply() // Deduct owner's balance, increment owner count (*sle)[sfBalance] = (*sle)[sfBalance] - ctx_.tx[sfAmount]; - adjustOwnerCount(ctx_.view(), sle, 1, ctx_.journal); + increaseOwnerCount(ctx_.getApplyViewContext(), sle, 1, ctx_.journal); + addSponsorToLedgerEntry(ctx_.getApplyViewContext(), slep); ctx_.view().update(sle); return tesSUCCESS; diff --git a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp index 4e3c5dd638..1d1cf07e25 100644 --- a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp +++ b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -87,13 +88,18 @@ PaymentChannelFund::doApply() { // Check reserve and funds availability - auto const balance = (*sle)[sfBalance]; - auto const reserve = ctx_.view().fees().accountReserve((*sle)[sfOwnerCount]); + STAmount const balance = (*sle)[sfBalance]; + if (auto const ret = checkReserve(ctx_.getApplyViewContext(), sle, balance.xrp(), {}, j_); + !isTesSuccess(ret)) + return ret; - if (balance < reserve) - return tecINSUFFICIENT_RESERVE; - - if (balance < reserve + ctx_.tx[sfAmount]) + // After locking sfAmount in the channel, the source must still meet + // its own reserve floor. We compare directly (rather than via + // checkReserve) because that helper diverts to the sponsor's balance + // when a sponsor is present and would ignore the source's post-lock + // balance entirely. Funding an existing channel adds no owned object, + // so there is no owner-count delta. + if (balance < accountReserve(ctx_.view(), sle, j_) + ctx_.tx[sfAmount]) return tecUNFUNDED; } diff --git a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp index 7eb3f282b9..9978d3e260 100644 --- a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp +++ b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp @@ -65,7 +65,7 @@ PermissionedDomainDelete::doApply() XRPL_ASSERT( ownerSle && ownerSle->getFieldU32(sfOwnerCount) > 0, "xrpl::PermissionedDomainDelete::doApply : nonzero owner count"); - adjustOwnerCount(view(), ownerSle, -1, ctx_.journal); + decreaseOwnerCountForObject(view(), ownerSle, slePd, 1, ctx_.journal); view().erase(slePd); return tesSUCCESS; diff --git a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp index 0e71ceada1..c2df114e48 100644 --- a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp +++ b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp @@ -106,7 +106,8 @@ PermissionedDomainSet::doApply() // Create new permissioned domain. // Check reserve availability for new object creation auto const balance = STAmount((*ownerSle)[sfBalance]).xrp(); - auto const reserve = ctx_.view().fees().accountReserve((*ownerSle)[sfOwnerCount] + 1); + auto const reserve = + accountReserve(ctx_.view(), ownerSle, ctx_.journal, {.ownerCountDelta = 1}); if (balance < reserve) return tecINSUFFICIENT_RESERVE; @@ -125,7 +126,7 @@ PermissionedDomainSet::doApply() slePd->setFieldU64(sfOwnerNode, *page); // If we succeeded, the new entry counts against the creator's reserve. - adjustOwnerCount(view(), ownerSle, 1, ctx_.journal); + increaseOwnerCount(view(), ownerSle, {}, 1, ctx_.journal); view().insert(slePd); } diff --git a/src/libxrpl/tx/transactors/sponsor/SponsorshipSet.cpp b/src/libxrpl/tx/transactors/sponsor/SponsorshipSet.cpp new file mode 100644 index 0000000000..2b6ab8cf15 --- /dev/null +++ b/src/libxrpl/tx/transactors/sponsor/SponsorshipSet.cpp @@ -0,0 +1,420 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace xrpl { + +static bool +hasSponsorshipBudget( + SLE::const_ref sponsorshipSle, + std::optional const& feeAmount, + std::optional const& remainingOwnerCount) +{ + // A field the transaction omits keeps whatever the existing object holds, + // so fall back to the current SLE value when the tx does not set it. + bool const hasFeeAmount = feeAmount + ? *feeAmount > beast::kZero + : sponsorshipSle && (*sponsorshipSle)[~sfFeeAmount].value_or(STAmount{0}) > beast::kZero; + + bool const hasRemainingOwnerCount = remainingOwnerCount + ? *remainingOwnerCount > 0 + : sponsorshipSle && (*sponsorshipSle)[~sfRemainingOwnerCount].value_or(0) > 0; + + return hasFeeAmount || hasRemainingOwnerCount; +} + +TxConsequences +SponsorshipSet::makeTxConsequences(PreflightContext const& ctx) +{ + auto const feeAmount = ctx.tx[~sfFeeAmount]; + return TxConsequences{ctx.tx, feeAmount.has_value() ? feeAmount->xrp() : beast::kZero}; +} + +std::uint32_t +SponsorshipSet::getFlagsMask(PreflightContext const& ctx) +{ + return tfSponsorshipSetMask; +} + +NotTEC +SponsorshipSet::preflight(PreflightContext const& ctx) +{ + if (ctx.tx.isFlag(tfSponsorshipSetRequireSignForFee) && + ctx.tx.isFlag(tfSponsorshipClearRequireSignForFee)) + return temINVALID_FLAG; + if (ctx.tx.isFlag(tfSponsorshipSetRequireSignForReserve) && + ctx.tx.isFlag(tfSponsorshipClearRequireSignForReserve)) + return temINVALID_FLAG; + + auto const account = ctx.tx.getAccountID(sfAccount); + bool const hasSponsor = ctx.tx.isFieldPresent(sfCounterpartySponsor); + bool const hasSponsee = ctx.tx.isFieldPresent(sfSponsee); + + // The transaction must specify either Sponsor or Sponsee, but not both. + if (hasSponsor == hasSponsee) + return temMALFORMED; + + auto const sponsorID = ctx.tx[~sfCounterpartySponsor].value_or(account); + auto const sponseeID = ctx.tx[~sfSponsee].value_or(account); + + if (sponsorID == sponseeID) + return temMALFORMED; + + if (ctx.tx.isFlag(tfDeleteObject)) + { + // Transactions deleting `Sponsorship` cannot set modification flags. + constexpr std::uint32_t kModifyFlags = tfSponsorshipSetRequireSignForFee | + tfSponsorshipSetRequireSignForReserve | tfSponsorshipClearRequireSignForFee | + tfSponsorshipClearRequireSignForReserve; + + if ((ctx.tx.getFlags() & kModifyFlags) != 0u) + return temINVALID_FLAG; + + // Transactions deleting `Sponsorship` cannot include modification fields. + if (ctx.tx.isFieldPresent(sfFeeAmount) || ctx.tx.isFieldPresent(sfRemainingOwnerCount) || + ctx.tx.isFieldPresent(sfMaxFee)) + return temMALFORMED; + } + else + { + // Both sponsor and sponsee can delete a Sponsorship object, but only + // the sponsor can create or update one. + if (account != sponsorID) + return temMALFORMED; + + // FeeAmount and MaxFee must be non-negative XRP amounts when present. + auto const checkOptionalAmountField = [&](SField const& field) -> NotTEC { + if (!ctx.tx.isFieldPresent(field)) + return tesSUCCESS; + + auto const amount = ctx.tx.getFieldAmount(field); + + if (!isXRP(amount)) + return temBAD_AMOUNT; + + if (amount.xrp() < beast::kZero) + return temBAD_AMOUNT; + + return tesSUCCESS; + }; + + if (auto const ret = checkOptionalAmountField(sfFeeAmount); !isTesSuccess(ret)) + return ret; + + if (auto const ret = checkOptionalAmountField(sfMaxFee); !isTesSuccess(ret)) + return ret; + } + + return tesSUCCESS; +} + +TER +SponsorshipSet::preclaim(PreclaimContext const& ctx) +{ + auto const sponsorID = ctx.tx[~sfCounterpartySponsor].value_or(ctx.tx[sfAccount]); + auto const sponseeID = ctx.tx[~sfSponsee].value_or(ctx.tx[sfAccount]); + + if (sponseeID == sponsorID) + return tecINTERNAL; // LCOV_EXCL_LINE + + auto const sponsorAccSle = ctx.view.read(keylet::account(sponsorID)); + if (!sponsorAccSle) + return tecNO_DST; + + auto const sponseeSle = ctx.view.read(keylet::account(sponseeID)); + if (!sponseeSle) + return tecNO_DST; + + // Pseudo-accounts cannot participate in sponsorship. + if (isPseudoAccount(sponsorAccSle) || isPseudoAccount(sponseeSle)) + return tecNO_PERMISSION; + + auto const sponsorshipSle = ctx.view.read(keylet::sponsorship(sponsorID, sponseeID)); + + // Deleting a Sponsorship object requires the object to already exist. + if (ctx.tx.isFlag(tfDeleteObject) && !sponsorshipSle) + return tecNO_ENTRY; + + // Reject creating or updating a Sponsorship that would be left with no + // budget (neither a positive FeeAmount nor a positive RemainingOwnerCount). + // Such an object is unusable yet still consumes the sponsor's reserve. + if (!ctx.tx.isFlag(tfDeleteObject) && + !hasSponsorshipBudget(sponsorshipSle, ctx.tx[~sfFeeAmount], ctx.tx[~sfRemainingOwnerCount])) + return tecNO_PERMISSION; + + return tesSUCCESS; +} + +static TER +deleteSponsorship(ApplyView& view, SLE::ref sle, beast::Journal j) +{ + if (!sle) + return tecINTERNAL; // LCOV_EXCL_LINE + + auto const sponsorID = (*sle)[sfOwner]; + auto const sponseeID = (*sle)[sfSponsee]; + + // The sponsor owns the Sponsorship object, so deletion releases the + // sponsor's owner reserve. + auto sponsorAccSle = view.peek(keylet::account(sponsorID)); + if (!sponsorAccSle) + return tecINTERNAL; // LCOV_EXCL_LINE + + if (!view.dirRemove(keylet::ownerDir(sponsorID), (*sle)[sfOwnerNode], sle->key(), false)) + { + // LCOV_EXCL_START + JLOG(j.fatal()) << "Unable to delete Sponsorship from sponsor."; + return tefBAD_LEDGER; + // LCOV_EXCL_STOP + } + if (!view.dirRemove(keylet::ownerDir(sponseeID), (*sle)[sfSponseeNode], sle->key(), false)) + { + // LCOV_EXCL_START + JLOG(j.fatal()) << "Unable to delete Sponsorship from sponsee."; + return tefBAD_LEDGER; + // LCOV_EXCL_STOP + } + + decreaseOwnerCountForObject(view, sponsorAccSle, sle, 1, j); + + // Return any prefunded fee amount to the sponsor before erasing the object. + if (sle->isFieldPresent(sfFeeAmount)) + { + (*sponsorAccSle)[sfBalance] += sle->getFieldAmount(sfFeeAmount); + view.update(sponsorAccSle); + } + + view.erase(sle); + + return tesSUCCESS; +} + +TER +SponsorshipSet::doApply() +{ + auto const sponsorID = ctx_.tx[~sfCounterpartySponsor].value_or(accountID_); + auto const sponseeID = ctx_.tx[~sfSponsee].value_or(accountID_); + + if (sponseeID == sponsorID) + return tecINTERNAL; // LCOV_EXCL_LINE + + auto const sponsorAccSle = ctx_.view().peek(keylet::account(sponsorID)); + if (!sponsorAccSle) + return tecINTERNAL; // LCOV_EXCL_LINE + + if (!ctx_.view().exists(keylet::account(sponseeID))) + return tecINTERNAL; // LCOV_EXCL_LINE + + auto const sponsorKeylet = keylet::sponsorship(sponsorID, sponseeID); + auto const sponsorshipSle = ctx_.view().peek(sponsorKeylet); + + if (ctx_.tx.isFlag(tfDeleteObject)) + { + if (!sponsorshipSle) + return tecINTERNAL; // LCOV_EXCL_LINE + + return deleteSponsorship(ctx_.view(), sponsorshipSle, ctx_.journal); + } + + auto const feeAmount = ctx_.tx[~sfFeeAmount]; + auto const maxFee = ctx_.tx[~sfMaxFee]; + auto const remainingOwnerCount = ctx_.tx[~sfRemainingOwnerCount]; + + bool const hasPositiveFeeAmount = feeAmount.has_value() && *feeAmount > beast::kZero; + + auto reserveSponsorAccSle = getTxReserveSponsor(ctx_.getApplyViewContext()); + if (!reserveSponsorAccSle) + return reserveSponsorAccSle.error(); // LCOV_EXCL_LINE + + if (!sponsorshipSle) + { + // Create a new Sponsorship object between the sponsor and sponsee. + auto newSle = std::make_shared(sponsorKeylet); + + (*newSle)[sfOwner] = sponsorID; + (*newSle)[sfSponsee] = sponseeID; + if (feeAmount && (*feeAmount).xrp() > (*sponsorAccSle)[sfBalance]) + return tecUNFUNDED; + + STAmount sponsorBalanceAfterFee = (*sponsorAccSle)[sfBalance]; + if (hasPositiveFeeAmount) + sponsorBalanceAfterFee -= *feeAmount; + + if (auto const ret = checkReserve( + ctx_.getApplyViewContext(), + sponsorAccSle, + sponsorBalanceAfterFee.xrp(), + *reserveSponsorAccSle, + {.ownerCountDelta = 1}, + ctx_.journal, + tecUNFUNDED); + !isTesSuccess(ret)) + { + return ret; + } + + if (hasPositiveFeeAmount) + { + // New object: FeeAmount starts absent, so deduct and record the full amount + (*newSle)[sfFeeAmount] = *feeAmount; + (*sponsorAccSle)[sfBalance] -= *feeAmount; + } + + if (maxFee && *maxFee > beast::kZero) + (*newSle)[sfMaxFee] = *maxFee; + if (remainingOwnerCount && *remainingOwnerCount > 0) + (*newSle)[sfRemainingOwnerCount] = *remainingOwnerCount; + + std::uint32_t flags = 0; + if (ctx_.tx.isFlag(tfSponsorshipSetRequireSignForFee)) + flags |= lsfSponsorshipRequireSignForFee; + + if (ctx_.tx.isFlag(tfSponsorshipSetRequireSignForReserve)) + flags |= lsfSponsorshipRequireSignForReserve; + + (*newSle)[sfFlags] = flags; + + auto const sponsorPage = view().dirInsert( + keylet::ownerDir(sponsorID), sponsorKeylet, describeOwnerDir(sponsorID)); + if (!sponsorPage) + return tecDIR_FULL; // LCOV_EXCL_LINE + (*newSle)[sfOwnerNode] = *sponsorPage; + + auto const sponseePage = view().dirInsert( + keylet::ownerDir(sponseeID), sponsorKeylet, describeOwnerDir(sponseeID)); + if (!sponseePage) + return tecDIR_FULL; // LCOV_EXCL_LINE + (*newSle)[sfSponseeNode] = *sponseePage; + + // NOLINTNEXTLINE(readability-suspicious-call-argument) + increaseOwnerCount(view(), sponsorAccSle, *reserveSponsorAccSle, 1, ctx_.journal); + addSponsorToLedgerEntry(newSle, *reserveSponsorAccSle); + + ctx_.view().insert(newSle); + return tesSUCCESS; + } + + // Update the existing Sponsorship object. + if (feeAmount) + { + auto const currentFeeAmount = (*sponsorshipSle)[~sfFeeAmount].valueOr(XRPAmount{0}); + auto const feeAmountDelta = XRPAmount(*feeAmount - currentFeeAmount); + + if (feeAmountDelta > beast::kZero && feeAmountDelta > (*sponsorAccSle)[sfBalance]) + return tecUNFUNDED; + + // Move the FeeAmount delta between the sponsor balance and Sponsorship + // object. + if (feeAmountDelta != beast::kZero) + { + STAmount sponsorBalanceAfterFee = (*sponsorAccSle)[sfBalance]; + sponsorBalanceAfterFee -= feeAmountDelta; + + if (auto const ret = checkReserve( + ctx_.getApplyViewContext(), + sponsorAccSle, + sponsorBalanceAfterFee.xrp(), + *reserveSponsorAccSle, + {}, + ctx_.journal, + tecUNFUNDED); + !isTesSuccess(ret)) + { + return ret; + } + + (*sponsorAccSle)[sfBalance] -= feeAmountDelta; + if (*feeAmount == beast::kZero) + { + (*sponsorshipSle).makeFieldAbsent(sfFeeAmount); + } + else + { + (*sponsorshipSle).setFieldAmount(sfFeeAmount, *feeAmount); + } + ctx_.view().update(sponsorAccSle); + } + } + + if (maxFee) + { + if (*maxFee == beast::kZero) + { + (*sponsorshipSle).makeFieldAbsent(sfMaxFee); + } + else + { + (*sponsorshipSle)[sfMaxFee] = *maxFee; + } + } + + if (remainingOwnerCount) + { + if (*remainingOwnerCount == 0) + { + sponsorshipSle->makeFieldAbsent(sfRemainingOwnerCount); + } + else + { + sponsorshipSle->at(sfRemainingOwnerCount) = *remainingOwnerCount; + } + } + + // Apply requested flag changes. + auto flags = sponsorshipSle->getFieldU32(sfFlags); + if (ctx_.tx.isFlag(tfSponsorshipSetRequireSignForFee)) + flags |= lsfSponsorshipRequireSignForFee; + + if (ctx_.tx.isFlag(tfSponsorshipClearRequireSignForFee)) + flags &= ~lsfSponsorshipRequireSignForFee; + + if (ctx_.tx.isFlag(tfSponsorshipSetRequireSignForReserve)) + flags |= lsfSponsorshipRequireSignForReserve; + + if (ctx_.tx.isFlag(tfSponsorshipClearRequireSignForReserve)) + flags &= ~lsfSponsorshipRequireSignForReserve; + + if (flags != (*sponsorshipSle)[sfFlags]) + (*sponsorshipSle)[sfFlags] = flags; + + view().update(sponsorshipSle); + + return tesSUCCESS; +} + +void +SponsorshipSet::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) +{ +} + +bool +SponsorshipSet::finalizeInvariants( + STTx const&, + TER, + XRPAmount, + ReadView const&, + beast::Journal const&) +{ + return true; +} + +} // namespace xrpl diff --git a/src/libxrpl/tx/transactors/sponsor/SponsorshipTransfer.cpp b/src/libxrpl/tx/transactors/sponsor/SponsorshipTransfer.cpp new file mode 100644 index 0000000000..0e036649fd --- /dev/null +++ b/src/libxrpl/tx/transactors/sponsor/SponsorshipTransfer.cpp @@ -0,0 +1,542 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace xrpl { + +// Increment an uint32 sponsor count field and update the SLE. +static TER +incrementSponsorCount( + ApplyView& view, + SLE::ref sle, + SF_UINT32 const& field, + std::uint32_t const delta) +{ + auto const currentValue = sle->getFieldU32(field); + if (std::numeric_limits::max() - currentValue < delta) + { + // LCOV_EXCL_START + UNREACHABLE("xrpl::incrementSponsorCount : sponsor field overflow"); + return tecINTERNAL; + // LCOV_EXCL_STOP + } + + sle->at(field) = currentValue + delta; + view.update(sle); + return tesSUCCESS; +} + +// Decrement an uint32 sponsor count field and update the SLE. +static TER +decrementSponsorCount( + ApplyView& view, + SLE::ref sle, + SF_UINT32 const& field, + std::uint32_t const delta) +{ + auto const currentValue = sle->getFieldU32(field); + if (currentValue < delta) + { + // LCOV_EXCL_START + UNREACHABLE("xrpl::decrementSponsorCount : sponsor field underflow"); + return tecINTERNAL; + // LCOV_EXCL_STOP + } + + sle->at(field) = currentValue - delta; + view.update(sle); + return tesSUCCESS; +} + +// Consume the sponsor's pre-funded reserve budget and lowers the Sponsorship +// object's RemainingOwnerCount. +static TER +decrementPrefundedReserveCount(ApplyView& view, SLE::ref sponsorshipSle, std::uint32_t const delta) +{ + if (delta == 0) + return tesSUCCESS; // LCOV_EXCL_LINE + + auto const currentReserveCount = sponsorshipSle->getFieldU32(sfRemainingOwnerCount); + if (currentReserveCount < delta) + { + // LCOV_EXCL_START + // Already verified by checkReserve (sufficient RemainingOwnerCount) + UNREACHABLE("xrpl::decrementPrefundedReserveCount : invalid reserve count"); + return tefINTERNAL; + // LCOV_EXCL_STOP + } + + sponsorshipSle->at(sfRemainingOwnerCount) = currentReserveCount - delta; + view.update(sponsorshipSle); + return tesSUCCESS; +} + +std::uint32_t +SponsorshipTransfer::getFlagsMask(PreflightContext const& ctx) +{ + return tfSponsorshipTransferMask; +} + +NotTEC +SponsorshipTransfer::preflight(PreflightContext const& ctx) +{ + static constexpr auto transferFlags = + tfSponsorshipCreate | tfSponsorshipReassign | tfSponsorshipEnd; + if (std::popcount(ctx.tx.getFlags() & transferFlags) != 1) + { + JLOG(ctx.j.debug()) << "preflight: Only one SponsorshipTransfer flag can be set per tx."; + return temINVALID_FLAG; + } + + if (ctx.tx.isFlag(tfSponsorshipCreate)) + { + // Creating sponsorship transfers an unsponsored target from the sponsee + // to a reserve sponsor identified by sfSponsor + spfSponsorReserve. + if (!ctx.tx.isFieldPresent(sfSponsor)) + { + JLOG(ctx.j.debug()) << "preflight: sfSponsor must be present when creating sponsorship"; + return temMALFORMED; + } + + if (!isReserveSponsored(ctx.tx)) + { + JLOG(ctx.j.debug()) + << "preflight: spfSponsorReserve must be set when creating sponsorship"; + return temINVALID_FLAG; + } + + if (ctx.tx.isFieldPresent(sfSponsee)) + { + JLOG(ctx.j.debug()) + << "preflight: sfSponsee must not be present when creating sponsorship"; + return temMALFORMED; + } + } + + if (ctx.tx.isFlag(tfSponsorshipReassign)) + { + // Reassigning sponsorship transfers an already sponsored target from its + // current reserve sponsor to the new sponsor identified by sfSponsor + + // spfSponsorReserve. + if (!ctx.tx.isFieldPresent(sfSponsor)) + { + JLOG(ctx.j.debug()) + << "preflight: sfSponsor must be present when reassigning sponsorship"; + return temMALFORMED; + } + + if (!isReserveSponsored(ctx.tx)) + { + JLOG(ctx.j.debug()) + << "preflight: spfSponsorReserve must be set when reassigning sponsorship"; + return temINVALID_FLAG; + } + if (ctx.tx.isFieldPresent(sfSponsee)) + { + JLOG(ctx.j.debug()) + << "preflight: sfSponsee must not be present when reassigning sponsorship"; + return temMALFORMED; + } + } + + if (ctx.tx.isFlag(tfSponsorshipEnd)) + { + // Ending sponsorship removes reserve sponsorship from a sponsored target. + // The target is sfSponsee when provided; otherwise it is sfAccount. + if (ctx.tx.isFieldPresent(sfSponsor)) + { + JLOG(ctx.j.debug()) + << "preflight: sfSponsor must not be present when ending sponsorship"; + return temMALFORMED; + } + + // sfSponsorFlags should not be present if it is ending sponsorship. + if (ctx.tx.isFieldPresent(sfSponsorFlags)) + { + // Unreachable: reaching here means sfSponsor is absent, which is already checked above, + // and preflight1Sponsor already rejects sfSponsorFlags present without sfSponsor with + // temINVALID_FLAG. Keep this as a defensive check. + // LCOV_EXCL_START + UNREACHABLE( + "xrpl::SponsorshipTransfer::preflight : sfSponsorFlags present without sfSponsor " + "when ending sponsorship"); + return temINVALID_FLAG; + // LCOV_EXCL_STOP + } + + if (ctx.tx.isFieldPresent(sfSponsee) && + ctx.tx.getAccountID(sfSponsee) == ctx.tx.getAccountID(sfAccount)) + { + JLOG(ctx.j.debug()) << "preflight: sfSponsee should not be the same as the account"; + return temMALFORMED; + } + } + + // Account-level reserve sponsorship changes the reserve responsibility for + // the account itself, so the new sponsor must explicitly co-sign. Object-level + // sponsorship may use pre-funded reserve sponsorship instead. + bool const isCreateOrReassign = + ctx.tx.isFlag(tfSponsorshipCreate) || ctx.tx.isFlag(tfSponsorshipReassign); + auto const reserveSponsor = getTxReserveSponsorID(ctx.tx); + bool const isAccountReserveSponsorship = + isCreateOrReassign && reserveSponsor && !ctx.tx.isFieldPresent(sfObjectID); + + if (isAccountReserveSponsorship && !ctx.tx.isFieldPresent(sfSponsorSignature)) + { + JLOG(ctx.j.debug()) << "preflight: account sponsorship requires sfSponsorSignature"; + return temMALFORMED; + } + + return tesSUCCESS; +} + +TER +SponsorshipTransfer::preclaim(PreclaimContext const& ctx) +{ + auto const objectID = ctx.tx[~sfObjectID]; + auto const newSponsorSleExpected = getTxReserveSponsor(ctx.view, ctx.tx); + if (!newSponsorSleExpected) + return newSponsorSleExpected.error(); // LCOV_EXCL_LINE + auto const newSponsorSle = *newSponsorSleExpected; + + auto const account = ctx.tx[sfAccount]; + auto const sponseeID = ctx.tx[~sfSponsee].value_or(account); + auto const sponseeSle = ctx.view.read(keylet::account(sponseeID)); + if (!sponseeSle) + { + // If it is ending sponsorship, sfSponsee is user input, return terNO_ACCOUNT if it does not + // exist. + if (ctx.tx.isFieldPresent(sfSponsee)) + return terNO_ACCOUNT; + + // If it is creating or reassigning sponsorship, sfSponsee is the account itself, which is + // always present by the time preclaim runs. Return tecINTERNAL if it does not exist. + return tecINTERNAL; // LCOV_EXCL_LINE + } + + // Default setup with an account sponsorship transfer. If it is an object transfer, they will be + // overridden to the object SLE and its type-specific sponsor field: + // sfHighSponsor/sfLowSponsor for a RippleState, sfSponsor for other object types. + SLE::const_pointer targetSle = sponseeSle; + auto const* sponsorField = &sfSponsor; + + if (objectID.has_value()) + { + auto const objectSle = ctx.view.read(keylet::unchecked(*objectID)); + if (!objectSle) + return tecNO_ENTRY; + + if (!isLedgerEntrySupportedBySponsorship(*objectSle)) + return tecNO_PERMISSION; + + if (!isLedgerEntryOwner(ctx.view, *objectSle, sponseeID)) + return tecNO_PERMISSION; + + // Object transfer: the target is the object, and its sponsor field + // depends on the object type, a RippleState stores the sponsor in + // sfHighSponsor/sfLowSponsor, while other object type uses sfSponsor. + targetSle = objectSle; + sponsorField = &getLedgerEntrySponsorField(*objectSle, sponseeID); + } + + bool const isSponsored = targetSle->isFieldPresent(*sponsorField); + + if (ctx.tx.isFlag(tfSponsorshipCreate)) + { + // Creating a new sponsorship: needs a new reserve sponsor, and the + // target must not already be sponsored + if (!newSponsorSle || isSponsored) + return tecNO_PERMISSION; + } + else if (ctx.tx.isFlag(tfSponsorshipReassign)) + { + // Reassigning sponsorship: needs a new reserve sponsor, and the target must already + // be sponsored + if (!newSponsorSle || !isSponsored) + return tecNO_PERMISSION; + + // Reassigning to the current sponsor would change no state, but would + // still draw down the sponsor's pre-funded reserve budget (and its + // reserve headroom would be double-counted in checkReserve). + if (targetSle->getAccountID(*sponsorField) == ctx.tx.getAccountID(sfSponsor)) + return tecNO_PERMISSION; + } + else if (ctx.tx.isFlag(tfSponsorshipEnd)) + { + // Ending sponsorship: no new reserve sponsor, the target must be sponsored. + if (newSponsorSle || !isSponsored) + return tecNO_PERMISSION; + + // Only the sponsor or sponsee can end sponsorship. + auto const sponsor = targetSle->getAccountID(*sponsorField); + if (account != sponsor && account != sponseeID) + return tecNO_PERMISSION; + } + + return tesSUCCESS; +} + +TER +SponsorshipTransfer::doApply() +{ + auto const objectID = ctx_.tx[~sfObjectID]; + + auto const sponseeID = ctx_.tx[~sfSponsee].value_or(accountID_); + auto const sponseeSle = view().peek(keylet::account(sponseeID)); + if (!sponseeSle) + return tefINTERNAL; // LCOV_EXCL_LINE + + auto const balanceBeforeFee = [&](SLE::const_ref sle) -> XRPAmount { + if (sle->getAccountID(sfAccount) == accountID_) + return preFeeBalance_; + return sle->getFieldAmount(sfBalance).xrp(); + }; + + bool const isCreate = ctx_.tx.isFlag(tfSponsorshipCreate); + bool const isReassign = ctx_.tx.isFlag(tfSponsorshipReassign); + + if (objectID.has_value()) + { + // Transfer object sponsor + auto const objectSle = view().peek(keylet::unchecked(*objectID)); + if (!objectSle) + return tefINTERNAL; // LCOV_EXCL_LINE + + // preclaim established that the sponsee owns the object. + if (!isLedgerEntryOwner(view(), *objectSle, sponseeID)) + return tefINTERNAL; // LCOV_EXCL_LINE + + auto const ownerSle = view().peek(keylet::account(sponseeID)); + if (!ownerSle) + return tefINTERNAL; // LCOV_EXCL_LINE + + auto const ownerCountDelta = + static_cast(getLedgerEntryOwnerCount(*objectSle)); + auto const& sponsorField = getLedgerEntrySponsorField(*objectSle, sponseeID); + + if (isCreate || isReassign) + { + auto const newSponsor = ctx_.tx[~sfSponsor]; + XRPL_ASSERT( + newSponsor.has_value(), + "xrpl::SponsorshipTransfer::doApply : sfSponsor present for object sponsor " + "create/reassign"); + if (!newSponsor) + return tefINTERNAL; // LCOV_EXCL_LINE + auto const newSponsorID = *newSponsor; + auto const newSponsorSle = view().peek(keylet::account(newSponsorID)); + if (!newSponsorSle) + return tefINTERNAL; // LCOV_EXCL_LINE + + // Check if new sponsor has sufficient balance + // NOLINTNEXTLINE(readability-suspicious-call-argument) + if (auto const ter = checkReserve( + ctx_.getApplyViewContext(), + sponseeSle, + sponseeSle->getFieldAmount(sfBalance).xrp(), + newSponsorSle, + {.ownerCountDelta = ownerCountDelta}, + ctx_.journal); + !isTesSuccess(ter)) + return ter; + + if (isCreate) + { + // Update owner's sponsored count + if (auto const ter = incrementSponsorCount( + view(), ownerSle, sfSponsoredOwnerCount, ownerCountDelta); + !isTesSuccess(ter)) + return ter; // LCOV_EXCL_LINE + } + else if (isReassign) + { + auto const oldSponsorID = objectSle->getAccountID(sponsorField); + if (!oldSponsorID) + return tefINTERNAL; // LCOV_EXCL_LINE + auto const oldSponsorSle = view().peek(keylet::account(oldSponsorID)); + if (!oldSponsorSle) + return tefINTERNAL; // LCOV_EXCL_LINE + + // Decrement old sponsor's sponsoring count + if (auto const ter = decrementSponsorCount( + view(), oldSponsorSle, sfSponsoringOwnerCount, ownerCountDelta); + !isTesSuccess(ter)) + return ter; // LCOV_EXCL_LINE + } + + // Increment new sponsor's sponsoring count + if (auto const ter = incrementSponsorCount( + view(), newSponsorSle, sfSponsoringOwnerCount, ownerCountDelta); + !isTesSuccess(ter)) + return ter; // LCOV_EXCL_LINE + + // Object is now sponsored by new sponsor + objectSle->setAccountID(sponsorField, newSponsorID); + view().update(objectSle); + + auto const sponsorshipSle = view().peek(keylet::sponsorship(newSponsorID, sponseeID)); + if (sponsorshipSle) + { + // Update ReserveCount for sponsorship object if it exists + if (auto const ter = + decrementPrefundedReserveCount(view(), sponsorshipSle, ownerCountDelta); + !isTesSuccess(ter)) + return ter; // LCOV_EXCL_LINE + } + } + else if (ctx_.tx.isFlag(tfSponsorshipEnd)) + { + // End object sponsor + auto const oldSponsorID = objectSle->getAccountID(sponsorField); + if (!oldSponsorID) + return tefINTERNAL; // LCOV_EXCL_LINE + auto const oldSponsorSle = view().peek(keylet::account(oldSponsorID)); + if (!oldSponsorSle) + return tefINTERNAL; // LCOV_EXCL_LINE + + // The owner reclaims the reserve burden when the object is no longer sponsored. + // We do not check the sponsee's reserve here (via `checkReserve`) so that a sponsor can + // always end a sponsorship, even if the sponsee lacks sufficient reserve. + + // Decrement sponsored count + if (auto const ter = decrementSponsorCount( + view(), sponseeSle, sfSponsoredOwnerCount, ownerCountDelta); + !isTesSuccess(ter)) + return ter; // LCOV_EXCL_LINE + + // Decrement old sponsoring count + if (auto const ter = decrementSponsorCount( + view(), oldSponsorSle, sfSponsoringOwnerCount, ownerCountDelta); + !isTesSuccess(ter)) + return ter; // LCOV_EXCL_LINE + + // Remove sponsor from object + objectSle->makeFieldAbsent(sponsorField); + view().update(objectSle); + } + } + else + { + // Account-level sponsorship is always co-signed (preflight requires + // sfSponsorSignature), so there is no pre-funded budget to draw down here. + if (isCreate || isReassign) + { + auto const newSponsor = ctx_.tx[~sfSponsor]; + XRPL_ASSERT( + newSponsor.has_value(), + "xrpl::SponsorshipTransfer::doApply : sfSponsor present for account sponsor " + "create/reassign"); + if (!newSponsor) + return tefINTERNAL; // LCOV_EXCL_LINE + auto const newSponsorID = *newSponsor; + auto const newSponsorSle = view().peek(keylet::account(newSponsorID)); + if (!newSponsorSle) + return tefINTERNAL; // LCOV_EXCL_LINE + + if (auto const ter = checkReserve( + ctx_.getApplyViewContext(), + sponseeSle, + sponseeSle->getFieldAmount(sfBalance).xrp(), + newSponsorSle, + {.accountCountDelta = 1}, + ctx_.journal); + !isTesSuccess(ter)) + return ter; + + if (isReassign) + { + auto const oldSponsorID = sponseeSle->getAccountID(sfSponsor); + if (!oldSponsorID) + return tefINTERNAL; // LCOV_EXCL_LINE + auto const oldSponsorSle = view().peek(keylet::account(oldSponsorID)); + if (!oldSponsorSle) + return tefINTERNAL; // LCOV_EXCL_LINE + + // Decrement old sponsoring count + if (auto const ter = + decrementSponsorCount(view(), oldSponsorSle, sfSponsoringAccountCount, 1); + !isTesSuccess(ter)) + return ter; // LCOV_EXCL_LINE + } + + // Increment new sponsoring count + if (auto const ter = + incrementSponsorCount(view(), newSponsorSle, sfSponsoringAccountCount, 1); + !isTesSuccess(ter)) + return ter; // LCOV_EXCL_LINE + + // Account is now sponsored by new sponsor + sponseeSle->setAccountID(sfSponsor, newSponsorID); + view().update(sponseeSle); + } + else if (ctx_.tx.isFlag(tfSponsorshipEnd)) + { + // End account sponsor + auto const oldSponsorID = sponseeSle->getAccountID(sfSponsor); + if (!oldSponsorID) + return tefINTERNAL; // LCOV_EXCL_LINE + auto const oldSponsorSle = view().peek(keylet::account(oldSponsorID)); + if (!oldSponsorSle) + return tefINTERNAL; // LCOV_EXCL_LINE + + // The sponsee must be able to hold its own account reserve after + // the sponsorship is removed. + if (auto const ter = checkReserve( + ctx_.getApplyViewContext(), + sponseeSle, + balanceBeforeFee(sponseeSle), + SLE::pointer(), + {.accountCountDelta = 1}, + ctx_.journal); + !isTesSuccess(ter)) + return ter; + + sponseeSle->makeFieldAbsent(sfSponsor); + view().update(sponseeSle); + + // Decrement account sponsoring count + if (auto const ter = + decrementSponsorCount(view(), oldSponsorSle, sfSponsoringAccountCount, 1); + !isTesSuccess(ter)) + return ter; // LCOV_EXCL_LINE + } + } + + return tesSUCCESS; +} + +void +SponsorshipTransfer::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) +{ +} + +bool +SponsorshipTransfer::finalizeInvariants( + STTx const&, + TER, + XRPAmount, + ReadView const&, + beast::Journal const&) +{ + return true; +} + +} // namespace xrpl diff --git a/src/libxrpl/tx/transactors/system/Batch.cpp b/src/libxrpl/tx/transactors/system/Batch.cpp index ba54c14b1e..dee465cb88 100644 --- a/src/libxrpl/tx/transactors/system/Batch.cpp +++ b/src/libxrpl/tx/transactors/system/Batch.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -227,6 +228,16 @@ Batch::preflight(PreflightContext const& ctx) return temINVALID_FLAG; } + if (ctx.tx.isFieldPresent(sfSponsorFlags)) + { + if (isReserveSponsored(ctx.tx)) + { + JLOG(ctx.j.debug()) << "BatchTrace[" << parentBatchId << "]:" + << "spfSponsorReserve is not allowed on outer Batch."; + return temINVALID_FLAG; + } + } + auto const& rawTxns = ctx.tx.getFieldArray(sfRawTransactions); if (rawTxns.size() <= 1) { @@ -331,6 +342,14 @@ Batch::preflight(PreflightContext const& ctx) return ret; } } + if (stx.isFieldPresent(sfSponsorSignature)) + { + auto const sponsorSignature = stx.getFieldObject(sfSponsorSignature); + if (auto const ret = checkSignatureFields(sponsorSignature, hash, "sponsor signature ")) + { + return ret; + } + } // Check that the Fee is native asset (XRP) and zero if (auto const fee = stx.getFieldAmount(sfFee); !fee.native() || fee.xrp() != beast::kZero) @@ -341,6 +360,10 @@ Batch::preflight(PreflightContext const& ctx) return temBAD_FEE; } + // Disallow fee sponsorship on Batch inner txs + if (stx.isFieldPresent(sfSponsor) && isFeeSponsored(stx)) + return temINVALID_FLAG; + auto const innerAccount = stx.getAccountID(sfAccount); if (auto const preflightResult = xrpl::preflight(ctx.registry, ctx.rules, parentBatchId, stx, TapBatch, ctx.j); @@ -422,7 +445,7 @@ Batch::preflightSigValidated(PreflightContext const& ctx) { // A delegated inner is signed by the delegate, not the account holder, // so the delegate is the required signer when present. - AccountID const authorizer = rb.getFeePayer(); + AccountID const authorizer = rb.getInitiator(); // The outer account signs the batch itself, so it is never added to the // required signers. @@ -433,6 +456,10 @@ Batch::preflightSigValidated(PreflightContext const& ctx) if (auto const counterparty = rb[~sfCounterparty]; counterparty && counterparty != outerAccount) requiredSigners.push_back(*counterparty); + + if (auto const sponsor = rb.at(~sfSponsor); + sponsor && rb.isFieldPresent(sfSponsorSignature) && sponsor != outerAccount) + requiredSigners.push_back(*sponsor); } std::ranges::sort(requiredSigners); auto const dupes = std::ranges::unique(requiredSigners); diff --git a/src/libxrpl/tx/transactors/system/TicketCreate.cpp b/src/libxrpl/tx/transactors/system/TicketCreate.cpp index 73a72c217d..e19dc9fe96 100644 --- a/src/libxrpl/tx/transactors/system/TicketCreate.cpp +++ b/src/libxrpl/tx/transactors/system/TicketCreate.cpp @@ -75,13 +75,12 @@ TicketCreate::doApply() // check the starting balance because we want to allow dipping into the // reserve to pay fees. std::uint32_t const ticketCount = ctx_.tx[sfTicketCount]; - { - XRPAmount const reserve = - view().fees().accountReserve(sleAccountRoot->getFieldU32(sfOwnerCount) + ticketCount); - - if (preFeeBalance_ < reserve) - return tecINSUFFICIENT_RESERVE; - } + if (preFeeBalance_ < accountReserve( + view(), + sleAccountRoot, + j_, + {.ownerCountDelta = static_cast(ticketCount)})) + return tecINSUFFICIENT_RESERVE; beast::Journal const viewJ{ctx_.registry.get().getJournal("View")}; @@ -105,6 +104,7 @@ TicketCreate::doApply() sleTicket->setAccountID(sfAccount, accountID_); sleTicket->setFieldU32(sfTicketSequence, curTicketSeq); + view().insert(sleTicket); auto const page = view().dirInsert( @@ -125,7 +125,7 @@ TicketCreate::doApply() sleAccountRoot->setFieldU32(sfTicketCount, oldTicketCount + ticketCount); // Every added Ticket counts against the creator's reserve. - adjustOwnerCount(view(), sleAccountRoot, ticketCount, viewJ); + increaseOwnerCount(view(), sleAccountRoot, {}, ticketCount, viewJ); // TicketCreate is the only transaction that can cause an account root's // Sequence field to increase by more than one. October 2018. diff --git a/src/libxrpl/tx/transactors/token/MPTokenAuthorize.cpp b/src/libxrpl/tx/transactors/token/MPTokenAuthorize.cpp index 6db37515cb..0aeb6f33d1 100644 --- a/src/libxrpl/tx/transactors/token/MPTokenAuthorize.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenAuthorize.cpp @@ -164,7 +164,7 @@ MPTokenAuthorize::doApply() { auto const& tx = ctx_.tx; return authorizeMPToken( - ctx_.view(), + ctx_.getApplyViewContext(), preFeeBalance_, tx[sfMPTokenIssuanceID], accountID_, diff --git a/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp b/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp index e30127b688..aad1642f68 100644 --- a/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -113,22 +114,35 @@ MPTokenIssuanceCreate::preflight(PreflightContext const& ctx) } std::expected -MPTokenIssuanceCreate::create(ApplyView& view, beast::Journal journal, MPTCreateArgs const& args) +MPTokenIssuanceCreate::create( + ApplyViewContext ctx, + beast::Journal journal, + MPTCreateArgs const& args) { - auto const acct = view.peek(keylet::account(args.account)); + auto const acct = ctx.view.peek(keylet::account(args.account)); if (!acct) return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE - if (args.priorBalance && - *(args.priorBalance) < view.fees().accountReserve((*acct)[sfOwnerCount] + 1)) - return std::unexpected(tecINSUFFICIENT_RESERVE); + // A reserve sponsor only covers tx.Account's own objects. + auto const sponsorExp = getEffectiveTxReserveSponsor(ctx, acct); + if (!sponsorExp) + return std::unexpected(sponsorExp.error()); // LCOV_EXCL_LINE + auto const sponsorSle = *sponsorExp; + + if (args.priorBalance) + { + if (auto const ret = checkReserve( + ctx, acct, *(args.priorBalance), sponsorSle, {.ownerCountDelta = 1}, journal); + !isTesSuccess(ret)) + return std::unexpected(ret); + } auto const mptId = makeMptID(args.sequence, args.account); auto const mptIssuanceKeylet = keylet::mptokenIssuance(mptId); // create the MPTokenIssuance { - auto const ownerNode = view.dirInsert( + auto const ownerNode = ctx.view.dirInsert( keylet::ownerDir(args.account), mptIssuanceKeylet, describeOwnerDir(args.account)); if (!ownerNode) @@ -166,7 +180,7 @@ MPTokenIssuanceCreate::create(ApplyView& view, beast::Journal journal, MPTCreate // populate this after the pseudo-account's MPToken / // RippleState has been installed. A missing holding here // would dangle the pointer and is a programmer error. - auto const sleHolding = view.read(keylet::unchecked(*args.referenceHolding)); + auto const sleHolding = ctx.view.read(keylet::unchecked(*args.referenceHolding)); if (!sleHolding) return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE auto const type = sleHolding->getType(); @@ -175,11 +189,13 @@ MPTokenIssuanceCreate::create(ApplyView& view, beast::Journal journal, MPTCreate (*mptIssuance)[sfReferenceHolding] = *args.referenceHolding; } - view.insert(mptIssuance); + addSponsorToLedgerEntry(mptIssuance, sponsorSle); + + ctx.view.insert(mptIssuance); } // Update owner count. - adjustOwnerCount(view, acct, 1, journal); + increaseOwnerCount(ctx.view, acct, sponsorSle, 1, journal); return mptId; } @@ -189,7 +205,7 @@ MPTokenIssuanceCreate::doApply() { auto const& tx = ctx_.tx; auto const result = create( - view(), + ctx_.getApplyViewContext(), j_, { .priorBalance = preFeeBalance_, diff --git a/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp b/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp index c56697767b..05d39c3596 100644 --- a/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp @@ -49,10 +49,9 @@ MPTokenIssuanceDestroy::doApply() if (!view().dirRemove(keylet::ownerDir(accountID_), (*mpt)[sfOwnerNode], mpt->key(), false)) return tefBAD_LEDGER; // LCOV_EXCL_LINE + decreaseOwnerCountForObject(view(), accountID_, mpt, 1, j_); view().erase(mpt); - adjustOwnerCount(view(), view().peek(keylet::account(accountID_)), -1, j_); - return tesSUCCESS; } diff --git a/src/libxrpl/tx/transactors/token/TrustSet.cpp b/src/libxrpl/tx/transactors/token/TrustSet.cpp index e79e3cad5a..3d078ce825 100644 --- a/src/libxrpl/tx/transactors/token/TrustSet.cpp +++ b/src/libxrpl/tx/transactors/token/TrustSet.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -297,8 +298,6 @@ TrustSet::doApply() if (!sle) return tefINTERNAL; // LCOV_EXCL_LINE - std::uint32_t const uOwnerCount = sle->getFieldU32(sfOwnerCount); - // The reserve that is required to create the line. Note // that although the reserve increases with every item // an account owns, in the case of trust lines we only @@ -317,9 +316,23 @@ TrustSet::doApply() // well. A person with no intention of using the gateway // could use the extra XRP for their own purposes. + auto const sponsorExp = getTxReserveSponsor(ctx_.getApplyViewContext()); + if (!sponsorExp) + return sponsorExp.error(); // LCOV_EXCL_LINE + auto const sponsorSle = *sponsorExp; + + auto getSponsor = [&sponsorSle, this](AccountID const& account) { + return (sponsorSle && account == accountID_) ? sponsorSle : SLE::pointer(); + }; + + // The "free-tier" shortcut (ownerCount < 2) only applies when there is no sponsor. + // With any sponsor on the tx, the sponsor must cover the reserve (via balance or + // prefunded budget), so the reserve check always runs. + bool const freeTrustLine = !sponsorSle && (ownerCount(sle, j_) < 2); + std::uint32_t const uOwnerCount = ownerCount(sle, j_); XRPAmount const reserveCreate( (uOwnerCount < 2) ? XRPAmount(beast::kZero) - : view().fees().accountReserve(uOwnerCount + 1)); + : accountReserve(view(), sle, j_, {.ownerCountDelta = 1})); std::uint32_t const uQualityIn(bQualityIn ? ctx_.tx.getFieldU32(sfQualityIn) : 0); std::uint32_t uQualityOut(bQualityOut ? ctx_.tx.getFieldU32(sfQualityOut) : 0); @@ -505,6 +518,11 @@ TrustSet::doApply() bool bReserveIncrease = false; + auto const currentHighSponsor = + getLedgerEntryReserveSponsor(view(), sleRippleState, sfHighSponsor); + auto const currentLowSponsor = + getLedgerEntryReserveSponsor(view(), sleRippleState, sfLowSponsor); + if (bSetAuth) { uFlagsOut |= (bHigh ? lsfHighAuth : lsfLowAuth); @@ -512,10 +530,30 @@ TrustSet::doApply() if (bLowReserveSet && !bLowReserved) { + SLE::pointer const lowSponsor = getSponsor(uLowAccountID); + + if (view().rules().enabled(featureSponsor)) + { + if (auto const ret = checkReserve( + ctx_.getApplyViewContext(), + sleLowAccount, + preFeeBalance_, + lowSponsor, + {.ownerCountDelta = 1}, + j_, + tecINSUF_RESERVE_LINE); + lowSponsor && !isTesSuccess(ret)) + { + return ret; + } + } + // Set reserve for low account. - adjustOwnerCount(view(), sleLowAccount, 1, viewJ); + increaseOwnerCount(view(), sleLowAccount, lowSponsor, 1, viewJ); uFlagsOut |= lsfLowReserve; + addSponsorToLedgerEntry(sleRippleState, lowSponsor, sfLowSponsor); + if (!bHigh) bReserveIncrease = true; } @@ -523,16 +561,41 @@ TrustSet::doApply() if (bLowReserveClear && bLowReserved) { // Clear reserve for low account. - adjustOwnerCount(view(), sleLowAccount, -1, viewJ); + decreaseOwnerCount(view(), sleLowAccount, currentLowSponsor, 1, viewJ); uFlagsOut &= ~lsfLowReserve; + + removeSponsorFromLedgerEntry(sleRippleState, sfLowSponsor); } if (bHighReserveSet && !bHighReserved) { + SLE::pointer const highSponsor = getSponsor(uHighAccountID); + + // should be checked PreFunded Sponsor before increaseOwnerCount() + // For PreFunded sponsors, we need to check if there are sufficient reserves before + // calling increaseOwnerCount(). + if (view().rules().enabled(featureSponsor)) + { + if (auto const ret = checkReserve( + ctx_.getApplyViewContext(), + sleHighAccount, + preFeeBalance_, + highSponsor, + {.ownerCountDelta = 1}, + j_, + tecINSUF_RESERVE_LINE); + highSponsor && !isTesSuccess(ret)) + { + return ret; + } + } + // Set reserve for high account. - adjustOwnerCount(view(), sleHighAccount, 1, viewJ); + increaseOwnerCount(view(), sleHighAccount, highSponsor, 1, viewJ); uFlagsOut |= lsfHighReserve; + addSponsorToLedgerEntry(sleRippleState, highSponsor, sfHighSponsor); + if (bHigh) bReserveIncrease = true; } @@ -540,34 +603,75 @@ TrustSet::doApply() if (bHighReserveClear && bHighReserved) { // Clear reserve for high account. - adjustOwnerCount(view(), sleHighAccount, -1, viewJ); + decreaseOwnerCount(view(), sleHighAccount, currentHighSponsor, 1, viewJ); uFlagsOut &= ~lsfHighReserve; + + removeSponsorFromLedgerEntry(sleRippleState, sfHighSponsor); } if (uFlagsIn != uFlagsOut) sleRippleState->setFieldU32(sfFlags, uFlagsOut); - if (bDefault || badCurrency() == currency) + if (view().rules().enabled(featureSponsor)) { - // Delete. + if (bDefault || badCurrency() == currency) + { + // Delete. - terResult = trustDelete(view(), sleRippleState, uLowAccountID, uHighAccountID, viewJ); - } - // Reserve is not scaled by load. - else if (bReserveIncrease && preFeeBalance_ < reserveCreate) - { - JLOG(j_.trace()) << "Delay transaction: Insufficent reserve to " - "add trust line."; + terResult = + trustDelete(view(), sleRippleState, uLowAccountID, uHighAccountID, viewJ); + } + // Reserve is not scaled by load + else if ( + auto const ret = checkReserve( + ctx_.getApplyViewContext(), + sle, + preFeeBalance_, + sponsorSle, + {}, + j_, + tecINSUF_RESERVE_LINE); + !freeTrustLine && bReserveIncrease && !isTesSuccess(ret)) + { + JLOG(j_.trace()) << "Delay transaction: Insufficent reserve to " + "add trust line."; - // Another transaction could provide XRP to the account and then - // this transaction would succeed. - terResult = tecINSUF_RESERVE_LINE; + // Another transaction could provide XRP to the account and then + // this transaction would succeed. + terResult = ret; + } + else + { + view().update(sleRippleState); + + JLOG(j_.trace()) << "Modify ripple line"; + } } else { - view().update(sleRippleState); + if (bDefault || badCurrency() == currency) + { + // Delete. - JLOG(j_.trace()) << "Modify ripple line"; + terResult = + trustDelete(view(), sleRippleState, uLowAccountID, uHighAccountID, viewJ); + } + // Reserve is not scaled by load. + else if (bReserveIncrease && preFeeBalance_ < reserveCreate) + { + JLOG(j_.trace()) << "Delay transaction: Insufficent reserve to " + "add trust line."; + + // Another transaction could provide XRP to the account and then + // this transaction would succeed. + terResult = tecINSUF_RESERVE_LINE; + } + else + { + view().update(sleRippleState); + + JLOG(j_.trace()) << "Modify ripple line"; + } } } // Line does not exist. @@ -582,8 +686,8 @@ TrustSet::doApply() JLOG(j_.trace()) << "Redundant: Setting non-existent ripple line to defaults."; return tecNO_LINE_REDUNDANT; } - else if (preFeeBalance_ < reserveCreate) // Reserve is not scaled by - // load. + // reserve is not scaled by load + else if (!view().rules().enabled(featureSponsor) && preFeeBalance_ < reserveCreate) { JLOG(j_.trace()) << "Delay transaction: Line does not exist. " "Insufficent reserve to create line."; @@ -592,6 +696,25 @@ TrustSet::doApply() // transaction would succeed. terResult = tecNO_LINE_INSUF_RESERVE; } + else if ( + auto const ret = checkReserve( + ctx_.getApplyViewContext(), + sle, + preFeeBalance_, + sponsorSle, + {.ownerCountDelta = 1}, + j_, + tecNO_LINE_INSUF_RESERVE); + view().rules().enabled(featureSponsor) && !freeTrustLine && + !isTesSuccess(ret)) // Reserve is not scaled by load. + { + JLOG(j_.trace()) << "Delay transaction: Line does not exist. " + "Insufficent reserve to create line."; + + // Another transaction could create the account and then this + // transaction would succeed. + terResult = ret; + } else { // Zero balance in currency. @@ -617,6 +740,7 @@ TrustSet::doApply() saLimitAllow, // Limit for who is being charged. uQualityIn, uQualityOut, + sponsorSle, viewJ); } diff --git a/src/libxrpl/tx/transactors/vault/VaultClawback.cpp b/src/libxrpl/tx/transactors/vault/VaultClawback.cpp index 541660c98f..d77286b667 100644 --- a/src/libxrpl/tx/transactors/vault/VaultClawback.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultClawback.cpp @@ -389,8 +389,8 @@ VaultClawback::doApply() auto const& vaultAccount = vault->at(sfAccount); // Transfer shares from holder to vault. - if (auto const ter = - accountSend(view(), holder, vaultAccount, sharesDestroyed, j_, WaiveTransferFee::Yes); + if (auto const ter = accountSend( + view(), holder, vaultAccount, sharesDestroyed, j_, {}, WaiveTransferFee::Yes); !isTesSuccess(ter)) return ter; @@ -399,7 +399,8 @@ VaultClawback::doApply() // Keep MPToken if holder is the vault owner. if (holder != vault->at(sfOwner)) { - if (auto const ter = removeEmptyHolding(view(), holder, sharesDestroyed.asset(), j_); + if (auto const ter = + removeEmptyHolding(ctx_.getApplyViewContext(), holder, sharesDestroyed.asset(), j_); isTesSuccess(ter)) { JLOG(j_.debug()) // @@ -425,7 +426,7 @@ VaultClawback::doApply() { // Transfer assets from vault to issuer. if (auto const ter = accountSend( - view(), vaultAccount, accountID_, assetsRecovered, j_, WaiveTransferFee::Yes); + view(), vaultAccount, accountID_, assetsRecovered, j_, {}, WaiveTransferFee::Yes); !isTesSuccess(ter)) return ter; diff --git a/src/libxrpl/tx/transactors/vault/VaultCreate.cpp b/src/libxrpl/tx/transactors/vault/VaultCreate.cpp index d262132b6f..e1f5873a89 100644 --- a/src/libxrpl/tx/transactors/vault/VaultCreate.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultCreate.cpp @@ -146,6 +146,7 @@ VaultCreate::doApply() // we can consider downgrading them to `tef` or `tem`. auto const& tx = ctx_.tx; + auto applyViewContext = ctx_.getApplyViewContext(); auto const sequence = tx.getSeqValue(); auto const owner = view().peek(keylet::account(accountID_)); if (owner == nullptr) @@ -156,9 +157,8 @@ VaultCreate::doApply() if (auto ter = dirLink(view(), accountID_, vault)) return ter; // We will create Vault and PseudoAccount, hence increase OwnerCount by 2 - adjustOwnerCount(view(), owner, 2, j_); - auto const ownerCount = owner->at(sfOwnerCount); - if (preFeeBalance_ < view().fees().accountReserve(ownerCount)) + increaseOwnerCount(view(), owner, {}, 2, j_); + if (preFeeBalance_ < accountReserve(view(), owner, j_)) return tecINSUFFICIENT_RESERVE; auto maybePseudo = createPseudoAccount(view(), vault->key(), sfVaultID); @@ -168,7 +168,8 @@ VaultCreate::doApply() AccountID const pseudoId = pseudo->at(sfAccount); auto const asset = tx[sfAsset]; - if (auto ter = addEmptyHolding(view(), pseudoId, preFeeBalance_, asset, j_); !isTesSuccess(ter)) + if (auto ter = addEmptyHolding(applyViewContext, pseudoId, preFeeBalance_, asset, j_); + !isTesSuccess(ter)) return ter; std::uint8_t const scale = (asset.holds() || asset.native()) @@ -197,7 +198,7 @@ VaultCreate::doApply() : keylet::trustLine(pseudoId, asset.get()).key; }(); auto const maybeShare = MPTokenIssuanceCreate::create( - view(), + applyViewContext, j_, { .priorBalance = std::nullopt, @@ -243,8 +244,8 @@ VaultCreate::doApply() view().insert(vault); // Explicitly create MPToken for the vault owner - if (auto const err = - authorizeMPToken(view(), preFeeBalance_, mptIssuanceID, accountID_, ctx_.journal); + if (auto const err = authorizeMPToken( + applyViewContext, preFeeBalance_, mptIssuanceID, accountID_, ctx_.journal); !isTesSuccess(err)) return err; @@ -252,7 +253,13 @@ VaultCreate::doApply() if (tx.isFlag(tfVaultPrivate)) { if (auto const err = authorizeMPToken( - view(), preFeeBalance_, mptIssuanceID, pseudoId, ctx_.journal, {}, accountID_); + applyViewContext, + preFeeBalance_, + mptIssuanceID, + pseudoId, + ctx_.journal, + {}, + accountID_); !isTesSuccess(err)) return err; } diff --git a/src/libxrpl/tx/transactors/vault/VaultDelete.cpp b/src/libxrpl/tx/transactors/vault/VaultDelete.cpp index fa38ae278b..497a2f2465 100644 --- a/src/libxrpl/tx/transactors/vault/VaultDelete.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultDelete.cpp @@ -96,13 +96,15 @@ TER VaultDelete::doApply() { auto const vault = view().peek(keylet::vault(ctx_.tx[sfVaultID])); + auto applyViewContext = ctx_.getApplyViewContext(); if (!vault) return tefINTERNAL; // LCOV_EXCL_LINE // Destroy the asset holding. auto asset = vault->at(sfAsset); - if (auto ter = removeEmptyHolding(view(), vault->at(sfAccount), asset, j_); !isTesSuccess(ter)) + if (auto ter = removeEmptyHolding(applyViewContext, vault->at(sfAccount), asset, j_); + !isTesSuccess(ter)) return ter; auto const& pseudoID = vault->at(sfAccount); @@ -130,7 +132,8 @@ VaultDelete::doApply() // Try to remove MPToken for vault shares for the vault owner if it exists. if (auto const mptoken = view().peek(keylet::mptoken(shareMPTID, accountID_))) { - if (auto const ter = removeEmptyHolding(view(), accountID_, MPTIssue(shareMPTID), j_); + if (auto const ter = + removeEmptyHolding(applyViewContext, accountID_, MPTIssue(shareMPTID), j_); !isTesSuccess(ter)) { // LCOV_EXCL_START @@ -151,7 +154,7 @@ VaultDelete::doApply() return tefBAD_LEDGER; // LCOV_EXCL_STOP } - adjustOwnerCount(view(), pseudoAcct, -1, j_); + decreaseOwnerCountForObject(view(), pseudoAcct, mpt, 1, j_); view().erase(mpt); @@ -210,7 +213,7 @@ VaultDelete::doApply() } // We are destroying Vault and PseudoAccount, hence decrease by 2 - adjustOwnerCount(view(), owner, -2, j_); + decreaseOwnerCountForObject(view(), owner, vault, 2, j_); // Destroy the vault. view().erase(vault); diff --git a/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp b/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp index d5eb80b84d..aa9cfc8537 100644 --- a/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp @@ -23,6 +23,7 @@ #include #include +#include #include namespace xrpl { @@ -196,6 +197,7 @@ VaultDeposit::doApply() { bool const fix320Enabled = view().rules().enabled(fixCleanup3_2_0); auto const vault = view().peek(keylet::vault(ctx_.tx[sfVaultID])); + auto applyViewContext = ctx_.getApplyViewContext(); if (!vault) return tefINTERNAL; // LCOV_EXCL_LINE auto const vaultAsset = vault->at(sfAsset); @@ -230,7 +232,7 @@ VaultDeposit::doApply() if (vault->isFlag(lsfVaultPrivate) && accountID_ != vault->at(sfOwner)) { if (auto const err = enforceMPTokenAuthorization( - ctx_.view(), mptIssuanceID, accountID_, preFeeBalance_, j_); + applyViewContext, mptIssuanceID, accountID_, preFeeBalance_, j_); !isTesSuccess(err)) return err; } @@ -240,7 +242,11 @@ VaultDeposit::doApply() if (!view().exists(keylet::mptoken(mptIssuanceID, accountID_))) { if (auto const err = authorizeMPToken( - view(), preFeeBalance_, mptIssuanceID->value(), accountID_, ctx_.journal); + applyViewContext, + preFeeBalance_, + mptIssuanceID->value(), + accountID_, + ctx_.journal); !isTesSuccess(err)) return err; } @@ -252,7 +258,7 @@ VaultDeposit::doApply() XRPL_ASSERT( accountID_ == vault->at(sfOwner), "xrpl::VaultDeposit::doApply : account is owner"); if (auto const err = authorizeMPToken( - view(), + applyViewContext, preFeeBalance_, // priorBalance mptIssuanceID->value(), // mptIssuanceID sleIssuance->at(sfIssuer), // account @@ -319,7 +325,7 @@ VaultDeposit::doApply() // Transfer assets from depositor to vault. if (auto const ter = accountSend( - view(), accountID_, vaultAccount, assetsDeposited, j_, WaiveTransferFee::Yes); + view(), accountID_, vaultAccount, assetsDeposited, j_, {}, WaiveTransferFee::Yes); !isTesSuccess(ter)) return ter; @@ -346,8 +352,8 @@ VaultDeposit::doApply() } // Transfer shares from vault to depositor. - if (auto const ter = - accountSend(view(), vaultAccount, accountID_, sharesCreated, j_, WaiveTransferFee::Yes); + if (auto const ter = accountSend( + view(), vaultAccount, accountID_, sharesCreated, j_, {}, WaiveTransferFee::Yes); !isTesSuccess(ter)) return ter; diff --git a/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp b/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp index ad91c55723..353b72c30d 100644 --- a/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp @@ -194,6 +194,7 @@ TER VaultWithdraw::doApply() { auto const vault = view().peek(keylet::vault(ctx_.tx[sfVaultID])); + auto applyViewContext = ctx_.getApplyViewContext(); if (!vault) return tefINTERNAL; // LCOV_EXCL_LINE @@ -349,9 +350,10 @@ VaultWithdraw::doApply() view().update(vault); auto const& vaultAccount = vault->at(sfAccount); + // Transfer shares from depositor to vault. if (auto const ter = accountSend( - view(), accountID_, vaultAccount, sharesRedeemed, j_, WaiveTransferFee::Yes); + view(), accountID_, vaultAccount, sharesRedeemed, j_, {}, WaiveTransferFee::Yes); !isTesSuccess(ter)) return ter; @@ -360,7 +362,8 @@ VaultWithdraw::doApply() // Keep MPToken if holder is the vault owner. if (accountID_ != vault->at(sfOwner)) { - if (auto const ter = removeEmptyHolding(view(), accountID_, sharesRedeemed.asset(), j_); + if (auto const ter = + removeEmptyHolding(applyViewContext, accountID_, sharesRedeemed.asset(), j_); isTesSuccess(ter)) { JLOG(j_.debug()) // @@ -386,7 +389,7 @@ VaultWithdraw::doApply() auto const dstAcct = ctx_.tx[~sfDestination].value_or(accountID_); return doWithdraw( - view(), ctx_.tx, accountID_, dstAcct, vaultAccount, preFeeBalance_, assetsWithdrawn, j_); + applyViewContext, accountID_, dstAcct, vaultAccount, preFeeBalance_, assetsWithdrawn, j_); } void diff --git a/src/test/app/AMMExtendedMPT_test.cpp b/src/test/app/AMMExtendedMPT_test.cpp index e6dcc95733..63b7841fba 100644 --- a/src/test/app/AMMExtendedMPT_test.cpp +++ b/src/test/app/AMMExtendedMPT_test.cpp @@ -463,7 +463,7 @@ private: // Provide micro amounts to compensate for fees to make results round // nice. auto const startingXrp = - XRP(100) + env.current()->fees().accountReserve(2) + env.current()->fees().base * 3; + XRP(100) + env.current()->fees().accountReserve(2, 1) + env.current()->fees().base * 3; env.fund(startingXrp, gw_, alice_); env.fund(XRP(2'000), bob_); diff --git a/src/test/app/AMMExtended_test.cpp b/src/test/app/AMMExtended_test.cpp index a0a7d0fb15..563bb3ca33 100644 --- a/src/test/app/AMMExtended_test.cpp +++ b/src/test/app/AMMExtended_test.cpp @@ -546,7 +546,7 @@ private: // 1 for each trust limit == 3 (alice_ < mtgox/amazon/bitstamp) + // 1 for payment == 4 auto const startingXrp = - XRP(100) + env.current()->fees().accountReserve(3) + env.current()->fees().base * 4; + XRP(100) + env.current()->fees().accountReserve(3, 1) + env.current()->fees().base * 4; env.fund(startingXrp, gw1, gw2, gw3, localAlice); env.fund(XRP(2'000), localBob); diff --git a/src/test/app/AMMMPT_test.cpp b/src/test/app/AMMMPT_test.cpp index 4877406ca1..bf0bc5c7d7 100644 --- a/src/test/app/AMMMPT_test.cpp +++ b/src/test/app/AMMMPT_test.cpp @@ -1002,7 +1002,7 @@ private: // Insufficient reserve, XRP/MPT { - Env env(*this); + Env env(*this, features); auto const startingXrp = reserve(env, 4) + env.current()->fees().base * 4; env.fund(XRP(10'000), gw_); env.fund(XRP(10'000), alice_); diff --git a/src/test/app/AccountSet_test.cpp b/src/test/app/AccountSet_test.cpp index 362113aec2..d94fb66e50 100644 --- a/src/test/app/AccountSet_test.cpp +++ b/src/test/app/AccountSet_test.cpp @@ -1,4 +1,3 @@ - #include #include #include @@ -453,7 +452,7 @@ public: // We'll insert a replacement for the account root // with the higher (currently invalid) transfer rate. - auto replacement = std::make_shared(*sle, sle->key()); + auto replacement = std::make_shared(*sle); (*replacement)[sfTransferRate] = static_cast(transferRate * QUALITY_ONE); view.rawReplace(replacement); diff --git a/src/test/app/CheckMPT_test.cpp b/src/test/app/CheckMPT_test.cpp index 861a115fc9..93e752154c 100644 --- a/src/test/app/CheckMPT_test.cpp +++ b/src/test/app/CheckMPT_test.cpp @@ -409,7 +409,7 @@ class CheckMPT_test : public beast::unit_test::Suite // Insufficient reserve. Account const cheri{"cheri"}; - env.fund(env.current()->fees().accountReserve(1) - drops(1), cheri); + env.fund(env.current()->fees().accountReserve(1, 1) - drops(1), cheri); env(check::create(cheri, bob, usd(50)), Fee(drops(env.current()->fees().base)), diff --git a/src/test/app/Check_test.cpp b/src/test/app/Check_test.cpp index 3ad1e3fa4e..b19a9f6894 100644 --- a/src/test/app/Check_test.cpp +++ b/src/test/app/Check_test.cpp @@ -470,7 +470,7 @@ class Check_test : public beast::unit_test::Suite // Insufficient reserve. Account const cheri{"cheri"}; - env.fund(env.current()->fees().accountReserve(1) - drops(1), cheri); + env.fund(env.current()->fees().accountReserve(1, 1) - drops(1), cheri); env.close(); env(check::create(cheri, bob, usd(50)), diff --git a/src/test/app/Credentials_test.cpp b/src/test/app/Credentials_test.cpp index 456a53bc01..1f6ec012c8 100644 --- a/src/test/app/Credentials_test.cpp +++ b/src/test/app/Credentials_test.cpp @@ -638,8 +638,8 @@ struct Credentials_test : public beast::unit_test::Suite { Env env{*this, features}; - env.fund(drops(env.current()->fees().accountReserve(1)), issuer); - env.fund(drops(env.current()->fees().accountReserve(0)), subject); + env.fund(drops(env.current()->fees().accountReserve(1, 1)), issuer); + env.fund(drops(env.current()->fees().accountReserve(0, 1)), subject); env.close(); { diff --git a/src/test/app/Delegate_test.cpp b/src/test/app/Delegate_test.cpp index 6f1d2ce7de..257ed33619 100644 --- a/src/test/app/Delegate_test.cpp +++ b/src/test/app/Delegate_test.cpp @@ -275,7 +275,7 @@ class Delegate_test : public beast::unit_test::Suite Account const bob{"bob"}; auto const txFee = env.current()->fees().base; - env.fund(env.current()->fees().accountReserve(0) + txFee, alice); + env.fund(env.current()->fees().accountReserve(0, 1) + txFee, alice); env.fund(XRP(100000), bob); env.close(); @@ -292,7 +292,7 @@ class Delegate_test : public beast::unit_test::Suite auto const txFee = env.current()->fees().base; - env.fund(env.current()->fees().accountReserve(1) + (txFee * 4), alice); + env.fund(env.current()->fees().accountReserve(1, 1) + (txFee * 4), alice); env.fund(XRP(100000), bob, carol); env.close(); @@ -318,8 +318,8 @@ class Delegate_test : public beast::unit_test::Suite Account const alice{"alice"}; Account const bob{"bob"}; - env.fund(drops(env.current()->fees().accountReserve(1)), alice); - env.fund(drops(env.current()->fees().accountReserve(2)), bob); + env.fund(drops(env.current()->fees().accountReserve(1, 1)), alice); + env.fund(drops(env.current()->fees().accountReserve(2, 1)), bob); env.close(); // alice gives bob permission @@ -422,7 +422,7 @@ class Delegate_test : public beast::unit_test::Suite Account const carol{"carol"}; auto const baseFee = env.current()->fees().base; - auto const reserve = env.current()->fees().accountReserve(1); + auto const reserve = env.current()->fees().accountReserve(1, 1); auto const paymentAmount = XRP(1); auto const highFee = reserve + baseFee; BEAST_EXPECT(highFee > reserve); @@ -488,9 +488,9 @@ class Delegate_test : public beast::unit_test::Suite Account const carol{"carol"}; auto const baseFee = env.current()->fees().base; - auto const baseReserve = env.current()->fees().accountReserve(0); + auto const baseReserve = env.current()->fees().accountReserve(0, 1); - env.fund(env.current()->fees().accountReserve(1) + baseFee + XRP(1), alice); + env.fund(env.current()->fees().accountReserve(1, 1) + baseFee + XRP(1), alice); env.fund(baseReserve, bob); env.fund(XRP(1000), carol); env.close(); @@ -523,7 +523,7 @@ class Delegate_test : public beast::unit_test::Suite Account const carol{"carol"}; auto const baseFee = env.current()->fees().base; - auto const reserve = env.current()->fees().accountReserve(1); + auto const reserve = env.current()->fees().accountReserve(1, 1); // Alice is funded with (reserve + baseFee): after DelegateSet she has // exactly 'reserve', which is insufficient to send XRP(10) while keeping @@ -2612,7 +2612,9 @@ class Delegate_test : public beast::unit_test::Suite {"CredentialDelete", featureCredentials}, {"NFTokenModify", featureDynamicNFT}, {"PermissionedDomainSet", featurePermissionedDomains}, - {"PermissionedDomainDelete", featurePermissionedDomains}}; + {"PermissionedDomainDelete", featurePermissionedDomains}, + {"SponsorshipSet", featureSponsor}, + }; // Can not delegate tx if any required feature disabled. { @@ -2747,7 +2749,7 @@ class Delegate_test : public beast::unit_test::Suite // DO NOT modify expectedDelegableCount unless all scenarios, including // edge cases, have been fully tested and verified. // ==================================================================== - std::size_t const expectedDelegableCount = 56; + std::size_t const expectedDelegableCount = 57; BEAST_EXPECTS( delegableCount == expectedDelegableCount, diff --git a/src/test/app/DepositAuth_test.cpp b/src/test/app/DepositAuth_test.cpp index 3496e67b54..c75bdeaf3a 100644 --- a/src/test/app/DepositAuth_test.cpp +++ b/src/test/app/DepositAuth_test.cpp @@ -51,7 +51,7 @@ namespace xrpl::test { static XRPAmount reserve(jtx::Env& env, std::uint32_t count) { - return env.current()->fees().accountReserve(count); + return env.current()->fees().accountReserve(count, 1); } // Helper function that returns true if acct has the lsfDepositAuth flag set. @@ -1025,7 +1025,7 @@ struct DepositPreauth_test : public beast::unit_test::Suite { // not enough reserve Account const john{"john"}; - env.fund(env.current()->fees().accountReserve(0), john); + env.fund(env.current()->fees().accountReserve(0, 1), john); env.close(); auto jv = deposit::authCredentials(john, {{.issuer = issuer, .credType = credType}}); diff --git a/src/test/app/EscrowToken_test.cpp b/src/test/app/EscrowToken_test.cpp index 666ee498bd..4015f5ddc8 100644 --- a/src/test/app/EscrowToken_test.cpp +++ b/src/test/app/EscrowToken_test.cpp @@ -937,7 +937,7 @@ struct EscrowToken_test : public beast::unit_test::Suite auto const expectedResult = env.current()->rules().enabled(fixCleanup3_2_0) ? Ter(tesSUCCESS) - : Ter(tefEXCEPTION); + : Ter(tefINTERNAL); env(escrow::cancel(alice, alice, seq), Fee(baseFee), expectedResult); env.close(); diff --git a/src/test/app/FlowMPT_test.cpp b/src/test/app/FlowMPT_test.cpp index 39d722da65..302e55a2cc 100644 --- a/src/test/app/FlowMPT_test.cpp +++ b/src/test/app/FlowMPT_test.cpp @@ -726,7 +726,7 @@ struct FlowMPT_test : public beast::unit_test::Suite static XRPAmount reserve(jtx::Env& env, std::uint32_t count) { - return env.current()->fees().accountReserve(count); + return env.current()->fees().accountReserve(count, 1); } // Helper function that returns the Offers on an account. diff --git a/src/test/app/Flow_test.cpp b/src/test/app/Flow_test.cpp index 72e39ab890..8d5162394e 100644 --- a/src/test/app/Flow_test.cpp +++ b/src/test/app/Flow_test.cpp @@ -706,7 +706,7 @@ struct Flow_test : public beast::unit_test::Suite static XRPAmount reserve(jtx::Env& env, std::uint32_t count) { - return env.current()->fees().accountReserve(count); + return env.current()->fees().accountReserve(count, 1); } // Helper function that returns the Offers on an account. diff --git a/src/test/app/Invariants_test.cpp b/src/test/app/Invariants_test.cpp index 79fdbb54ed..b70d099ba4 100644 --- a/src/test/app/Invariants_test.cpp +++ b/src/test/app/Invariants_test.cpp @@ -337,7 +337,72 @@ class Invariants_test : public beast::unit_test::Suite // check. sleA1->at(sfBalance) = beast::kZero; BEAST_EXPECT(sleA1->at(sfOwnerCount) == 0); - adjustOwnerCount(ac.view(), sleA1, 1, ac.journal); + increaseOwnerCount(ac.view(), sleA1, {}, 1, ac.journal); + + ac.view().erase(sleA1); + + return true; + }, + XRPAmount{}, + STTx{ttACCOUNT_DELETE, [](STObject& tx) {}}); + + doInvariantCheck( + {{"account deletion left behind a sponsorship field"}}, + [&](Account const& a1, Account const& a2, ApplyContext& ac) { + auto const sleA1 = ac.view().peek(keylet::account(a1.id())); + if (!sleA1) + return false; + sleA1->at(sfBalance) = beast::kZero; + sleA1->setFieldU32(sfSponsoredOwnerCount, 1); + + ac.view().erase(sleA1); + + return true; + }, + XRPAmount{}, + STTx{ttACCOUNT_DELETE, [](STObject& tx) {}}); + + doInvariantCheck( + {{"account deletion left behind a sponsorship field"}}, + [&](Account const& a1, Account const& a2, ApplyContext& ac) { + auto const sleA1 = ac.view().peek(keylet::account(a1.id())); + if (!sleA1) + return false; + sleA1->at(sfBalance) = beast::kZero; + sleA1->setFieldU32(sfSponsoringOwnerCount, 1); + + ac.view().erase(sleA1); + + return true; + }, + XRPAmount{}, + STTx{ttACCOUNT_DELETE, [](STObject& tx) {}}); + + doInvariantCheck( + {{"account deletion left behind a sponsorship field"}}, + [&](Account const& a1, Account const& a2, ApplyContext& ac) { + auto const a1Id = a1.id(); + auto const sleA1 = ac.view().peek(keylet::account(a1Id)); + if (!sleA1) + return false; + sleA1->at(sfBalance) = beast::kZero; + sleA1->setFieldU32(sfSponsoringAccountCount, 1); + + ac.view().erase(sleA1); + + return true; + }, + XRPAmount{}, + STTx{ttACCOUNT_DELETE, [](STObject& tx) {}}); + + doInvariantCheck( + {{"account deletion left behind a sponsorship field"}}, + [&](Account const& a1, Account const& a2, ApplyContext& ac) { + auto const sleA1 = ac.view().peek(keylet::account(a1.id())); + if (!sleA1) + return false; + sleA1->at(sfBalance) = beast::kZero; + sleA1->setAccountID(sfSponsor, a2.id()); ac.view().erase(sleA1); @@ -1771,6 +1836,7 @@ class Invariants_test : public beast::unit_test::Suite "pseudo-account sequence changed" "pseudo-account flags are not set" "pseudo-account has a regular key" + "pseudo-account has a sponsorship field" */ struct Mod { @@ -1798,6 +1864,22 @@ class Invariants_test : public beast::unit_test::Suite .expectedFailure = "pseudo-account has a regular key", .func = [](SLE::pointer& sle) { sle->at(sfRegularKey) = Account("regular").id(); }, }, + { + .expectedFailure = "pseudo-account has a sponsorship field", + .func = [](SLE::pointer& sle) { sle->at(sfSponsoredOwnerCount) = 1; }, + }, + { + .expectedFailure = "pseudo-account has a sponsorship field", + .func = [](SLE::pointer& sle) { sle->at(sfSponsoringOwnerCount) = 1; }, + }, + { + .expectedFailure = "pseudo-account has a sponsorship field", + .func = [](SLE::pointer& sle) { sle->at(sfSponsoringAccountCount) = 1; }, + }, + { + .expectedFailure = "pseudo-account has a sponsorship field", + .func = [](SLE::pointer& sle) { sle->at(sfSponsor) = Account("sponsor").id(); }, + }, }); for (auto const& mod : mods) @@ -5065,6 +5147,111 @@ class Invariants_test : public beast::unit_test::Suite } } + void + testSponsorship() + { + using namespace test::jtx; + using namespace std::string_literals; + testcase("Sponsorship"); + { + auto const expectMessage = + "SponsoredOwnerCount does not equal SponsoringOwnerCount delta."; + + doInvariantCheck( + {{expectMessage}}, [&](Account const& a1, Account const& a2, ApplyContext& ac) { + auto const sle = ac.view().peek(keylet::account(a1.id())); + if (!sle) + return false; + sle->setFieldU32(sfSponsoredOwnerCount, 1); + ac.view().update(sle); + return true; + }); + + doInvariantCheck( + {{expectMessage}}, [&](Account const& a1, Account const& a2, ApplyContext& ac) { + auto const sle = ac.view().peek(keylet::account(a1.id())); + if (!sle) + return false; + sle->setFieldU32(sfSponsoringOwnerCount, 1); + ac.view().update(sle); + return true; + }); + } + + { + auto const expectMessage = + "OwnerCount must be greater than or equal to SponsoredOwnerCount."; + + doInvariantCheck( + {{expectMessage}}, [&](Account const& a1, Account const& a2, ApplyContext& ac) { + auto const sle = ac.view().peek(keylet::account(a1.id())); + if (!sle) + return false; + sle->setFieldU32(sfOwnerCount, 0); + sle->setFieldU32(sfSponsoredOwnerCount, 1); + ac.view().update(sle); + + auto const sle2 = ac.view().peek(keylet::account(a2.id())); + if (!sle2) + return false; + sle2->setFieldU32(sfSponsoringOwnerCount, 1); + ac.view().update(sle2); + return true; + }); + } + + { + auto const expectMessage = + "SponsoredObjectOwnerCount does not equal SponsoredOwnerCount delta."; + uint256 checkID; + + doInvariantCheck( + {{expectMessage}}, + [&](Account const&, Account const& a2, ApplyContext& ac) { + auto const check = ac.view().peek(keylet::check(checkID)); + if (!check) + return false; + check->setAccountID(sfSponsor, a2.id()); + ac.view().update(check); + return true; + }, + XRPAmount{}, + STTx{ttACCOUNT_SET, [](STObject&) {}}, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, + [&checkID](Account const& a1, Account const& a2, Env& env) { + checkID = keylet::check(a1.id(), env.seq(a1)).key; + env(check::create(a1, a2, XRP(1))); + return true; + }); + } + + { + auto const expectMessage = + "Invariant failed: Net delta of SponsoringAccountCount does " + "not match net delta of sfSponsor presence."; + + doInvariantCheck( + {{expectMessage}}, [&](Account const& a1, Account const& a2, ApplyContext& ac) { + auto const sle = ac.view().peek(keylet::account(a1.id())); + if (!sle) + return false; + sle->setFieldU32(sfSponsoringAccountCount, 1); + ac.view().update(sle); + return true; + }); + + doInvariantCheck( + {{expectMessage}}, [&](Account const& a1, Account const& a2, ApplyContext& ac) { + auto const sle = ac.view().peek(keylet::account(a1.id())); + if (!sle) + return false; + sle->setAccountID(sfSponsor, a2.id()); + ac.view().update(sle); + return true; + }); + } + } + void testObjectHasPseudoAccount() { @@ -5467,6 +5654,7 @@ public: testVaultComputeCoarsestScale(); testAMM(); testObjectHasPseudoAccount(); + testSponsorship(); } }; diff --git a/src/test/app/LoanBroker_test.cpp b/src/test/app/LoanBroker_test.cpp index 2a4de18a9c..b0b0924c6e 100644 --- a/src/test/app/LoanBroker_test.cpp +++ b/src/test/app/LoanBroker_test.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -1078,7 +1079,7 @@ class LoanBroker_test : public beast::unit_test::Suite } auto const amt = - env.balance(alice) - env.current()->fees().accountReserve(env.ownerCount(alice)); + env.balance(alice) - accountReserve(*env.current(), alice.id(), env.journal); env(pay(alice, issuer, amt)); // preclaim:: tecINSUFFICIENT_RESERVE diff --git a/src/test/app/Loan_test.cpp b/src/test/app/Loan_test.cpp index 3e62af48ff..569306e920 100644 --- a/src/test/app/Loan_test.cpp +++ b/src/test/app/Loan_test.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -36,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -671,11 +673,11 @@ protected: case AssetType::MPT: { // Enough to cover initial fees if (!env.le(keylet::account(issuer))) - env.fund(env.current()->fees().accountReserve(10) * 10, issuer); + env.fund(env.current()->fees().accountReserve(10, 1) * 10, issuer); if (!env.le(keylet::account(lender))) - env.fund(env.current()->fees().accountReserve(10) * 10, noripple(lender)); + env.fund(env.current()->fees().accountReserve(10, 1) * 10, noripple(lender)); if (!env.le(keylet::account(borrower))) - env.fund(env.current()->fees().accountReserve(10) * 10, noripple(borrower)); + env.fund(env.current()->fees().accountReserve(10, 1) * 10, noripple(borrower)); MPTTester mptt{env, issuer, kMptInitNoFund}; mptt.create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock}); @@ -760,11 +762,11 @@ protected: using namespace jtx; // Enough to cover initial fees - env.fund(env.current()->fees().accountReserve(10) * 10, issuer); + env.fund(env.current()->fees().accountReserve(10, 1) * 10, issuer); if (lender != issuer) - env.fund(env.current()->fees().accountReserve(10) * 10, noripple(lender)); + env.fund(env.current()->fees().accountReserve(10, 1) * 10, noripple(lender)); if (borrower != issuer && borrower != lender) - env.fund(env.current()->fees().accountReserve(10) * 10, noripple(borrower)); + env.fund(env.current()->fees().accountReserve(10, 1) * 10, noripple(borrower)); describeLoan(env, brokerParams, loanParams, assetType, issuer, lender, borrower); @@ -841,11 +843,10 @@ protected: // Add extra for transaction fees and reserves, if appropriate, or a // tiny amount for the extra paid in each transaction auto const totalNeeded = state.totalValue + (serviceFee * state.paymentRemaining) + - (broker.asset.native() - ? Number( - baseFee * state.paymentRemaining + - env.current()->fees().accountReserve(env.ownerCount(borrower))) - : broker.asset(15).number()); + (broker.asset.native() ? Number( + baseFee * state.paymentRemaining + + accountReserve(*env.current(), borrower.id(), env.journal)) + : broker.asset(15).number()); auto const shortage = totalNeeded - borrowerBalance.number(); @@ -3071,7 +3072,7 @@ protected: auto const [acctReserve, incReserve] = [this]() -> std::pair { Env const env{*this, testableAmendments()}; return { - env.current()->fees().accountReserve(0).drops() / kDropsPerXrp.drops(), + env.current()->fees().accountReserve(0, 1).drops() / kDropsPerXrp.drops(), env.current()->fees().increment.drops() / kDropsPerXrp.drops()}; }(); @@ -4419,11 +4420,12 @@ protected: Account const lender{"lender"}; Account const issuer{"issuer"}; Account const borrower{"borrower"}; + Account const sponsor{"sponsor"}; auto const iou = issuer["IOU"]; auto testWrapper = [&](auto&& test) { Env env(*this); - env.fund(XRP(1'000), lender, issuer, borrower); + env.fund(XRP(1'000), lender, issuer, borrower, sponsor); env(trust(lender, iou(10'000'000))); env(pay(issuer, lender, iou(5'000'000))); BrokerInfo const brokerInfo{createVaultAndBroker(env, issuer["IOU"], lender)}; @@ -4438,6 +4440,15 @@ protected: BrokerInfo const& brokerInfo, jtx::Fee const& loanSetFee, Number const& debtMaximumRequest) { + for (auto const sponsorFlags : {spfSponsorReserve, spfSponsorReserve | spfSponsorFee}) + { + env(set(borrower, brokerInfo.brokerID, debtMaximumRequest), + sponsor::As(sponsor, sponsorFlags), + Sig(sfCounterpartySignature, lender), + loanSetFee, + Ter(temINVALID_FLAG)); + } + // first temBAD_SIGNER: TODO // invalid grace period { @@ -4528,8 +4539,8 @@ protected: BrokerInfo const& brokerInfo, jtx::Fee const& loanSetFee, Number const& debtMaximumRequest) { - auto const amt = env.balance(borrower) - - env.current()->fees().accountReserve(env.ownerCount(borrower)); + auto const amt = + env.balance(borrower) - accountReserve(*env.current(), borrower.id(), env.journal); env(pay(borrower, issuer, amt)); // tecINSUFFICIENT_RESERVE diff --git a/src/test/app/MPToken_test.cpp b/src/test/app/MPToken_test.cpp index bbf1c54ab1..7543db7364 100644 --- a/src/test/app/MPToken_test.cpp +++ b/src/test/app/MPToken_test.cpp @@ -2108,6 +2108,15 @@ class MPToken_test : public beast::unit_test::Suite reward = STAmount{sfSignatureReward, usd(10)}; minAmount = STAmount{sfMinAccountCreateAmount, mpt}; } + // SponsorshipSet + { + json::Value jv; + jv[jss::TransactionType] = jss::SponsorshipSet; + jv[jss::Account] = alice.human(); + jv[sfSponsee.fieldName] = carol.human(); + jv[sfFeeAmount.fieldName] = mpt.getJson(JsonOptions::Values::None); + test(jv, sfFeeAmount.fieldName.c_str()); + } } BEAST_EXPECT(txWithAmounts.empty()); } @@ -7563,7 +7572,7 @@ class MPToken_test : public beast::unit_test::Suite void testFixDoubleOwnerCount(FeatureBitset all) { - testcase("Fix Double adjustOwnerCount in AMMWithdraw"); + testcase("Fix Double OwnerCount in AMMWithdraw"); using namespace jtx; diff --git a/src/test/app/NFToken_test.cpp b/src/test/app/NFToken_test.cpp index 844bb4ee87..acd54ae26a 100644 --- a/src/test/app/NFToken_test.cpp +++ b/src/test/app/NFToken_test.cpp @@ -452,7 +452,7 @@ class NFTokenBaseUtil_test : public beast::unit_test::Suite // Just for sanity's sake we'll check that the current value // of sfMintedNFTokens matches what we expect. - auto replacement = std::make_shared(*sle, sle->key()); + auto replacement = std::make_shared(*sle); if (replacement->getFieldU32(sfMintedNFTokens) != 1) return false; // Unexpected test conditions. diff --git a/src/test/app/OfferMPT_test.cpp b/src/test/app/OfferMPT_test.cpp index 800b14877a..d03b1b8e93 100644 --- a/src/test/app/OfferMPT_test.cpp +++ b/src/test/app/OfferMPT_test.cpp @@ -59,7 +59,7 @@ class OfferMPT_test : public beast::unit_test::Suite static XRPAmount reserve(jtx::Env& env, std::uint32_t count) { - return env.current()->fees().accountReserve(count); + return env.current()->fees().accountReserve(count, 1); } static std::uint32_t @@ -1794,7 +1794,8 @@ public: // 1 for each trust limit == 3 (alice < mtgox/amazon/bitstamp) + // 1 for payment == 4 auto const base = env.current()->fees().base; - auto const startingXrp = XRP(100) + env.current()->fees().accountReserve(3) + base * 4; + auto const startingXrp = + XRP(100) + env.current()->fees().accountReserve(3, 1) + base * 4; env.fund(startingXrp, gw1, gw2, gw3, alice, bob); env.close(); @@ -1814,7 +1815,8 @@ public: env(offer(alice, usD1(200), XRP(200))); BEAST_EXPECT(env.balance(alice, usD1) == usD1(100)); - BEAST_EXPECT(env.balance(alice) == STAmount(env.current()->fees().accountReserve(3))); + BEAST_EXPECT( + env.balance(alice) == STAmount(env.current()->fees().accountReserve(3, 1))); BEAST_EXPECT(env.balance(bob, usD1) == usD1(400)); }; @@ -1865,7 +1867,7 @@ public: auto const bob = Account{"bob"}; auto const startingXrp = - XRP(100) + env.current()->fees().accountReserve(1) + env.current()->fees().base * 2; + XRP(100) + env.current()->fees().accountReserve(1, 1) + env.current()->fees().base * 2; env.fund(startingXrp, gw, alice, bob); @@ -1884,7 +1886,7 @@ public: jrr = ledgerEntryRoot(env, alice); BEAST_EXPECT( jrr[jss::node][sfBalance.fieldName] == - STAmount(env.current()->fees().accountReserve(1)).getText()); + STAmount(env.current()->fees().accountReserve(1, 1)).getText()); jrr = ledgerEntryMPT(env, bob, usd); BEAST_EXPECT(jrr[jss::node][sfMPTAmount.fieldName] == "400"); @@ -1904,7 +1906,7 @@ public: auto const bob = Account{"bob"}; auto const startingXrp = - XRP(100) + env.current()->fees().accountReserve(1) + env.current()->fees().base * 2; + XRP(100) + env.current()->fees().accountReserve(1, 1) + env.current()->fees().base * 2; env.fund(startingXrp, gw, alice, bob); @@ -1925,7 +1927,7 @@ public: jrr = ledgerEntryRoot(env, alice); BEAST_EXPECT( jrr[jss::node][sfBalance.fieldName] == - STAmount(env.current()->fees().accountReserve(1)).getText()); + STAmount(env.current()->fees().accountReserve(1, 1)).getText()); jrr = ledgerEntryMPT(env, bob, usd); BEAST_EXPECT(jrr[jss::node][sfMPTAmount.fieldName] == "300"); @@ -1946,7 +1948,7 @@ public: auto const base = env.current()->fees().base; auto const startingXrp = - XRP(100.1) + env.current()->fees().accountReserve(1) + base * 2; + XRP(100.1) + env.current()->fees().accountReserve(1, 1) + base * 2; env.fund(startingXrp, gw, alice, bob); env.close(); diff --git a/src/test/app/Offer_test.cpp b/src/test/app/Offer_test.cpp index 2724a6474b..7fc7161e36 100644 --- a/src/test/app/Offer_test.cpp +++ b/src/test/app/Offer_test.cpp @@ -60,7 +60,7 @@ class OfferBaseUtil_test : public beast::unit_test::Suite static XRPAmount reserve(jtx::Env& env, std::uint32_t count) { - return env.current()->fees().accountReserve(count); + return env.current()->fees().accountReserve(count, 1); } static std::uint32_t @@ -1962,7 +1962,7 @@ public: // 1 for each trust limit == 3 (alice < mtgox/amazon/bitstamp) + // 1 for payment == 4 auto const startingXrp = - XRP(100) + env.current()->fees().accountReserve(3) + env.current()->fees().base * 4; + XRP(100) + env.current()->fees().accountReserve(3, 1) + env.current()->fees().base * 4; env.fund(startingXrp, gw1, gw2, gw3, alice, bob); env.close(); @@ -1985,7 +1985,7 @@ public: jrr = ledgerEntryRoot(env, alice); BEAST_EXPECT( jrr[jss::node][sfBalance.fieldName] == - STAmount(env.current()->fees().accountReserve(3)).getText()); + STAmount(env.current()->fees().accountReserve(3, 1)).getText()); jrr = ledgerEntryState(env, bob, gw1, "USD"); BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-400"); @@ -2045,7 +2045,7 @@ public: auto const usd = gw["USD"]; auto const startingXrp = - XRP(100) + env.current()->fees().accountReserve(1) + env.current()->fees().base * 2; + XRP(100) + env.current()->fees().accountReserve(1, 1) + env.current()->fees().base * 2; env.fund(startingXrp, gw, alice, bob); env.close(); @@ -2066,7 +2066,7 @@ public: jrr = ledgerEntryRoot(env, alice); BEAST_EXPECT( jrr[jss::node][sfBalance.fieldName] == - STAmount(env.current()->fees().accountReserve(1)).getText()); + STAmount(env.current()->fees().accountReserve(1, 1)).getText()); jrr = ledgerEntryState(env, bob, gw, "USD"); BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-400"); @@ -2087,7 +2087,7 @@ public: auto const usd = gw["USD"]; auto const startingXrp = - XRP(100) + env.current()->fees().accountReserve(1) + env.current()->fees().base * 2; + XRP(100) + env.current()->fees().accountReserve(1, 1) + env.current()->fees().base * 2; env.fund(startingXrp, gw, alice, bob); env.close(); @@ -2110,7 +2110,7 @@ public: jrr = ledgerEntryRoot(env, alice); BEAST_EXPECT( jrr[jss::node][sfBalance.fieldName] == - STAmount(env.current()->fees().accountReserve(1)).getText()); + STAmount(env.current()->fees().accountReserve(1, 1)).getText()); jrr = ledgerEntryState(env, bob, gw, "USD"); BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-300"); @@ -2131,8 +2131,8 @@ public: auto const xts = gw["XTS"]; auto const xxx = gw["XXX"]; - auto const startingXrp = - XRP(100.1) + env.current()->fees().accountReserve(1) + env.current()->fees().base * 2; + auto const startingXrp = XRP(100.1) + env.current()->fees().accountReserve(1, 1) + + env.current()->fees().base * 2; env.fund(startingXrp, gw, alice, bob); env.close(); diff --git a/src/test/app/Oracle_test.cpp b/src/test/app/Oracle_test.cpp index 7e7f5e9bd0..b8d9fb32d1 100644 --- a/src/test/app/Oracle_test.cpp +++ b/src/test/app/Oracle_test.cpp @@ -60,7 +60,7 @@ private: // Insufficient reserve { Env env(*this); - env.fund(env.current()->fees().accountReserve(0), owner); + env.fund(env.current()->fees().accountReserve(0, 1), owner); Oracle const oracle( env, {.owner = owner, @@ -71,7 +71,7 @@ private: { Env env(*this); env.fund( - env.current()->fees().accountReserve(1) + env.current()->fees().base * 2, owner); + env.current()->fees().accountReserve(1, 1) + env.current()->fees().base * 2, owner); Oracle oracle( env, {.owner = owner, .fee = static_cast(env.current()->fees().base.drops())}); BEAST_EXPECT(oracle.exists()); @@ -640,7 +640,7 @@ private: Env env(*this); auto const baseFee = static_cast(env.current()->fees().base.drops()); env.fund( - env.current()->fees().accountReserve(1) + env.current()->fees().base * 2, owner); + env.current()->fees().accountReserve(1, 1) + env.current()->fees().base * 2, owner); Oracle oracle(env, {.owner = owner, .fee = baseFee}); oracle.set(UpdateArg{.series = {{"XRP", "USD", 742, 2}}, .fee = baseFee}); } diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp new file mode 100644 index 0000000000..f20aac68f9 --- /dev/null +++ b/src/test/app/Sponsor_test.cpp @@ -0,0 +1,5511 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl::test { + +static STAmount +accountReserve(jtx::Env& env, std::uint32_t count = 1) +{ + return env.current()->fees().reserve * count; +} + +static STAmount +reserve(jtx::Env& env, std::uint32_t count) +{ + return env.current()->fees().accountReserve(count, 1); +} + +static void +adjustAccountXRPBalance(jtx::Env& env, jtx::Account const& account, STAmount const& balanceTo) +{ + using namespace test::jtx; + XRPL_ASSERT(isXRP(balanceTo), "adjustAccountXRPBalance: balanceTo must be XRP"); + auto const currentBalance = env.balance(account); + if (currentBalance == balanceTo) + return; + + auto const baseFee = env.current()->fees().base; + if (currentBalance > balanceTo) + { + env(pay(account, env.master, currentBalance - balanceTo), + Fee(XRP(1)), + sponsor::As(env.master, spfSponsorFee), + Sig(sfSponsorSignature, env.master)); + } + else + { + env(pay(env.master, account, balanceTo - currentBalance), Fee(baseFee)); + } + + env.close(); +} + +class Sponsor_test : public beast::unit_test::Suite +{ +public: + void + testDisabled() + { + testcase("Disabled"); + using namespace test::jtx; + Env env{*this, testableAmendments() - featureSponsor}; + Account const alice("alice"); + Account const sponsor("sponsor"); + env.fund(XRP(10000), alice, sponsor); + + // check Sponsor fields + auto const jt = noop(alice); + auto jt1 = jt; + jt1[sfSponsor.jsonName] = sponsor.human(); + env(jt1, Ter(temDISABLED)); + env(jt, Sig(sfSponsorSignature, sponsor), Ter(temDISABLED)); + + auto jt2 = jt; + jt2[sfSponsorFlags.jsonName] = spfSponsorFee | spfSponsorReserve; + env(jt2, Ter(temDISABLED)); + + // check Sponsor transactions + env(sponsor::transfer(alice, 0), Ter(temDISABLED)); + env(sponsor::set(sponsor, 0), Ter(temDISABLED)); + } + + void + testInvalidSponsorshipSet() + { + testcase("Invalid SponsorshipSet"); + using namespace test::jtx; + Env env{*this, testableAmendments()}; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + Account const noFunded("noFunded"); + Account const gw("gw"); + + auto const usd = gw["usd"]; + env.fund(XRP(10000), alice, sponsor, gw); + env.close(); + + // + // preflight + // + + // Invalid flags + { + env(sponsor::set(sponsor, ~tfSponsorshipSetMask - tfInnerBatchTxn), + sponsor::SponseeAcc(alice), + Ter(temINVALID_FLAG)); + + env(sponsor::set( + sponsor, + tfSponsorshipSetRequireSignForFee | tfSponsorshipClearRequireSignForFee), + sponsor::SponseeAcc(alice), + Ter(temINVALID_FLAG)); + + env(sponsor::set( + sponsor, + tfSponsorshipSetRequireSignForReserve | + tfSponsorshipClearRequireSignForReserve), + sponsor::SponseeAcc(alice), + Ter(temINVALID_FLAG)); + + for (auto flag : + {tfSponsorshipSetRequireSignForFee, + tfSponsorshipClearRequireSignForFee, + tfSponsorshipSetRequireSignForReserve, + tfSponsorshipClearRequireSignForReserve}) + { + env(sponsor::set(sponsor, tfDeleteObject | flag), + sponsor::SponseeAcc(alice), + Ter(temINVALID_FLAG)); + } + } + + // invalid SponsorAccount / Sponsee + // Account = Sponsor + env(sponsor::set(alice, tfDeleteObject), + sponsor::CounterpartySponsor(alice), + Ter(temMALFORMED)); + // Account = Sponsee + env(sponsor::set(alice, tfDeleteObject), sponsor::SponseeAcc(alice), Ter(temMALFORMED)); + // Both Sponsor and Sponsee are specified + env(sponsor::set(alice, 0), + sponsor::CounterpartySponsor(sponsor), + sponsor::SponseeAcc(alice), + Ter(temMALFORMED)); + + // Invalid feeAmount + for (auto const& amt : {XRP(-1), usd(1)}) + { + env(sponsor::set_fee(sponsor, 0, amt), sponsor::SponseeAcc(alice), Ter(temBAD_AMOUNT)); + } + // Invalid MaxFee + for (auto const& amt : {XRP(-1), usd(1)}) + { + env(sponsor::set_fee(sponsor, 0, XRP(1), amt), + sponsor::SponseeAcc(alice), + Ter(temBAD_AMOUNT)); + } + + // Invalid Delete operation + env(sponsor::set_reserve(sponsor, tfDeleteObject, 1), + sponsor::SponseeAcc(alice), + Ter(temMALFORMED)); + env(sponsor::set_fee(sponsor, tfDeleteObject, XRP(1)), + sponsor::SponseeAcc(alice), + Ter(temMALFORMED)); + env(sponsor::set_max_fee(sponsor, tfDeleteObject, XRP(1)), + sponsor::SponseeAcc(alice), + Ter(temMALFORMED)); + + // Invalid SponsorAccount with non-Delete operation + env(sponsor::set_reserve(sponsor, 0, 100), + sponsor::CounterpartySponsor(alice), + Ter(temMALFORMED)); + env(sponsor::set_fee(sponsor, 0, XRP(1), XRP(1)), + sponsor::CounterpartySponsor(alice), + Ter(temMALFORMED)); + + // + // preclaim + // + + // Invalid Sponsee + env(sponsor::set(sponsor, 0), sponsor::SponseeAcc(noFunded), Ter(tecNO_DST)); + env.close(); + + // Invalid Sponsor + env(sponsor::set(sponsor, tfDeleteObject), + sponsor::CounterpartySponsor(noFunded), + Ter(tecNO_DST)); + env.close(); + + // Invalid Delete operation (sponsorship not found) + env(sponsor::set(sponsor, tfDeleteObject), sponsor::SponseeAcc(alice), Ter(tecNO_ENTRY)); + env.close(); + + // insufficient balance to sponsor Fee + adjustAccountXRPBalance(env, sponsor, env.current()->fees().reserve); + env(sponsor::set_fee(sponsor, 0, XRP(4)), sponsor::SponseeAcc(alice), Ter(tecUNFUNDED)); + env.close(); + + // insufficient reserve to create sponsorship + adjustAccountXRPBalance(env, sponsor, XRP(100) + XRP(1) + reserve(env, 1) - drops(1)); + env(sponsor::set(sponsor, 0, 100, XRP(100)), + sponsor::SponseeAcc(alice), + Fee(XRP(1)), + Ter(tecUNFUNDED)); + env.close(); + + // FeeAmount + Fee > Balance + // Balance = 1000XRP, FeeAmount = 1001XRP + adjustAccountXRPBalance(env, sponsor, XRP(1000)); + env(sponsor::set_fee(sponsor, 0, XRP(1001)), + sponsor::SponseeAcc(alice), + Fee(XRP(1)), + Ter(tecUNFUNDED)); + env.close(); + // Balance = 1000XRP, FeeAmount = 999XRP, Fee=2XRP + adjustAccountXRPBalance(env, sponsor, XRP(1000)); + env(sponsor::set_fee(sponsor, 0, XRP(999)), + sponsor::SponseeAcc(alice), + Fee(XRP(2)), + Ter(tecUNFUNDED)); + env.close(); + + // create sponsor to use above tests + // need feeAmount(1000) + Fee(1) + reserve(~250) = ~1251 + adjustAccountXRPBalance(env, sponsor, XRP(1000) + XRP(1) + reserve(env, 1)); + env(sponsor::set(sponsor, 0, 100, XRP(1000)), + sponsor::SponseeAcc(alice), + Fee(XRP(1)), + Ter(tesSUCCESS)); + env.close(); + + // delta-based balance check + // After create: sponsor balance ~ 0, feeAmount = XRP(1000) + + // Decreasing feeAmount should succeed (refund, negative delta) + adjustAccountXRPBalance(env, sponsor, XRP(500)); + env(sponsor::set_fee(sponsor, 0, XRP(800)), + sponsor::SponseeAcc(alice), + Fee(XRP(1)), + Ter(tesSUCCESS)); + env.close(); + // balance was 500, delta = 800-1000 = -200 (refund), balance = 500+200-1 = 699 + + // Increasing feeAmount within delta budget should succeed + adjustAccountXRPBalance(env, sponsor, XRP(500)); + env(sponsor::set_fee(sponsor, 0, XRP(850)), + sponsor::SponseeAcc(alice), + Fee(XRP(1)), + Ter(tesSUCCESS)); + env.close(); + // balance was 500, delta = 850-800 = 50, balance = 500-50-1 = 449 + + // Increasing feeAmount where delta exceeds balance should fail + adjustAccountXRPBalance(env, sponsor, XRP(310)); + env(sponsor::set_fee(sponsor, 0, XRP(1200)), + sponsor::SponseeAcc(alice), + Fee(XRP(1)), + Ter(tecUNFUNDED)); + env.close(); + + // Increasing feeAmount to reach insufficient reserve + auto const currentFeeAmount = env.le(keylet::sponsorship(sponsor.id(), alice.id())) + ->getFieldAmount(sfFeeAmount) + .xrp(); + adjustAccountXRPBalance(env, sponsor, XRP(310)); + env(sponsor::set_fee(sponsor, 0, currentFeeAmount + XRP(309)), + sponsor::SponseeAcc(alice), + Fee(XRP(1)), + Ter(tecUNFUNDED)); + env.close(); + } + + void + testPseudoAccountSponsorship() + { + testcase("Pseudo account sponsorship"); + using namespace test::jtx; + Env env{*this, testableAmendments()}; + Account const alice("alice"); + Account const bob("bob"); + Account const gw("gw"); + Account const sp("sponsor"); + + Asset const asset = gw["IOU"].asset(); + + env.fund(XRP(1000000), alice, bob, gw, sp); + env.close(); + + // Create a vault to get a pseudo account + Vault const vault{env}; + auto [tx, keylet] = vault.create({.owner = alice, .asset = asset}); + env(tx); + env.close(); + + auto const vaultSle = env.le(keylet); + BEAST_EXPECT(vaultSle); + Account const pseudoAcc("vault", vaultSle->getAccountID(sfAccount)); + env.memoize(pseudoAcc); + + // Sponsee is a pseudo account -> tecNO_PERMISSION + env(sponsor::set(sp, 0, 100, XRP(100)), + sponsor::SponseeAcc(pseudoAcc), + Ter(tecNO_PERMISSION)); + env.close(); + + // Sponsor is a pseudo account -> tecNO_PERMISSION + // (submitted by bob with counterpartySponsor pointing to pseudo account) + env(sponsor::set(bob, tfDeleteObject), + sponsor::CounterpartySponsor(pseudoAcc), + Ter(tecNO_PERMISSION)); + env.close(); + } + + void + testSingleSigning() + { + testcase("Single signing"); + using namespace test::jtx; + Env env{*this, testableAmendments()}; + Account const alice("alice"); + Account const sponsor("sponsor"); + Account const invalid("invalid"); + + env.fund(XRP(10000), alice, sponsor); + env.close(); + + // Signature doesn't exist + auto tx = noop(alice); + tx[sfSponsor.jsonName] = sponsor.human(); + tx[sfSponsorSignature.jsonName][sfSigningPubKey.jsonName] = strHex(sponsor.pk().slice()); + + env(tx, Fee(XRP(1)), sponsor::As(sponsor, spfSponsorReserve), Ter(telENV_RPC_FAILED)); + + // Invalid signature + tx[sfSponsorSignature.jsonName][sfTxnSignature.jsonName] = "DEADBEEF"; + env(tx, Fee(XRP(1)), sponsor::As(sponsor, spfSponsorReserve), Ter(telENV_RPC_FAILED)); + + // Signer account doesn't exist + env(noop(alice), + Fee(XRP(1)), + sponsor::As(invalid, spfSponsorReserve), + Sig(sfSponsorSignature, invalid), + Ter(terNO_ACCOUNT)); + + // Success + env(noop(alice), + Fee(XRP(1)), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor), + Ter(tesSUCCESS)); + } + + void + testMultiSigning() + { + testcase("Multi signing"); + using namespace test::jtx; + Env env{*this, testableAmendments()}; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + Account const signer1("signer1"); + Account const signer2("signer2"); + + env.fund(XRP(10000), alice, bob, sponsor); + env.close(); + + env(signers(sponsor, 1, {{signer1, 1}, {signer2, 1}})); + env.close(); + + // Invalid signature + auto tx = noop(alice); + auto& signers1 = tx[sfSponsorSignature.jsonName][sfSigners.jsonName][0U][sfSigner.jsonName]; + signers1[sfAccount.jsonName] = signer1.human(); + signers1[sfSigningPubKey.jsonName] = strHex(signer1.pk().slice()); + signers1[sfTxnSignature.jsonName] = "DEADBEEF"; + env(tx, Fee(XRP(1)), sponsor::As(sponsor, spfSponsorReserve), Ter(telENV_RPC_FAILED)); + + // bob is not a multi-signing account. + env(noop(alice), + Fee(XRP(1)), + sponsor::As(bob, spfSponsorReserve), + Msig(sfSponsorSignature, {signer1}), + Ter(tefNOT_MULTI_SIGNING)); + + env(noop(alice), + Fee(XRP(1)), + sponsor::As(sponsor, spfSponsorReserve), + Msig(sfSponsorSignature, {signer1}), + Ter(tesSUCCESS)); + env.close(); + + env(signers(sponsor, 2, {{signer1, 1}, {signer2, 1}})); + env.close(); + + // test calculateBaseFee for multisigned sponsor + auto const baseFee = env.current()->fees().base; + env(noop(alice), + Fee(baseFee + 2 * baseFee - 1), + sponsor::As(sponsor, spfSponsorReserve), + Msig(sfSponsorSignature, {signer1, signer2}), + Ter(telINSUF_FEE_P)); + + env(noop(alice), + Fee(baseFee + 2 * baseFee), + sponsor::As(sponsor, spfSponsorReserve), + Msig(sfSponsorSignature, {signer1, signer2}), + Ter(tesSUCCESS)); + } + + void + testInvalidSponsorField() + { + testcase("Invalid Sponsor Field"); + using namespace test::jtx; + Env env{*this, testableAmendments()}; + Account const alice("alice"); + Account const sponsor("sponsor"); + Account const noFunded("noFunded"); + env.fund(XRP(10000), alice, sponsor); + env.close(); + + // Invalid Sponsor Account (Account = Sponsor.Account) + env(noop(alice), sponsor::As(alice, spfSponsorFee), Ter(temMALFORMED)); + + // Invalid Sponsor Account + // (SponsorSignature is specified but Sponsor.Account is not specified) + env(noop(alice), Sig(sfSponsorSignature, sponsor), Ter(temMALFORMED)); + + // Invalid Sponsor Account (Sponsor.Account doesn't exist) + env(noop(alice), sponsor::As(noFunded, spfSponsorReserve), Ter(terNO_ACCOUNT)); + env(noop(alice), + sponsor::As(noFunded, spfSponsorReserve), + Sig(sfSponsorSignature, noFunded), + Ter(terNO_ACCOUNT)); + + // Invalid Flags + env(noop(alice), + sponsor::As(sponsor, (spfSponsorFee | spfSponsorReserve) + 1), + Ter(temINVALID_FLAG)); + + // SponsorFlags=0 with valid sponsor (no sponsorship purpose) + env(noop(alice), sponsor::As(sponsor, 0), Ter(temINVALID_FLAG)); + + // no SponsorFlag with valid sponsor + auto tx = noop(alice); + tx[sfSponsor.jsonName] = sponsor.human(); + env(tx, Ter(temINVALID_FLAG)); + + // Invalid Flags without sponsor + tx = noop(alice); + tx[sfSponsorFlags.jsonName] = spfSponsorFee | spfSponsorReserve; + env(tx, Ter(temINVALID_FLAG)); + } + + void + testSimpleSponsorshipSet() + { + testcase("Simple SponsorshipSet"); + using namespace test::jtx; + Env env{*this, testableAmendments()}; + Account const alice("alice"); + Account const sponsor("sponsor"); + env.fund(XRP(10000), alice, sponsor); + env.close(); + + { + // create sponsorship + env(sponsor::set( + sponsor, + tfSponsorshipSetRequireSignForFee | tfSponsorshipSetRequireSignForReserve, + 100, + XRP(100), + XRP(1)), + Fee(XRP(1)), + sponsor::SponseeAcc(alice), + Ter(tesSUCCESS)); + env.close(); + + auto sle = env.le(keylet::sponsorship(sponsor, alice)); + BEAST_EXPECT(sle); + BEAST_EXPECT(sle->at(sfRemainingOwnerCount) == 100); + BEAST_EXPECT(sle->at(sfFeeAmount) == XRP(100)); + BEAST_EXPECT(sle->at(sfMaxFee) == XRP(1)); + BEAST_EXPECT(sle->isFlag(lsfSponsorshipRequireSignForFee)); + BEAST_EXPECT(sle->isFlag(lsfSponsorshipRequireSignForReserve)); + BEAST_EXPECT(env.balance(sponsor) == XRP(10000) - sle->at(sfFeeAmount) - XRP(1)); + + // update sponsorship (decrement) + env(sponsor::set(sponsor, 0, 50, XRP(50), XRP(0.5)), + sponsor::SponseeAcc(alice), + Fee(XRP(1)), + Ter(tesSUCCESS)); + env.close(); + + sle = env.le(keylet::sponsorship(sponsor, alice)); + BEAST_EXPECT(sle); + BEAST_EXPECT(sle->at(sfRemainingOwnerCount) == 50); + BEAST_EXPECT(sle->at(sfFeeAmount) == XRP(50)); + BEAST_EXPECT(sle->at(sfMaxFee) == XRP(0.5)); + BEAST_EXPECT(env.balance(sponsor) == XRP(10000) - sle->at(sfFeeAmount) - XRP(2)); + + // update sponsorship (increment) + env(sponsor::set(sponsor, 0, 200, XRP(200), XRP(2)), + sponsor::SponseeAcc(alice), + Fee(XRP(1)), + Ter(tesSUCCESS)); + env.close(); + + sle = env.le(keylet::sponsorship(sponsor, alice)); + BEAST_EXPECT(sle); + BEAST_EXPECT(sle->at(sfRemainingOwnerCount) == 200); + BEAST_EXPECT(sle->at(sfFeeAmount) == XRP(200)); + BEAST_EXPECT(sle->at(sfMaxFee) == XRP(2)); + BEAST_EXPECT(env.balance(sponsor) == XRP(10000) - sle->at(sfFeeAmount) - XRP(3)); + + // delete from sponsor + env(sponsor::del(sponsor), sponsor::SponseeAcc(alice), Fee(XRP(1)), Ter(tesSUCCESS)); + env.close(); + + BEAST_EXPECT(env.balance(sponsor) == XRP(10000) - XRP(4)); + + env(sponsor::set( + sponsor, + tfSponsorshipSetRequireSignForFee | tfSponsorshipSetRequireSignForReserve, + 100, + XRP(100), + XRP(1)), + sponsor::SponseeAcc(alice), + Ter(tesSUCCESS)); + env.close(); + + // delete from sponsee + env(sponsor::del(alice), sponsor::CounterpartySponsor(sponsor), Ter(tesSUCCESS)); + env.close(); + BEAST_EXPECT(!env.le(keylet::sponsorship(sponsor, alice))); + + // Cannot create sponsorship with no fee or reserve budget. MaxFee + // and flags do not make a sponsorship object useful by themselves. + env(sponsor::set(sponsor, 0), sponsor::SponseeAcc(alice), Ter(tecNO_PERMISSION)); + env.close(); + BEAST_EXPECT(!env.le(keylet::sponsorship(sponsor, alice))); + + env(sponsor::set_max_fee(sponsor, 0, XRP(1)), + sponsor::SponseeAcc(alice), + Ter(tecNO_PERMISSION)); + env.close(); + BEAST_EXPECT(!env.le(keylet::sponsorship(sponsor, alice))); + + env(sponsor::set(sponsor, 0, 0, XRP(0), XRP(0)), + sponsor::SponseeAcc(alice), + Ter(tecNO_PERMISSION)); + env.close(); + BEAST_EXPECT(!env.le(keylet::sponsorship(sponsor, alice))); + + // update sponsorship with non-zero value + env(sponsor::set(sponsor, 0, 100, XRP(100), XRP(1)), + sponsor::SponseeAcc(alice), + Fee(XRP(1))); + env.close(); + + sle = env.le(keylet::sponsorship(sponsor, alice)); + BEAST_EXPECT(sle); + BEAST_EXPECT(sle->at(sfRemainingOwnerCount) == 100); + BEAST_EXPECT(sle->at(sfFeeAmount) == XRP(100)); + BEAST_EXPECT(sle->at(sfMaxFee) == XRP(1)); + + // update sponsorship flags + auto testFlagUpdate = [&](auto setFlag, auto clearFlag, auto ledgerFlag) { + env(sponsor::set(sponsor, setFlag), sponsor::SponseeAcc(alice), Fee(XRP(1))); + env.close(); + + sle = env.le(keylet::sponsorship(sponsor, alice)); + BEAST_EXPECT(sle); + BEAST_EXPECT(sle->isFlag(ledgerFlag)); + + env(sponsor::set(sponsor, clearFlag), sponsor::SponseeAcc(alice), Fee(XRP(1))); + env.close(); + + sle = env.le(keylet::sponsorship(sponsor, alice)); + BEAST_EXPECT(sle); + BEAST_EXPECT(!sle->isFlag(ledgerFlag)); + }; + + testFlagUpdate( + tfSponsorshipSetRequireSignForFee, + tfSponsorshipClearRequireSignForFee, + lsfSponsorshipRequireSignForFee); + testFlagUpdate( + tfSponsorshipSetRequireSignForReserve, + tfSponsorshipClearRequireSignForReserve, + lsfSponsorshipRequireSignForReserve); + + // Cannot update sponsorship so both fee and reserve budgets are absent. + env(sponsor::set(sponsor, 0, 0, XRP(0), XRP(0)), + sponsor::SponseeAcc(alice), + Fee(XRP(1)), + Ter(tecNO_PERMISSION)); + env.close(); + + sle = env.le(keylet::sponsorship(sponsor, alice)); + BEAST_EXPECT(sle); + BEAST_EXPECT(sle->at(sfRemainingOwnerCount) == 100); + BEAST_EXPECT(sle->at(sfFeeAmount) == XRP(100)); + BEAST_EXPECT(sle->at(sfMaxFee) == XRP(1)); + } + + { + // Removing one budget field while the other remains keeps the + // Sponsorship valid. Starting state (from above): + // RemainingOwnerCount = 100, FeeAmount = XRP(100). + + // Remove only FeeAmount (set to 0); RemainingOwnerCount remains. + env(sponsor::set_fee(sponsor, 0, XRP(0)), + sponsor::SponseeAcc(alice), + Fee(XRP(1)), + Ter(tesSUCCESS)); + env.close(); + + auto sle = env.le(keylet::sponsorship(sponsor, alice)); + BEAST_EXPECT(sle); + BEAST_EXPECT(!sle->isFieldPresent(sfFeeAmount)); + BEAST_EXPECT(sle->at(sfRemainingOwnerCount) == 100); + + // Re-add FeeAmount, then remove only RemainingOwnerCount; + // FeeAmount remains. + env(sponsor::set_fee(sponsor, 0, XRP(100)), + sponsor::SponseeAcc(alice), + Fee(XRP(1)), + Ter(tesSUCCESS)); + env.close(); + + env(sponsor::set_reserve(sponsor, 0, 0), + sponsor::SponseeAcc(alice), + Fee(XRP(1)), + Ter(tesSUCCESS)); + env.close(); + + sle = env.le(keylet::sponsorship(sponsor, alice)); + BEAST_EXPECT(sle); + BEAST_EXPECT(!sle->isFieldPresent(sfRemainingOwnerCount)); + BEAST_EXPECT(sle->at(sfFeeAmount) == XRP(100)); + } + + { + // Update Sponsorship (FeeAmount) + // set empty FeeAmount + env(sponsor::set_reserve(sponsor, 0, 100), sponsor::SponseeAcc(alice), Ter(tesSUCCESS)); + env.close(); + + // add FeeAmount + env(sponsor::set_fee(sponsor, 0, XRP(100)), + sponsor::SponseeAcc(alice), + Ter(tesSUCCESS)); + env.close(); + + env(sponsor::del(alice), sponsor::CounterpartySponsor(sponsor), Ter(tesSUCCESS)); + env.close(); + } + { + // Update Sponsorship (ReserveCount) + // set empty ReserveCount + env(sponsor::set_fee(sponsor, 0, XRP(100)), + sponsor::SponseeAcc(alice), + Ter(tesSUCCESS)); + env.close(); + + // add ReserveCount + env(sponsor::set_reserve(sponsor, 0, 100), sponsor::SponseeAcc(alice), Ter(tesSUCCESS)); + env.close(); + + env(sponsor::del(alice), sponsor::CounterpartySponsor(sponsor), Ter(tesSUCCESS)); + env.close(); + } + { + // delete Sponsorship (only with FeeAmount) + env(sponsor::set_fee(sponsor, 0, XRP(100)), + sponsor::SponseeAcc(alice), + Ter(tesSUCCESS)); + env.close(); + + env(sponsor::del(alice), sponsor::CounterpartySponsor(sponsor), Ter(tesSUCCESS)); + env.close(); + } + { + // delete Sponsorship (only with ReserveCount) + env(sponsor::set_reserve(sponsor, 0, 100), sponsor::SponseeAcc(alice), Ter(tesSUCCESS)); + env.close(); + + env(sponsor::del(alice), sponsor::CounterpartySponsor(sponsor), Ter(tesSUCCESS)); + env.close(); + } + } + + void + testPreFundAndCosign() + { + testcase("PreFund and Cosign"); + using namespace test::jtx; + Account const alice("alice"); + Account const bob("bob"); + Account const charlie("charlie"); + Account const sponsor("sponsor"); + + { + // Both pre-funded and co-signed; the pre-funded value is used. + Env env{*this, testableAmendments()}; + env.fund(XRP(10000), alice, bob, sponsor); + env.close(); + + env(sponsor::set(sponsor, 0, 100, XRP(100), XRP(1)), + sponsor::SponseeAcc(alice), + Ter(tesSUCCESS)); + env.close(); + + auto const checkSeq = env.seq(alice); + env(check::create(alice, bob, XRP(1)), + sponsor::As(sponsor, spfSponsorReserve | spfSponsorFee), + Sig(sfSponsorSignature, sponsor), + Fee(XRP(1)), + Ter(tesSUCCESS)); + env.close(); + + auto sle = env.le(keylet::sponsorship(sponsor, alice)); + BEAST_EXPECT(sle); + BEAST_EXPECT(sle->at(sfRemainingOwnerCount) == 99); + BEAST_EXPECT(sle->at(sfFeeAmount) == XRP(99)); + + env(check::cancel(alice, keylet::check(alice, checkSeq).key), Ter(tesSUCCESS)); + env.close(); + + sle = env.le(keylet::sponsorship(sponsor, alice)); + BEAST_EXPECT(sle); + BEAST_EXPECT(sle->at(sfRemainingOwnerCount) == 99); // not restored + BEAST_EXPECT(sle->at(sfFeeAmount) == XRP(99)); + } + + { + // if pre-funded value is not enough, error + Env env{*this, testableAmendments()}; + env.fund(XRP(10000), alice, bob, charlie, sponsor); + env.close(); + + env(sponsor::set(sponsor, 0, 1, XRP(10), XRP(100)), + sponsor::SponseeAcc(alice), + Ter(tesSUCCESS)); + env.close(); + + // Fee insufficient + env(check::create(alice, bob, XRP(1)), + sponsor::As(sponsor, spfSponsorReserve | spfSponsorFee), + Sig(sfSponsorSignature, sponsor), + Fee(XRP(11)), + Ter(terINSUF_FEE_B)); + env.close(); + + env(sponsor::set_reserve(sponsor, 0, 0), sponsor::SponseeAcc(alice), Ter(tesSUCCESS)); + env.close(); + + // reserve insufficient + env(check::create(alice, bob, XRP(1)), + sponsor::As(sponsor, spfSponsorReserve | spfSponsorFee), + Sig(sfSponsorSignature, sponsor), + Fee(XRP(1)), + Ter(tecINSUFFICIENT_RESERVE)); + env.close(); + } + } + + void + testSponsoredFreeTierReserve() + { + testcase("Sponsored Free-Tier Reserve"); + using namespace test::jtx; + Account const alice("alice"); + Account const issuer("issuer"); + Account const sponsor("sponsor"); + + // Trust lines and MPTokens normally skip the reserve check when the + // holder's ownerCount < 2 (the "free-tier" / first-two-items shortcut). When the + // tx is sponsored, that shortcut must not apply — the sponsor must + // still cover the reserve. + Env env{*this, testableAmendments()}; + env.fund(XRP(10000), alice, issuer); + // Sponsor is funded just below the reserve required to cover a single + // sponsored item. + env.fund(reserve(env, 1) - drops(1), sponsor); + env.close(); + BEAST_EXPECT(ownerCount(env, alice) == 0); + + MPTTester mptt(env, issuer, {.fund = false}); + mptt.create(); + + // Free-tier trust line cosigned by an undercapitalized sponsor must + // fail — the holder's free-first-two-items shortcut does not let the + // sponsor skip the reserve check. + env(trust(alice, issuer["USD"](100)), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor), + Ter(tecNO_LINE_INSUF_RESERVE)); + env.close(); + + // Free-tier MPTokenAuthorize must also fail for the same reason. + env(MPTTester::authorizeJV({.account = alice, .id = mptt.issuanceID()}), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor), + Ter(tecINSUFFICIENT_RESERVE)); + env.close(); + } + + void + testTransferSponsor() + { + testcase("Transfer Sponsor"); + using namespace test::jtx; + + // Verify preflight checks + { + Env env{*this, testableAmendments()}; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + env.fund(XRP(10000), alice, bob, sponsor); + env.close(); + + // Test invalid flags for SponsorshipTransfer + { + // Invalid flag is provided + env(sponsor::transfer( + alice, + (tfSponsorshipCreate | tfSponsorshipReassign | tfSponsorshipEnd) + 1), + Ter(temINVALID_FLAG)); + + // No SponsorshipTransfer flag is provided + env(sponsor::transfer(alice, 0), Ter(temINVALID_FLAG)); + + // Only one of the three valid flags can be set. + // Setting more than one flag is invalid + for (auto flag : { + tfSponsorshipCreate | tfSponsorshipReassign, + tfSponsorshipCreate | tfSponsorshipEnd, + tfSponsorshipReassign | tfSponsorshipEnd, + tfSponsorshipCreate | tfSponsorshipReassign | tfSponsorshipEnd, + }) + env(sponsor::transfer(alice, flag), Ter(temINVALID_FLAG)); + } + + // Malformed tests for tfSponsorshipCreate + { + // No sponsor field present + env(sponsor::transfer(alice, tfSponsorshipCreate), Ter(temMALFORMED)); + + // Sponsor field present without spfSponsorReserve + env(sponsor::transfer(alice, tfSponsorshipCreate), + sponsor::As(sponsor, spfSponsorFee), + Ter(temINVALID_FLAG)); + + // Sponsee field present + env(sponsor::transfer(alice, tfSponsorshipCreate), + sponsor::SponseeAcc(bob), + sponsor::As(sponsor, spfSponsorReserve), + Ter(temMALFORMED)); + } + + // Malformed tests for tfSponsorshipReassign + { + // No sponsor field present + env(sponsor::transfer(alice, tfSponsorshipReassign), Ter(temMALFORMED)); + + // Sponsor field present without spfSponsorReserve + env(sponsor::transfer(alice, tfSponsorshipReassign), + sponsor::As(sponsor, spfSponsorFee), + Ter(temINVALID_FLAG)); + + // Sponsee field present + env(sponsor::transfer(alice, tfSponsorshipReassign), + sponsor::SponseeAcc(bob), + sponsor::As(sponsor, spfSponsorReserve), + Ter(temMALFORMED)); + } + + // Malformed tests for tfSponsorshipEnd + { + // Sponsor field present + env(sponsor::transfer(alice, tfSponsorshipEnd), + sponsor::As(sponsor, spfSponsorReserve), + Ter(temMALFORMED)); + + // SponsorFlags field present + auto tx = sponsor::transfer(alice, tfSponsorshipEnd); + tx[sfSponsorFlags.jsonName] = spfSponsorFee; + env(tx, Ter(temINVALID_FLAG)); + + // Account = Sponsee + env(sponsor::transfer(alice, tfSponsorshipEnd), + sponsor::SponseeAcc(alice), + Ter(temMALFORMED)); + } + } + + { + // Invalid SponsorshipEnd permission (sponsor object/sponsor account) + Env env{*this, testableAmendments()}; + Account const alice("alice"); + Account const bob("bob"); + Account const charlie("charlie"); + Account const sponsor("sponsor"); + env.fund(XRP(10000), alice, bob, sponsor); + env.close(); + + { + // sponsor object + env.fund(XRP(1000), charlie); + env.close(); + env(deposit::auth(alice, charlie), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); + env.close(); + + auto const keylet = keylet::depositPreauth(alice, charlie); + env(sponsor::transfer(bob, tfSponsorshipEnd, keylet.key), + sponsor::SponseeAcc(alice), + Ter(tecNO_PERMISSION)); + } + { + // sponsor object + env(sponsor::transfer(alice, tfSponsorshipCreate), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); + env.close(); + + env(sponsor::transfer(bob, tfSponsorshipEnd), + sponsor::SponseeAcc(alice), + Ter(tecNO_PERMISSION)); + } + { + // The provided sfSponsee account does not exist + // when ending sponsorship. + Account const ghost("ghost"); // never funded, absent from ledger + env(sponsor::transfer(sponsor, tfSponsorshipEnd), + sponsor::SponseeAcc(ghost), + Ter(terNO_ACCOUNT)); + } + } + + { + // sponsor account + Env env{*this, testableAmendments()}; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor1("sponsor1"); + Account const sponsor2("sponsor2"); + env.fund(XRP(10000), alice, bob, sponsor1, sponsor2); + + // sfSponsor provided but sfSponsorSignature not provided + env(sponsor::transfer(alice, tfSponsorshipCreate), + sponsor::As(sponsor1, spfSponsorReserve), + Ter(temMALFORMED)); + env.close(); + + adjustAccountXRPBalance(env, sponsor1, accountReserve(env, 2) - drops(1)); + + env(sponsor::transfer(alice, tfSponsorshipCreate), + sponsor::As(sponsor1, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor1), + Ter(tecINSUFFICIENT_RESERVE)); + env.close(); + + adjustAccountXRPBalance(env, sponsor1, accountReserve(env, 2)); + + env(sponsor::transfer(alice, tfSponsorshipCreate), + sponsor::As(sponsor1, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor1)); + env.close(); + + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, alice) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor1) == 1); + auto const sle1 = env.le(keylet::account(alice)); + BEAST_EXPECT(sle1->isFieldPresent(sfSponsor)); + BEAST_EXPECT(sle1->getAccountID(sfSponsor) == sponsor1.id()); + + // transfer sponsor + adjustAccountXRPBalance(env, sponsor2, accountReserve(env, 2) - drops(1)); + + env(sponsor::transfer(alice, tfSponsorshipReassign), + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2), + Ter(tecINSUFFICIENT_RESERVE)); + env.close(); + + adjustAccountXRPBalance(env, sponsor2, accountReserve(env, 2)); + + env(sponsor::transfer(alice, tfSponsorshipReassign), + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2)); + env.close(); + + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, sponsor2) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, alice) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor2) == 1); + BEAST_EXPECT( + !env.le(keylet::account(sponsor1))->isFieldPresent(sfSponsoringAccountCount)); + auto const sle2 = env.le(keylet::account(alice)); + BEAST_EXPECT(sle2->isFieldPresent(sfSponsor)); + BEAST_EXPECT(sle2->getAccountID(sfSponsor) == sponsor2.id()); + + // Reassign to the current sponsor is a no-op and is rejected + env(sponsor::transfer(alice, tfSponsorshipReassign), + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2), + Ter(tecNO_PERMISSION)); + env.close(); + + BEAST_EXPECT(sponsoringAccountCount(env, sponsor2) == 1); + + // sponsor 2 accounts + adjustAccountXRPBalance(env, sponsor2, accountReserve(env, 3)); + env(sponsor::transfer(bob, tfSponsorshipCreate), + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2)); + env.close(); + + // dissolve sponsors + adjustAccountXRPBalance(env, alice, accountReserve(env, 1) - drops(1)); + + env(sponsor::transfer(alice, tfSponsorshipEnd), Ter(tecINSUFFICIENT_RESERVE)); + env.close(); + + adjustAccountXRPBalance(env, alice, accountReserve(env, 1)); + + env(sponsor::transfer(alice, tfSponsorshipEnd)); + env.close(); + + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, sponsor2) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, alice) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor2) == 1); + auto const sle3 = env.le(keylet::account(alice)); + BEAST_EXPECT(!sle3->isFieldPresent(sfSponsor)); + + env(sponsor::transfer(bob, tfSponsorshipEnd)); + env.close(); + + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, sponsor2) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, bob) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, bob) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor2) == 0); + BEAST_EXPECT( + !env.le(keylet::account(sponsor2))->isFieldPresent(sfSponsoringAccountCount)); + auto const sle4 = env.le(keylet::account(bob)); + BEAST_EXPECT(!sle4->isFieldPresent(sfSponsor)); + + // not sponsored + env(sponsor::transfer(bob, tfSponsorshipEnd), Ter(tecNO_PERMISSION)); + env.close(); + } + { + // dissolve account sponsorship from sponsor + Env env{*this, testableAmendments()}; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + env.fund(XRP(10000), alice, bob, sponsor); + env.close(); + + env(sponsor::transfer(alice, tfSponsorshipCreate), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); + env.close(); + + BEAST_EXPECT(env.le(alice)->getAccountID(sfSponsor) == sponsor.id()); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor) == 1); + + env(sponsor::transfer(sponsor, tfSponsorshipEnd), sponsor::SponseeAcc(alice)); + env.close(); + + BEAST_EXPECT(!env.le(alice)->isFieldPresent(sfSponsor)); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor) == 0); + } + + { + // sponsor object (co-signing) + Env env{*this, testableAmendments()}; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor1("sponsor1"); + Account const sponsor2("sponsor2"); + env.fund(XRP(10000), alice, bob, sponsor1, sponsor2); + env.close(); + + adjustAccountXRPBalance(env, sponsor1, reserve(env, 1) - drops(1)); + adjustAccountXRPBalance(env, sponsor2, reserve(env, 1) - drops(1)); + + auto const seq = env.seq(alice); + env(check::create(alice, bob, XRP(1))); + env.close(); + + auto const checkId = keylet::check(alice, seq).key; + BEAST_EXPECT(env.le(keylet::unchecked(checkId)) != nullptr); + + env(sponsor::transfer(alice, tfSponsorshipCreate, checkId), + sponsor::As(sponsor1, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor1), + Ter(tecINSUFFICIENT_RESERVE)); + env.close(); + + env(pay(alice, sponsor1, drops(1))); + env.close(); + + // Invalid ObjectID (not found) + env(sponsor::transfer(alice, tfSponsorshipCreate, keylet::check(alice, 0).key), + sponsor::As(sponsor1, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor1), + Ter(tecNO_ENTRY)); + env.close(); + + // Invalid Owner + env(sponsor::transfer(bob, tfSponsorshipCreate, checkId), + sponsor::As(sponsor1, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor1), + Ter(tecNO_PERMISSION)); + env.close(); + + // Reassign an object that is not sponsored yet + env(sponsor::transfer(alice, tfSponsorshipReassign, checkId), + sponsor::As(sponsor1, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor1), + Ter(tecNO_PERMISSION)); + env.close(); + + // Valid Owner + env(sponsor::transfer(alice, tfSponsorshipCreate, checkId), + sponsor::As(sponsor1, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor1)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor1) == 1); + BEAST_EXPECT(sponsoringAccountCount(env, alice) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor1) == 0); + auto const sle1 = env.le(keylet::unchecked(checkId)); + BEAST_EXPECT(sle1->isFieldPresent(sfSponsor)); + BEAST_EXPECT(sle1->getAccountID(sfSponsor) == sponsor1.id()); + + // Create on an object that is already sponsored + env(sponsor::transfer(alice, tfSponsorshipCreate, checkId), + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2), + Ter(tecNO_PERMISSION)); + env.close(); + + // Reassign to the current sponsor is a no-op and is rejected + env(sponsor::transfer(alice, tfSponsorshipReassign, checkId), + sponsor::As(sponsor1, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor1), + Ter(tecNO_PERMISSION)); + env.close(); + + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor1) == 1); + + // transfer sponsor + env(sponsor::transfer(alice, tfSponsorshipReassign, checkId), + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2), + Ter(tecINSUFFICIENT_RESERVE)); + + env(pay(alice, sponsor2, drops(1))); + env.close(); + + env(sponsor::transfer(alice, tfSponsorshipReassign, checkId), + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2)); + env.close(); + + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, sponsor2) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + BEAST_EXPECT(sponsoringAccountCount(env, alice) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor2) == 0); + auto const sle2 = env.le(keylet::unchecked(checkId)); + BEAST_EXPECT(sle2->isFieldPresent(sfSponsor)); + BEAST_EXPECT(sle2->getAccountID(sfSponsor) == sponsor2.id()); + + // dissolve sponsor: ending an object sponsorship succeeds even + // when the sponsee lacks sufficient reserve to reclaim the object. + adjustAccountXRPBalance(env, alice, reserve(env, 1) - drops(1)); + + env(sponsor::transfer(alice, tfSponsorshipEnd, checkId)); + env.close(); + + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, sponsor2) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, alice) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor2) == 0); + BEAST_EXPECT( + !env.le(keylet::account(sponsor2))->isFieldPresent(sfSponsoringOwnerCount)); + auto const sle3 = env.le(keylet::unchecked(checkId)); + BEAST_EXPECT(!sle3->isFieldPresent(sfSponsor)); + + // Ending sponsorship on an object that is not sponsored (a ticket, + // which cannot be sponsored) is rejected. + adjustAccountXRPBalance(env, alice, reserve(env, 2)); + auto const ticketSeq = env.seq(alice); + env(ticket::create(alice, 1)); + env.close(); + auto ticketId = keylet::ticket(alice, ticketSeq + 1).key; + BEAST_EXPECT(env.le(keylet::unchecked(ticketId))); + env(sponsor::transfer(alice, tfSponsorshipEnd, ticketId), Ter(tecNO_PERMISSION)); + env.close(); + env(noop(alice), ticket::Use(ticketSeq + 1)); + env.close(); + } + { + // sponsor object (pre-funded + no ltSponsorship entry) + Env env{*this, testableAmendments()}; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor1("sponsor1"); + Account const sponsor2("sponsor2"); + env.fund(XRP(10000), alice, bob, sponsor1, sponsor2); + env.close(); + + auto const seq = env.seq(alice); + env(check::create(alice, bob, XRP(1))); + env.close(); + + auto const checkId = keylet::check(alice, seq).key; + BEAST_EXPECT(env.le(keylet::unchecked(checkId)) != nullptr); + + env(sponsor::transfer(alice, tfSponsorshipCreate, checkId), + sponsor::As(sponsor1, spfSponsorReserve), + Ter(terNO_PERMISSION)); + env.close(); + + env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::SponseeAcc(alice)); + env.close(); + + env(sponsor::transfer(alice, tfSponsorshipCreate, checkId), + sponsor::As(sponsor2, spfSponsorReserve)); + env.close(); + + env(sponsor::transfer(alice, tfSponsorshipReassign, checkId), + sponsor::As(sponsor1, spfSponsorReserve), + Ter(terNO_PERMISSION)); + env.close(); + } + { + // sponsor object (pre-funded) + Env env{*this, testableAmendments()}; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor1("sponsor1"); + Account const sponsor2("sponsor2"); + env.fund(XRP(10000), alice, bob, sponsor1, sponsor2); + env.close(); + + auto const seq = env.seq(alice); + env(check::create(alice, bob, XRP(1))); + env.close(); + + auto const checkId = keylet::check(alice, seq).key; + BEAST_EXPECT(env.le(keylet::unchecked(checkId)) != nullptr); + + // insufficient reserve count + env(sponsor::set_fee(sponsor1, 0, XRP(100)), sponsor::SponseeAcc(alice)); + env.close(); + env(sponsor::transfer(alice, tfSponsorshipCreate, checkId), + sponsor::As(sponsor1, spfSponsorReserve), + Ter(tecINSUFFICIENT_RESERVE)); + env.close(); + + env(sponsor::set_reserve(sponsor1, 0, 100), sponsor::SponseeAcc(alice)); + env.close(); + + env(sponsor::transfer(alice, tfSponsorshipCreate, checkId), + sponsor::As(sponsor1, spfSponsorReserve)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor1) == 1); + BEAST_EXPECT(sponsoringAccountCount(env, alice) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor1) == 0); + auto checkSle = env.le(keylet::unchecked(checkId)); + BEAST_EXPECT(checkSle->isFieldPresent(sfSponsor)); + BEAST_EXPECT(checkSle->getAccountID(sfSponsor) == sponsor1.id()); + auto sponsor1Sle = env.le(keylet::sponsorship(sponsor1, alice)); + BEAST_EXPECT(sponsor1Sle->getFieldU32(sfRemainingOwnerCount) == 99); + + // Reassign to the current sponsor is rejected and must not draw + // down the pre-funded reserve budget + env(sponsor::transfer(alice, tfSponsorshipReassign, checkId), + sponsor::As(sponsor1, spfSponsorReserve), + Ter(tecNO_PERMISSION)); + env.close(); + + sponsor1Sle = env.le(keylet::sponsorship(sponsor1, alice)); + BEAST_EXPECT(sponsor1Sle->getFieldU32(sfRemainingOwnerCount) == 99); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor1) == 1); + + // transfer sponsor + env(sponsor::set_reserve(sponsor2, 0, 100), sponsor::SponseeAcc(alice)); + env.close(); + + env(sponsor::transfer(alice, tfSponsorshipReassign, checkId), + sponsor::As(sponsor2, spfSponsorReserve)); + env.close(); + + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, sponsor2) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + BEAST_EXPECT(sponsoringAccountCount(env, alice) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor2) == 0); + checkSle = env.le(keylet::unchecked(checkId)); + BEAST_EXPECT(checkSle->isFieldPresent(sfSponsor)); + BEAST_EXPECT(checkSle->getAccountID(sfSponsor) == sponsor2.id()); + sponsor1Sle = env.le(keylet::sponsorship(sponsor1, alice)); + BEAST_EXPECT(sponsor1Sle->getFieldU32(sfRemainingOwnerCount) == 99); + auto sponsor2Sle = env.le(keylet::sponsorship(sponsor2, alice)); + BEAST_EXPECT(sponsor2Sle->getFieldU32(sfRemainingOwnerCount) == 99); + + // dissolve sponsor + adjustAccountXRPBalance(env, alice, reserve(env, 1)); + env(sponsor::transfer(alice, tfSponsorshipEnd, checkId)); + env.close(); + + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, sponsor2) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, alice) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor1) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor2) == 0); + BEAST_EXPECT( + !env.le(keylet::account(sponsor2))->isFieldPresent(sfSponsoringOwnerCount)); + checkSle = env.le(keylet::unchecked(checkId)); + BEAST_EXPECT(!checkSle->isFieldPresent(sfSponsor)); + sponsor2Sle = env.le(keylet::sponsorship(sponsor2, alice)); + BEAST_EXPECT(sponsor2Sle->getFieldU32(sfRemainingOwnerCount) == 99); + } + + { + // Dissolve object sponsorship from sponsor(no-ltSponsorship) + Env env{*this, testableAmendments()}; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + env.fund(XRP(10000), alice, bob, sponsor); + env.close(); + + auto const seq = env.seq(alice); + env(check::create(alice, bob, XRP(1))); + env.close(); + + auto const checkId = keylet::check(alice, seq).key; + BEAST_EXPECT(env.le(keylet::unchecked(checkId)) != nullptr); + + env(sponsor::transfer(alice, tfSponsorshipCreate, checkId), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); + env.close(); + + BEAST_EXPECT( + env.le(keylet::unchecked(checkId))->getAccountID(sfSponsor) == sponsor.id()); + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + + // not the owner of the object + env(sponsor::transfer(sponsor, tfSponsorshipEnd, checkId), Ter(tecNO_PERMISSION)); + env.close(); + + env(sponsor::transfer(sponsor, tfSponsorshipEnd, checkId), sponsor::SponseeAcc(alice)); + env.close(); + + BEAST_EXPECT(!env.le(keylet::unchecked(checkId))->isFieldPresent(sfSponsor)); + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + } + + { + // Dissolve object sponsorship from sponsor (with ltSponsorship) + Env env{*this, testableAmendments()}; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + env.fund(XRP(10000), alice, bob, sponsor); + env.close(); + + auto const seq = env.seq(alice); + env(check::create(alice, bob, XRP(1))); + env.close(); + + auto const checkId = keylet::check(alice, seq).key; + BEAST_EXPECT(env.le(keylet::unchecked(checkId)) != nullptr); + + env(sponsor::transfer(alice, tfSponsorshipCreate, checkId), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); + env.close(); + + env(sponsor::set_reserve(sponsor, 0, 100), sponsor::SponseeAcc(alice)); + env.close(); + + BEAST_EXPECT( + env.le(keylet::unchecked(checkId))->getAccountID(sfSponsor) == sponsor.id()); + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + BEAST_EXPECT( + env.le(keylet::sponsorship(sponsor, alice))->getFieldU32(sfRemainingOwnerCount) == + 100); + + // not the owner of the object + env(sponsor::transfer(sponsor, tfSponsorshipEnd, checkId), Ter(tecNO_PERMISSION)); + env.close(); + + env(sponsor::transfer(sponsor, tfSponsorshipEnd, checkId), sponsor::SponseeAcc(alice)); + env.close(); + + BEAST_EXPECT(!env.le(keylet::unchecked(checkId))->isFieldPresent(sfSponsor)); + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT( + env.le(keylet::sponsorship(sponsor, alice))->getFieldU32(sfRemainingOwnerCount) == + 100); + } + + { + // sponsor trustline + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + + auto const& highAcc = alice > bob ? alice : bob; + auto const& lowAcc = alice > bob ? bob : alice; + + for (bool const isIssuerHigh : {false, true}) + { + Env env{*this, testableAmendments()}; + env.fund(XRP(10000), alice, bob, sponsor); + env.close(); + + auto const& issuer = isIssuerHigh ? highAcc : lowAcc; + auto const& user = isIssuerHigh ? lowAcc : highAcc; + + auto const usd = issuer["usd"]; + auto const currency = usd.currency; + + env(trust(user, issuer["usd"](100))); + env.close(); + + auto const trustId = keylet::trustLine(user, issuer, currency); + BEAST_EXPECT(env.le(trustId)); + + // transfer sponsor + env(sponsor::transfer(user, tfSponsorshipCreate, trustId.key), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); + env.close(); + + BEAST_EXPECT(env.le(trustId)); + + BEAST_EXPECT( + env.le(trustId)->getAccountID(isIssuerHigh ? sfLowSponsor : sfHighSponsor) == + sponsor.id()); + BEAST_EXPECT( + !env.le(trustId)->isFieldPresent(isIssuerHigh ? sfHighSponsor : sfLowSponsor)); + + // dissolve sponsor + env(sponsor::transfer(user, tfSponsorshipEnd, trustId.key)); + env.close(); + + BEAST_EXPECT(env.le(trustId)); + BEAST_EXPECT( + !env.le(trustId)->isFieldPresent(isIssuerHigh ? sfLowSponsor : sfHighSponsor)); + BEAST_EXPECT( + !env.le(trustId)->isFieldPresent(isIssuerHigh ? sfHighSponsor : sfLowSponsor)); + } + } + + { + // invalid transfer + Env env{*this, testableAmendments()}; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + env.fund(XRP(10000), alice, bob, sponsor); + env.close(); + + // create owner dir + env(ticket::create(alice, 1)); + env.close(); + + // AccountRoot + // Amendments + // LedgerHashes + // FeeSettings + // NegativeUNL + // DirNode + auto const keylets = { + keylet::account(alice), + // keylet::amendments(), + keylet::skip(), + keylet::feeSettings(), + // keylet::negativeUNL(), + keylet::ownerDir(alice), + }; + for (auto const& keylet : keylets) + { + env(sponsor::transfer(alice, tfSponsorshipCreate, keylet.key), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor), + Ter(tecNO_PERMISSION)); + } + } + + { + // existing owner objects that are outside the v1 SponsorshipTransfer + // object allow-list + Env env{*this, testableAmendments()}; + Account const alice("alice"); + Account const sponsor("sponsor"); + env.fund(XRP(10000), alice, sponsor); + env.close(); + + auto const checkBlocked = [&](Account const& account, uint256 const& objectID) { + env(sponsor::transfer(account, tfSponsorshipCreate, objectID), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor), + Ter(tecNO_PERMISSION)); + env.close(); + }; + + auto const ticketSeq = env.seq(alice); + env(ticket::create(alice, 1)); + env.close(); + auto const ticketID = keylet::ticket(alice, ticketSeq + 1).key; + BEAST_EXPECT(env.le(keylet::unchecked(ticketID))); + checkBlocked(alice, ticketID); + + env(did::setValid(alice)); + env.close(); + auto const didKeylet = keylet::did(alice.id()); + BEAST_EXPECT(env.le(didKeylet)); + checkBlocked(alice, didKeylet.key); + + env(token::mint(alice, 0u)); + env.close(); + auto const nftPageKeylet = keylet::nftokenPageMax(alice); + BEAST_EXPECT(env.le(nftPageKeylet)); + checkBlocked(alice, nftPageKeylet.key); + + Account const borrower("borrower"); + env.fund(XRP(1000000), borrower); + env.close(); + + PrettyAsset const xrpAsset{xrpIssue(), 1'000'000}; + Vault const vault{env}; + auto [vaultTx, vaultKeylet] = vault.create({.owner = alice, .asset = xrpAsset}); + env(vaultTx); + env.close(); + + env(vault.deposit( + {.depositor = alice, .id = vaultKeylet.key, .amount = xrpAsset(1000)})); + env.close(); + + auto const brokerKeylet = keylet::loanBroker(alice.id(), env.seq(alice)); + env(loanBroker::set(alice, vaultKeylet.key), + loanBroker::kDebtMaximum(xrpAsset(1000).value()), + loanBroker::kManagementFeeRate(TenthBips16{0}), + loanBroker::kCoverRateMinimum(TenthBips32{0}), + loanBroker::kCoverRateLiquidation(TenthBips32{0})); + env.close(); + + auto const loanKeylet = keylet::loan(brokerKeylet.key, 1); + env(loan::set(borrower, brokerKeylet.key, xrpAsset(100).value()), + Sig(sfCounterpartySignature, alice), + Fee(env.current()->fees().base * 2)); + env.close(); + BEAST_EXPECT(env.le(loanKeylet)); + checkBlocked(borrower, loanKeylet.key); + } + } + + void + testSponsorFee() + { + using namespace test::jtx; + + testcase("Sponsor Fee"); + + { + // co-signing + Env env{*this, testableAmendments()}; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + env.fund(XRP(10000), alice, bob); + env.close(); + + { + // Fee should be checked before sponsor permission, otherwise a tec + // result from a later check could cause context reset to pay Fee. + auto aliceBalance = env.balance(alice); + auto bobBalance = env.balance(bob); + auto sponsorBalance = env.balance(sponsor); + + env(pay(alice, bob, XRP(100)), + Fee(XRP(2000)), + sponsor::As(sponsor, spfSponsorFee), + Sig(sfSponsorSignature, sponsor), + Ter(terNO_ACCOUNT)); + env.close(); + BEAST_EXPECT(env.balance(alice) == aliceBalance); + BEAST_EXPECT(env.balance(bob) == bobBalance); + BEAST_EXPECT(env.balance(sponsor) == sponsorBalance); + } + + env.fund(XRP(1000), sponsor); + env.close(); + + { + // Sponsor pays the Fee + auto aliceBalance = env.balance(alice); + auto bobBalance = env.balance(bob); + auto sponsorBalance = env.balance(sponsor); + + auto const sendAmt = XRP(100); + auto const feeAmt = XRP(10); + env(pay(alice, bob, sendAmt), + Fee(feeAmt), + sponsor::As(sponsor, spfSponsorFee), + Sig(sfSponsorSignature, sponsor)); + env.close(); + BEAST_EXPECT(env.balance(alice) == aliceBalance - sendAmt); + BEAST_EXPECT(env.balance(bob) == bobBalance + sendAmt); + BEAST_EXPECT(env.balance(sponsor) == sponsorBalance - feeAmt); + } + + { + // insufficient balance to pay Fee + auto aliceBalance = env.balance(alice); + auto bobBalance = env.balance(bob); + auto sponsorBalance = env.balance(sponsor); + + env(pay(alice, bob, XRP(100)), + Fee(XRP(2000)), + sponsor::As(sponsor, spfSponsorFee), + Sig(sfSponsorSignature, sponsor), + Ter(terINSUF_FEE_B)); + env.close(); + BEAST_EXPECT(env.balance(alice) == aliceBalance); + BEAST_EXPECT(env.balance(bob) == bobBalance); + BEAST_EXPECT(env.balance(sponsor) == sponsorBalance); + } + + { + // Fee is paid by Sponsor + // on context reset (tec error) + auto aliceBalance = env.balance(alice); + auto bobBalance = env.balance(bob); + auto sponsorBalance = env.balance(sponsor); + auto const feeAmt = XRP(10); + + env(pay(alice, bob, XRP(20000)), + Fee(feeAmt), + sponsor::As(sponsor, spfSponsorFee), + Sig(sfSponsorSignature, sponsor), + Ter(tecUNFUNDED_PAYMENT)); + env.close(); + + BEAST_EXPECT(env.balance(alice) == aliceBalance); + BEAST_EXPECT(env.balance(bob) == bobBalance); + BEAST_EXPECT(env.balance(sponsor) == sponsorBalance - feeAmt); + } + + { + // A co-signed sponsor pays the fee from its own balance, but + // must never be charged into its own account reserve. With a + // balance of exactly reserve + fee the fee is still payable, + // charging the sponsor down to precisely its reserve. + auto const feeAmt = XRP(10); + adjustAccountXRPBalance(env, sponsor, reserve(env, 0) + feeAmt); + auto const sponsorBalance = env.balance(sponsor); + + env(noop(alice), + Fee(feeAmt), + sponsor::As(sponsor, spfSponsorFee), + Sig(sfSponsorSignature, sponsor), + Ter(tesSUCCESS)); + env.close(); + + BEAST_EXPECT(env.balance(sponsor) == sponsorBalance - feeAmt); + BEAST_EXPECT(env.balance(sponsor) == reserve(env, 0)); + } + + { + // below reserve + adjustAccountXRPBalance(env, sponsor, env.current()->fees().reserve); + + env(noop(alice), + Fee(env.current()->fees().base), + sponsor::As(sponsor, spfSponsorFee), + Sig(sfSponsorSignature, sponsor), + Ter(terINSUF_FEE_B)); + env.close(); + + env(noop(alice), + Fee(XRP(10)), + sponsor::As(sponsor, spfSponsorFee), + Sig(sfSponsorSignature, sponsor), + Ter(terINSUF_FEE_B)); + env.close(); + } + } + + { + // pre funded + Env env{*this, testableAmendments()}; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + env.fund(XRP(10000), alice, bob, sponsor); + env.close(); + + { + // Fee should be checked before sponsor permission, otherwise a tec + // result from a later check could cause context reset to pay Fee. + auto aliceBalance = env.balance(alice); + auto bobBalance = env.balance(bob); + auto sponsorBalance = env.balance(sponsor); + + env(pay(alice, bob, XRP(100)), + Fee(XRP(2000)), + sponsor::As(sponsor, spfSponsorFee), + Ter(terNO_PERMISSION)); + env.close(); + BEAST_EXPECT(env.balance(alice) == aliceBalance); + BEAST_EXPECT(env.balance(bob) == bobBalance); + BEAST_EXPECT(env.balance(sponsor) == sponsorBalance); + } + + env(sponsor::set_fee(sponsor, 0, XRP(100)), sponsor::SponseeAcc(alice)); + env.close(); + + { + // Sponsor pays the Fee + auto aliceBalance = env.balance(alice); + auto bobBalance = env.balance(bob); + auto sponsorBalance = env.balance(sponsor); + auto sponsorFee = sponsor::sponsorshipFeeBalance(env, sponsor, alice); + + auto const sendAmt = XRP(100); + auto const feeAmt = XRP(10); + env(pay(alice, bob, sendAmt), Fee(feeAmt), sponsor::As(sponsor, spfSponsorFee)); + env.close(); + + BEAST_EXPECT(env.balance(alice) == aliceBalance - sendAmt); + BEAST_EXPECT(env.balance(bob) == bobBalance + sendAmt); + BEAST_EXPECT(env.balance(sponsor) == sponsorBalance); + BEAST_EXPECT( + sponsor::sponsorshipFeeBalance(env, sponsor, alice) == sponsorFee - feeAmt); + } + + { + // insufficient balance to pay Fee + { + // > FeeAmount + auto aliceBalance = env.balance(alice); + auto bobBalance = env.balance(bob); + auto sponsorBalance = env.balance(sponsor); + auto sponsorFee = sponsor::sponsorshipFeeBalance(env, sponsor, alice); + + env(pay(alice, bob, XRP(100)), + Fee(XRP(90) + drops(1)), + sponsor::As(sponsor, spfSponsorFee), + Ter(terINSUF_FEE_B)); + env.close(); + + BEAST_EXPECT(env.balance(alice) == aliceBalance); + BEAST_EXPECT(env.balance(bob) == bobBalance); + BEAST_EXPECT(env.balance(sponsor) == sponsorBalance); + BEAST_EXPECT(sponsor::sponsorshipFeeBalance(env, sponsor, alice) == sponsorFee); + } + // use all FeeAmount + { + // = FeeAmount + auto aliceBalance = env.balance(alice); + auto bobBalance = env.balance(bob); + auto sponsorBalance = env.balance(sponsor); + + env(pay(alice, bob, XRP(100)), + Fee(XRP(90)), + sponsor::As(sponsor, spfSponsorFee), + Ter(tesSUCCESS)); + env.close(); + + BEAST_EXPECT(env.balance(alice) == aliceBalance - XRP(100)); + BEAST_EXPECT(env.balance(bob) == bobBalance + XRP(100)); + BEAST_EXPECT(env.balance(sponsor) == sponsorBalance); + BEAST_EXPECT( + !env.le(keylet::sponsorship(sponsor, alice))->isFieldPresent(sfFeeAmount)); + } + + // reset FeeAmount and MaxFee + env(sponsor::del(sponsor), sponsor::SponseeAcc(alice)); + env.close(); + env(sponsor::set_fee(sponsor, 0, XRP(10), XRP(1)), sponsor::SponseeAcc(alice)); + env.close(); + + { + // > MaxFee + auto aliceBalance = env.balance(alice); + auto bobBalance = env.balance(bob); + auto sponsorBalance = env.balance(sponsor); + auto sponsorFee = sponsor::sponsorshipFeeBalance(env, sponsor, alice); + + env(pay(alice, bob, XRP(100)), + Fee(XRP(1) + drops(1)), + sponsor::As(sponsor, spfSponsorFee), + Ter(terINSUF_FEE_B)); + env.close(); + + BEAST_EXPECT(env.balance(alice) == aliceBalance); + BEAST_EXPECT(env.balance(bob) == bobBalance); + BEAST_EXPECT(env.balance(sponsor) == sponsorBalance); + BEAST_EXPECT(sponsor::sponsorshipFeeBalance(env, sponsor, alice) == sponsorFee); + } + } + + { + // Fee is paid by Sponsor + // on context reset (tec error) + auto aliceBalance = env.balance(alice); + auto bobBalance = env.balance(bob); + auto sponsorBalance = env.balance(sponsor); + auto sponsorFee = sponsor::sponsorshipFeeBalance(env, sponsor, alice); + auto const feeAmt = XRP(1); + + env(pay(alice, bob, XRP(20000)), + Fee(feeAmt), + sponsor::As(sponsor, spfSponsorFee), + Ter(tecUNFUNDED_PAYMENT)); + env.close(); + + BEAST_EXPECT(env.balance(alice) == aliceBalance); + BEAST_EXPECT(env.balance(bob) == bobBalance); + BEAST_EXPECT(env.balance(sponsor) == sponsorBalance); + BEAST_EXPECT( + sponsor::sponsorshipFeeBalance(env, sponsor, alice) == sponsorFee - feeAmt); + } + + // make sfFeeAmount absent if tec error and all Fee is paid + { + // reset FeeAmount and MaxFee + env(sponsor::del(sponsor), sponsor::SponseeAcc(alice)); + env(sponsor::set_fee(sponsor, 0, XRP(10)), sponsor::SponseeAcc(alice)); + env.close(); + + BEAST_EXPECT( + env.le(keylet::sponsorship(sponsor, alice))->isFieldPresent(sfFeeAmount)); + auto sponsorAvailableFee = sponsor::sponsorshipFeeBalance(env, sponsor, alice); + env(check::cancel(alice, uint256(1)), + Fee(sponsorAvailableFee), + sponsor::As(sponsor, spfSponsorFee), + Ter(tecNO_ENTRY)); + env.close(); + BEAST_EXPECT( + !env.le(keylet::sponsorship(sponsor, alice))->isFieldPresent(sfFeeAmount)); + } + } + + // MaxFee cap is enforced in reset() for tec-failing transactions. + // On a closed ledger view (!view.open()), checkFee returns tecINSUFF_FEE when + // Fee > MaxFee (not terINSUF_FEE_B), triggering reset() + { + Env env{*this, testableAmendments()}; + Account const alice("alice"); + Account const carol("sponsor"); + + env.fund(XRP(10000), alice, carol); + env.close(); + + // FeeAmount=1000 drops, MaxFee=10 drops + env(sponsor::set_fee(carol, 0, drops(1000), drops(10)), sponsor::SponseeAcc(alice)); + env.close(); + + // Apply directly against the closed ledger view (open_ = false) so that + // checkFee returns tecINSUFF_FEE and reset() is invoked. + OpenView overlay(&*env.closed()); + + auto jt = env.jt( + noop(alice), + Fee(drops(1000)), + Seq(env.seq(alice)), + sponsor::As(carol, spfSponsorFee)); + + auto const result = xrpl::apply(env.app(), overlay, *jt.stx, TapNone, env.journal); + BEAST_EXPECT(result.ter == tecINSUFF_FEE); + BEAST_EXPECT(result.applied); + + // Only MaxFee (10 drops) must be deducted, not the full 1000 drops. + auto const sle = overlay.read(keylet::sponsorship(carol.id(), alice.id())); + BEAST_EXPECT(sle); + BEAST_EXPECT(sle->isFieldPresent(sfFeeAmount)); + BEAST_EXPECT(sle->getFieldAmount(sfFeeAmount) == drops(990)); // 1000 - MaxFee(10) + } + + // LedgerStateFix charges an owner-reserve fee and can claim that fee + // while returning tecFAILED_PROCESSING. That path must be safe when the + // fee is pre-funded by a sponsorship object. + { + Env env{*this, testableAmendments()}; + Account const alice("alice"); + Account const sponsor("sponsor"); + env.fund(XRP(1000), alice, sponsor); + env.close(); + + auto const fixFee = drops(env.current()->fees().increment); + env(sponsor::set_fee(sponsor, 0, fixFee), sponsor::SponseeAcc(alice)); + env.close(); + + env(ledgerStateFix::nftPageLinks(alice, alice), + Fee(fixFee), + sponsor::As(sponsor, spfSponsorFee), + Ter(tecFAILED_PROCESSING)); + + if (auto const sle = env.le(keylet::sponsorship(sponsor, alice)); BEAST_EXPECT(sle)) + BEAST_EXPECT(!sle->isFieldPresent(sfFeeAmount)); + } + + // If preclaim saw spendable sponsored FeeAmount but the apply view no + // longer has it, the fee path should fail cleanly instead of throwing. + { + Env env{*this, testableAmendments()}; + Account const alice("alice"); + Account const sponsor("sponsor"); + env.fund(XRP(1000), alice, sponsor); + env.close(); + + auto const fixFee = drops(env.current()->fees().increment); + env(sponsor::set_fee(sponsor, 0, fixFee), sponsor::SponseeAcc(alice)); + env.close(); + + OpenView overlay(&*env.closed()); + auto jt = env.jt( + ledgerStateFix::nftPageLinks(alice, alice), + Fee(fixFee), + sponsor::As(sponsor, spfSponsorFee)); + + auto const pf = preflight(env.app(), overlay.rules(), *jt.stx, TapNone, env.journal); + BEAST_EXPECT(isTesSuccess(pf.ter)); + auto const pc = preclaim(pf, env.app(), overlay); + BEAST_EXPECT(isTesSuccess(pc.ter)); + + auto const original = overlay.read(keylet::sponsorship(sponsor, alice)); + if (BEAST_EXPECT(original)) + { + auto sle = std::make_shared(*original); + sle->makeFieldAbsent(sfFeeAmount); + overlay.rawReplace(sle); + } + + auto const result = doApply(pc, env.app(), overlay); + BEAST_EXPECT(result.ter == terINSUF_FEE_B); + BEAST_EXPECT(!result.applied); + } + + // test lsfSponsorshipRequireSignForFee + { + Env env{*this, testableAmendments()}; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + env.fund(XRP(10000), alice, bob, sponsor); + env.close(); + + // set flag + env(sponsor::set_fee(sponsor, tfSponsorshipSetRequireSignForFee, XRP(10)), + sponsor::SponseeAcc(alice)); + env.close(); + + env(pay(alice, bob, XRP(100)), + Fee(XRP(10)), + sponsor::As(sponsor, spfSponsorFee), + Ter(terNO_PERMISSION)); + env.close(); + + BEAST_EXPECT( + env.le(keylet::sponsorship(sponsor, alice))->getFieldAmount(sfFeeAmount) == + XRP(10)); + + // clear flag + env(sponsor::set_fee(sponsor, tfSponsorshipClearRequireSignForFee, XRP(10)), + sponsor::SponseeAcc(alice)); + env.close(); + + // Payment is re-applied + BEAST_EXPECT(!env.le(keylet::sponsorship(sponsor, alice))->isFieldPresent(sfFeeAmount)); + } + + // RequireSignForFee: co-signing should succeed + { + Env env{*this, testableAmendments()}; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + env.fund(XRP(10000), alice, bob, sponsor); + env.close(); + + // set flag + env(sponsor::set_fee(sponsor, tfSponsorshipSetRequireSignForFee, XRP(10)), + sponsor::SponseeAcc(alice)); + env.close(); + + // pre-funded (no sig) should fail + env(pay(alice, bob, XRP(100)), + Fee(XRP(1)), + sponsor::As(sponsor, spfSponsorFee), + Ter(terNO_PERMISSION)); + env.close(); + + // co-signing (with sig) should succeed + env(pay(alice, bob, XRP(100)), + Fee(XRP(1)), + sponsor::As(sponsor, spfSponsorFee), + Sig(sfSponsorSignature, sponsor), + Ter(tesSUCCESS)); + env.close(); + + BEAST_EXPECT( + env.le(keylet::sponsorship(sponsor, alice))->getFieldAmount(sfFeeAmount) == XRP(9)); + } + } + + void + testSponsorAccount() + { + testcase("Sponsor Account"); + using namespace test::jtx; + + Account const alice("alice"); + Account const sponsor("sponsor"); + Account const sponsor2("sponsor2"); + Account const sponsor3("sponsor3"); + Account const bob("bob"); + Account const charlie("charlie"); + Account const dave("dave"); + Account const gw("gw"); + auto const usd = gw["usd"]; + + { + // Disabled + Env env{*this, testableAmendments() - featureSponsor}; + env.fund(XRP(10000), alice, sponsor); + env.close(); + env(pay(alice, bob, XRP(100)), Txflags(tfSponsorCreatedAccount), Ter(temDISABLED)); + env.close(); + } + + Env env{*this, testableAmendments()}; + env.fund(XRP(10000), alice, sponsor, sponsor2, sponsor3); + env.close(); + + // Invalid flags + for (auto flag : { + tfNoRippleDirect, + tfPartialPayment, + tfLimitQuality, + }) + { + env(pay(alice, bob, XRP(100)), + Txflags(tfSponsorCreatedAccount | flag), + Ter(temINVALID_FLAG)); + env.close(); + } + + // Invalid amount(iou) + env(pay(alice, bob, usd(100)), Txflags(tfSponsorCreatedAccount), Ter(temBAD_AMOUNT)); + env.close(); + + // Sponsored account creation is reserve sponsorship and is only supported for direct XRP + // payments. + env(pay(alice, bob, drops(1)), + Txflags(tfSponsorCreatedAccount), + Sendmax(usd(2)), + Ter(temINVALID)); + env.close(); + + env(pay(alice, bob, drops(1)), + Txflags(tfSponsorCreatedAccount), + Path(~XRP), + Ter(temINVALID)); + env.close(); + + // Account is not sponsored by normal Sponsor specification + { + env(pay(alice, bob, drops(env.current()->fees().accountReserve(0, 1))), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); + env.close(); + + auto const bobSle = env.le(keylet::account(bob)); + BEAST_EXPECT(!bobSle->isFieldPresent(sfSponsor)); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor) == 0); + } + + // Use tfSponsorCreatedAccount to sponsor an account + { + // to funded account + env(pay(sponsor2, bob, drops(1)), + Txflags(tfSponsorCreatedAccount), + Fee(XRP(1)), + Ter(tecNO_SPONSOR_PERMISSION)); + env.close(); + + BEAST_EXPECT(env.balance(sponsor2) == XRP(9999)); + + // to non-funded account / insufficient balance for reserve + env(pay(sponsor2, charlie, XRP(9999) - env.current()->fees().reserve + drops(1)), + Txflags(tfSponsorCreatedAccount), + Ter(tecUNFUNDED_PAYMENT)); + env.close(); + + // to non-funded account + auto const sponsor2BalanceBefore = env.balance(sponsor2); + env(pay(sponsor2, charlie, drops(1)), Txflags(tfSponsorCreatedAccount), Fee(XRP(1))); + env.close(); + + auto const charlieSle = env.le(keylet::account(charlie)); + BEAST_EXPECT(charlieSle->isFieldPresent(sfSponsor)); + BEAST_EXPECT(charlieSle->getAccountID(sfSponsor) == sponsor2.id()); + BEAST_EXPECT(sponsoredOwnerCount(env, charlie) == 0); + BEAST_EXPECT(sponsoringAccountCount(env, sponsor2) == 1); + // verify sponsor balance decreased by payment + Fee + BEAST_EXPECT(env.balance(sponsor2) == sponsor2BalanceBefore - drops(1) - XRP(1)); + } + { + // insufficient reserve to sponsor acount + + auto const sendAmount = drops(1); + // 2 account reserve + send amount + auto const requireBalance = accountReserve(env, 2) + sendAmount; + adjustAccountXRPBalance(env, sponsor3, requireBalance - drops(1)); + env(pay(sponsor3, dave, sendAmount), + Txflags(tfSponsorCreatedAccount), + Fee(XRP(1)), + Ter(tecUNFUNDED_PAYMENT)); + env.close(); + + adjustAccountXRPBalance(env, sponsor3, requireBalance); + env(pay(sponsor3, dave, sendAmount), + Txflags(tfSponsorCreatedAccount), + Fee(XRP(1)), + Ter(tesSUCCESS)); + env.close(); + } + } + + void + testRequireFlag() + { + using namespace test::jtx; + { + testcase("SponsorshipRequireSignForReserve"); + + Env env{*this, testableAmendments()}; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + env.fund(XRP(10000), alice, bob, sponsor); + env.close(); + + // set flag + env(sponsor::set_reserve(sponsor, tfSponsorshipSetRequireSignForReserve, 10), + sponsor::SponseeAcc(alice)); + env.close(); + + env(check::create(alice, bob, XRP(100)), + Fee(XRP(10)), + sponsor::As(sponsor, spfSponsorReserve), + Ter(terNO_PERMISSION)); + + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + + // clear flag + env(sponsor::set_reserve(sponsor, tfSponsorshipClearRequireSignForReserve, 1), + sponsor::SponseeAcc(alice)); + env.close(); + + // CheckCreate is re-applied + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + } + + { + testcase("SponsorshipRequireSignForFee"); + + Env env{*this, testableAmendments()}; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + env.fund(XRP(10000), alice, bob, sponsor); + env.close(); + + // set flag + env(sponsor::set_fee(sponsor, tfSponsorshipSetRequireSignForFee, XRP(10)), + sponsor::SponseeAcc(alice)); + env.close(); + + env(check::create(alice, bob, XRP(100)), + Fee(XRP(10)), + sponsor::As(sponsor, spfSponsorFee), + Ter(terNO_PERMISSION)); + + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT( + env.le(keylet::sponsorship(sponsor, alice))->getFieldAmount(sfFeeAmount) == + XRP(10)); + + // clear flag + env(sponsor::set_fee(sponsor, tfSponsorshipClearRequireSignForFee, XRP(10)), + sponsor::SponseeAcc(alice)); + env.close(); + + // CheckCreate is re-applied + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(!env.le(keylet::sponsorship(sponsor, alice))->isFieldPresent(sfFeeAmount)); + } + } + + void + testSponsorReserveSimple(bool cosigning) + { + testcase("SponsorReserveSimple"); + using namespace test::jtx; + Env env{*this, testableAmendments()}; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + + env.fund(XRP(10000), alice, bob, sponsor); + env.close(); + + if (cosigning) + { + adjustAccountXRPBalance(env, sponsor, reserve(env, 1) - drops(1)); + + env(check::create(alice, bob, XRP(100)), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor), + Ter(tecINSUFFICIENT_RESERVE)); + env.close(); + + adjustAccountXRPBalance(env, sponsor, reserve(env, 1)); + + env(check::create(alice, bob, XRP(100)), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor), + Ter(tesSUCCESS)); + env.close(); + } + else + { + env(sponsor::set_reserve(sponsor, 0, 250), sponsor::SponseeAcc(alice)); + env.close(); + + adjustAccountXRPBalance(env, sponsor, reserve(env, 2) - drops(1)); + + env(check::create(alice, bob, XRP(100)), + sponsor::As(sponsor, spfSponsorReserve), + Ter(tecINSUFFICIENT_RESERVE)); + env.close(); + + adjustAccountXRPBalance(env, sponsor, reserve(env, 2)); + + env(check::create(alice, bob, XRP(100)), + sponsor::As(sponsor, spfSponsorReserve), + Ter(tesSUCCESS)); + env.close(); + } + } + + // test helper for both cosigning and pre-funded sponsorship + template + void + testEachSponsorship( + test::jtx::Env& env, + bool cosigning, + jtx::Account const& sponsor, + jtx::Account const& sponsee, + uint32_t reserveCount, + uint32_t sponsorReserveCount, + TER insufficientReserveResult, + SubmitCallback callback, + std::optional> expected = std::nullopt) + { + using namespace test::jtx; + auto const sponseeOwnerCountBefore = ownerCount(env, sponsee); + auto const sponseeSponsoredOwnerCountBefore = sponsoredOwnerCount(env, sponsee); + auto const sponseeSponsoringOwnerCountBefore = sponsoringOwnerCount(env, sponsee); + auto const sponsorOwnerCountBefore = ownerCount(env, sponsor); + auto const sponsorSponsoredOwnerCountBefore = sponsoredOwnerCount(env, sponsor); + auto const sponsorSponsoringOwnerCountBefore = sponsoringOwnerCount(env, sponsor); + + std::optional sponsorSig = + cosigning ? std::optional(Sig(sfSponsorSignature, sponsor)) : std::nullopt; + + auto const sponsorCurrentOwnerCount = ownerCount(env, sponsor) - + sponsoredOwnerCount(env, sponsor) + sponsoringOwnerCount(env, sponsor); + + auto submit = [&](TER ter) { + return [&, ter](json::Value const& jv, auto const&... fN) { + if (sponsorSig) + { + env(jv, fN..., sponsor::As(sponsor, spfSponsorReserve), *sponsorSig, Ter(ter)); + } + else + { + env(jv, fN..., sponsor::As(sponsor, spfSponsorReserve), Ter(ter)); + } + }; + }; + + // Insufficient Reserve + { + if (cosigning) + { + adjustAccountXRPBalance( + env, + sponsor, + reserve(env, sponsorCurrentOwnerCount + sponsorReserveCount) - drops(1)); + } + else + { + // cleanup previous sponsorship + if (env.le(keylet::sponsorship(sponsor, sponsee))) + { + env(sponsor::del(sponsor), sponsor::SponseeAcc(sponsee)); + env.close(); + } + + if (sponsorReserveCount > 1) + { + env(sponsor::set(sponsor, 0, sponsorReserveCount - 1, XRP(1)), + sponsor::SponseeAcc(sponsee)); + } + else + { + // just create sponsor object + env(sponsor::set(sponsor, 0, std::nullopt, XRP(1)), + sponsor::SponseeAcc(sponsee)); + } + env.close(); + } + + // A failed sponsored create must not consume prefunded reserve or mutate owner counts. + auto const sponseeOwnerCountBeforeAttempt = ownerCount(env, sponsee); + auto const sponseeSponsoredOwnerCountBeforeAttempt = sponsoredOwnerCount(env, sponsee); + auto const sponseeSponsoringOwnerCountBeforeAttempt = + sponsoringOwnerCount(env, sponsee); + auto const sponsorOwnerCountBeforeAttempt = ownerCount(env, sponsor); + auto const sponsorSponsoredOwnerCountBeforeAttempt = sponsoredOwnerCount(env, sponsor); + auto const sponsorSponsoringOwnerCountBeforeAttempt = + sponsoringOwnerCount(env, sponsor); + auto const sponsorshipSleBeforeAttempt = env.le(keylet::sponsorship(sponsor, sponsee)); + bool const reserveCountPresentBeforeAttempt = sponsorshipSleBeforeAttempt && + sponsorshipSleBeforeAttempt->isFieldPresent(sfRemainingOwnerCount); + std::uint32_t const reserveCountBeforeAttempt = reserveCountPresentBeforeAttempt + ? sponsorshipSleBeforeAttempt->getFieldU32(sfRemainingOwnerCount) + : 0; + + callback(env, submit(insufficientReserveResult)); + env.close(); + + BEAST_EXPECT(ownerCount(env, sponsee) == sponseeOwnerCountBeforeAttempt); + BEAST_EXPECT( + sponsoredOwnerCount(env, sponsee) == sponseeSponsoredOwnerCountBeforeAttempt); + BEAST_EXPECT( + sponsoringOwnerCount(env, sponsee) == sponseeSponsoringOwnerCountBeforeAttempt); + BEAST_EXPECT(ownerCount(env, sponsor) == sponsorOwnerCountBeforeAttempt); + BEAST_EXPECT( + sponsoredOwnerCount(env, sponsor) == sponsorSponsoredOwnerCountBeforeAttempt); + BEAST_EXPECT( + sponsoringOwnerCount(env, sponsor) == sponsorSponsoringOwnerCountBeforeAttempt); + + auto const sponsorshipSleAfterAttempt = env.le(keylet::sponsorship(sponsor, sponsee)); + BEAST_EXPECT( + static_cast(sponsorshipSleAfterAttempt) == + static_cast(sponsorshipSleBeforeAttempt)); + if (sponsorshipSleAfterAttempt) + { + BEAST_EXPECT( + sponsorshipSleAfterAttempt->isFieldPresent(sfRemainingOwnerCount) == + reserveCountPresentBeforeAttempt); + if (reserveCountPresentBeforeAttempt) + { + BEAST_EXPECT( + sponsorshipSleAfterAttempt->getFieldU32(sfRemainingOwnerCount) == + reserveCountBeforeAttempt); + } + } + } + + // Success + { + if (cosigning) + { + adjustAccountXRPBalance( + env, sponsor, reserve(env, sponsorCurrentOwnerCount + sponsorReserveCount)); + } + else + { + // reset sponsorship + env(sponsor::del(sponsor), sponsor::SponseeAcc(sponsee)); + env(sponsor::set(sponsor, 0, sponsorReserveCount, XRP(1)), + sponsor::SponseeAcc(sponsee)); + env.close(); + } + callback(env, submit(tesSUCCESS)); + env.close(); + + if (!cosigning) + { + // Prefunded success consumes the reserved owner slot before cleanup. + auto const sponsorshipSle = env.le(keylet::sponsorship(sponsor, sponsee)); + BEAST_EXPECT(sponsorshipSle); + BEAST_EXPECT( + !sponsorshipSle->isFieldPresent(sfRemainingOwnerCount) || + sponsorshipSle->getFieldU32(sfRemainingOwnerCount) == 0); + + // cleanup sponsorship + env(sponsor::del(sponsor), sponsor::SponseeAcc(sponsee)); + env.close(); + } + } + + if (expected) + { + (*expected)(); + } + else + { + BEAST_EXPECT(ownerCount(env, sponsee) - sponseeOwnerCountBefore == reserveCount); + BEAST_EXPECT( + sponsoredOwnerCount(env, sponsee) - sponseeSponsoredOwnerCountBefore == + sponsorReserveCount); + BEAST_EXPECT( + sponsoringOwnerCount(env, sponsee) - sponseeSponsoringOwnerCountBefore == 0); + BEAST_EXPECT(ownerCount(env, sponsor) == sponsorOwnerCountBefore); + BEAST_EXPECT(sponsoredOwnerCount(env, sponsor) == sponsorSponsoredOwnerCountBefore); + BEAST_EXPECT( + sponsoringOwnerCount(env, sponsor) - sponsorSponsoringOwnerCountBefore == + sponsorReserveCount); + } + }; + + void + testCheck(bool cosigning) + { + testcase("Check"); + using namespace test::jtx; + Account const alice("alice"); + Account const bob("bob"); + Account const gw("gw"); + Account const sponsor("sponsor"); + Account const sponsor2("sponsor2"); + + auto const usd = gw["usd"]; + + { + Env env{*this, testableAmendments()}; + env.fund(XRP(10000), alice, bob, sponsor, sponsor2); + env.close(); + + // CheckCreate -> Check -> CheckCancel + + uint32_t seq = 0; + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + seq = env.seq(alice); + submit(check::create(alice, bob, XRP(1))); + }); + + BEAST_EXPECT(ownerCount(env, alice) == 1); // Check + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + + auto const keylet = keylet::check(alice, seq); + BEAST_EXPECT(env.le(keylet)->getAccountID(sfSponsor) == sponsor.id()); + + if (cosigning) + { + // transfer sponsor + env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2)); + env.close(); + } + else + { + env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::SponseeAcc(alice)); + env.close(); + + // transfer sponsor + env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), + sponsor::As(sponsor2, spfSponsorReserve)); + env.close(); + } + + BEAST_EXPECT(ownerCount(env, alice) == 1); // Check + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + + BEAST_EXPECT(env.le(keylet)->getAccountID(sfSponsor) == sponsor2.id()); + + // CheckCancel + env(check::cancel(alice, keylet.key)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + } + + { + Env env{*this, testableAmendments()}; + env.fund(XRP(10000), alice, bob, sponsor); + env.close(); + + // CheckCreate -> CheckCash + uint32_t seq2 = 0; + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + seq2 = env.seq(alice); + submit(check::create(alice, bob, XRP(1))); + }); + + BEAST_EXPECT(ownerCount(env, bob) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 0); + + // CheckCash + auto const checkId2 = keylet::check(alice, seq2).key; + env(check::cash(bob, checkId2, XRP(1))); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(ownerCount(env, bob) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + } + + // RippleState sponsor (CheckCashMakesTrustLine) + { + Env env{*this, testableAmendments()}; + env.fund(XRP(10000), alice, bob, gw, sponsor, sponsor2); + env.close(); + + env.trust(usd(100), alice); + env.close(); + env(pay(gw, alice, usd(100))); + env.close(); + + // CheckCreate -> CheckCash + uint32_t seq2 = 0; + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + seq2 = env.seq(alice); + submit(check::create(alice, bob, usd(1))); + }); + + BEAST_EXPECT(ownerCount(env, bob) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 0); + + auto const keylet = keylet::check(alice, seq2); + BEAST_EXPECT(env.le(keylet)->getAccountID(sfSponsor) == sponsor.id()); + + // CheckCash + testEachSponsorship( + env, + cosigning, + sponsor, + bob, + 1, + 1, + tecNO_LINE_INSUF_RESERVE, + [&](Env& env, auto const& submit) { submit(check::cash(bob, keylet.key, usd(1))); }, + [&]() { + BEAST_EXPECT(ownerCount(env, alice) == 1); // RippleState + BEAST_EXPECT(ownerCount(env, bob) == 1); // RippleState + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + + // the newly-created trust line carries the sponsor on + // bob's side, and no sponsor on the counterparty side + bool const bobLow = bob.id() < gw.id(); + auto const lineSle = env.le(keylet::trustLine(bob, gw, usd.currency)); + BEAST_EXPECT(lineSle); + if (lineSle) + { + BEAST_EXPECT( + lineSle->getAccountID(bobLow ? sfLowSponsor : sfHighSponsor) == + sponsor.id()); + BEAST_EXPECT( + !lineSle->isFieldPresent(bobLow ? sfHighSponsor : sfLowSponsor)); + } + }); + } + + // MPT sponsor: cashing an MPT check creates a sponsored MPToken for + // the casher. Unlike the trust-line path, this returns + // tecINSUFFICIENT_RESERVE (not tecNO_LINE_INSUF_RESERVE) on shortfall. + { + Env env{*this, testableAmendments()}; + env.fund(XRP(10000), bob, sponsor, sponsor2); + env.close(); + + MPTTester mptGw(env, gw, {.holders = {alice}}); + mptGw.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer}); + mptGw.authorize({.account = alice}); + auto const mpt = mptGw["MPT"]; + env(pay(gw, alice, mpt(10'000))); + env.close(); + + // CheckCreate (alice -> bob) paying the MPT + uint32_t seq2 = 0; + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + seq2 = env.seq(alice); + submit(check::create(alice, bob, mpt(1))); + }); + + auto const checkKeylet = keylet::check(alice, seq2); + BEAST_EXPECT(env.le(checkKeylet)->getAccountID(sfSponsor) == sponsor.id()); + BEAST_EXPECT(ownerCount(env, bob) == 0); + + // CheckCash by bob (no MPToken yet) creates a sponsored MPToken + testEachSponsorship( + env, + cosigning, + sponsor, + bob, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + submit(check::cash(bob, checkKeylet.key, mpt(1))); + }, + [&]() { + BEAST_EXPECT(ownerCount(env, bob) == 1); // MPToken + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + BEAST_EXPECT( + env.le(keylet::mptoken(mptGw.issuanceID(), bob))->getAccountID(sfSponsor) == + sponsor.id()); + }); + } + } + + void + testDelegate(bool cosigning) + { + testcase("Delegate"); + using namespace test::jtx; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + Account const sponsor2("sponsor2"); + + { + Env env{*this, testableAmendments()}; + env.fund(XRP(1000000), alice, bob, sponsor, sponsor2); + env.close(); + + // DelegateSet + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + submit(delegate::set(alice, bob, {"Payment"})); + }); + + // the created Delegate object carries the sponsor + auto const keylet = keylet::delegate(alice, bob); + BEAST_EXPECT(env.le(keylet)->getAccountID(sfSponsor) == sponsor.id()); + + // transfer sponsor + if (cosigning) + { + env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2)); + env.close(); + } + else + { + env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::SponseeAcc(alice)); + env.close(); + + env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), + sponsor::As(sponsor2, spfSponsorReserve)); + env.close(); + } + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + BEAST_EXPECT(env.le(keylet)->getAccountID(sfSponsor) == sponsor2.id()); + + // delete + env(delegate::set(alice, bob, {})); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + } + } + + void + testDepositPreauth(bool cosigning) + { + testcase("DepositPreauth"); + using namespace test::jtx; + Account const alice("alice"); + Account const sponsor("sponsor"); + Account const sponsor2("sponsor2"); + auto const credType = std::string("credType"); + + { + Env env{*this, testableAmendments()}; + env.fund(XRP(1000000), alice, sponsor, sponsor2); + env.close(); + + // DepositPreauthSet + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { submit(deposit::auth(alice, sponsor)); }); + + // the created DepositPreauth object carries the sponsor + auto const keylet = keylet::depositPreauth(alice, sponsor); + BEAST_EXPECT(env.le(keylet)->getAccountID(sfSponsor) == sponsor.id()); + + // transfer sponsor + if (cosigning) + { + env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2)); + env.close(); + } + else + { + env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::SponseeAcc(alice)); + env.close(); + // No sponsor signature here: this exercises the prefunded reassign path. + env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key), + sponsor::As(sponsor2, spfSponsorReserve)); + env.close(); + + auto const sponsor2Sle = env.le(keylet::sponsorship(sponsor2, alice)); + BEAST_EXPECT(sponsor2Sle); + if (sponsor2Sle) + { + BEAST_EXPECT( + !sponsor2Sle->isFieldPresent(sfRemainingOwnerCount) || + sponsor2Sle->getFieldU32(sfRemainingOwnerCount) == 0); + } + } + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + BEAST_EXPECT(env.le(keylet)->getAccountID(sfSponsor) == sponsor2.id()); + + // DepositPreauthDelete + env(deposit::unauth(alice, sponsor)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + } + + { + Env env{*this, testableAmendments()}; + env.fund(XRP(1000000), alice, sponsor); + env.close(); + auto const authCreds = std::vector{ + {.issuer = sponsor, .credType = credType}}; + auto const preauthKeylet = keylet::depositPreauth( + alice.id(), + std::set>{ + {sponsor.id(), Slice(credType.data(), credType.size())}}); + + // Cover DepositPreauth's sfAuthorizeCredentials sponsor-reserve branch. + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env&, auto const& submit) { + submit(deposit::authCredentials(alice, authCreds)); + }); + + // Cover sfUnauthorizeCredentials cleanup for a sponsored preauth object. + BEAST_EXPECT(env.le(preauthKeylet)); + BEAST_EXPECT(env.le(preauthKeylet)->getAccountID(sfSponsor) == sponsor.id()); + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + + env(deposit::unauthCredentials(alice, authCreds)); + env.close(); + + BEAST_EXPECT(!env.le(preauthKeylet)); + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + } + } + + void + testCredential(bool cosigning) + { + testcase("Credential"); + using namespace test::jtx; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + Account const sponsor2("sponsor2"); + auto const credType = std::string("test"); + + // Self-issued credential: alice creates for herself, sponsor covers reserve + { + Env env{*this, testableAmendments()}; + env.fund(XRP(1000000), alice, bob, sponsor, sponsor2); + env.close(); + + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + submit(credentials::create(alice, alice, credType)); + }); + + auto const credKeylet = credentials::keylet(alice, alice, credType); + BEAST_EXPECT(env.le(credKeylet)->getAccountID(sfSponsor) == sponsor.id()); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + + // Transfer sponsor + if (cosigning) + { + env(sponsor::transfer(alice, tfSponsorshipReassign, credKeylet.key), + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2)); + env.close(); + } + else + { + env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::SponseeAcc(alice)); + env.close(); + env(sponsor::transfer(alice, tfSponsorshipReassign, credKeylet.key), + sponsor::As(sponsor2, spfSponsorReserve)); + env.close(); + } + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + BEAST_EXPECT(env.le(credKeylet)->getAccountID(sfSponsor) == sponsor2.id()); + + // CredentialDelete + env(credentials::deleteCred(alice, alice, alice, credType)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + } + + // Issuer creates for subject; sponsorship transfers to subject on accept + { + Env env{*this, testableAmendments()}; + env.fund(XRP(1000000), alice, bob, sponsor); + env.close(); + + // alice (issuer) creates credential for bob (subject), sponsor covers alice's reserve + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + submit(credentials::create(bob, alice, credType)); + }); + + // Before accept: alice (issuer) owns the credential + auto const credKeylet = credentials::keylet(bob, alice, credType); + BEAST_EXPECT(env.le(credKeylet)->getAccountID(sfSponsor) == sponsor.id()); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + BEAST_EXPECT(ownerCount(env, bob) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 0); + + // Bob accepts: issuer's sponsorship ends, bob becomes unsponsored owner + env(credentials::accept(bob, alice, credType)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); // original sponsorship ended + BEAST_EXPECT(ownerCount(env, bob) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 0); // bob owns it without a sponsor + + // CredentialDelete by subject + env(credentials::deleteCred(bob, bob, alice, credType)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(ownerCount(env, bob) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + } + + // Issuer creates for subject; accept tx itself is reserve-sponsored + // This exercises the checkReserve() call in CredentialAccept::doApply() + // that guards the sponsor's reserve when featureSponsor is enabled. + { + Env env{*this, testableAmendments()}; + env.fund(XRP(1000000), alice, bob, sponsor); + env.close(); + + // alice (issuer) creates credential for bob (subject) without sponsor + env(credentials::create(bob, alice, credType)); + env.close(); + + auto const credKeylet = credentials::keylet(bob, alice, credType); + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(ownerCount(env, bob) == 0); + + // Bob accepts with a sponsored reserve; the first attempt uses an + // undercapitalized sponsor and must fail with tecINSUFFICIENT_RESERVE; + // the second attempt uses a properly funded sponsor and must succeed. + testEachSponsorship( + env, + cosigning, + sponsor, + bob, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + submit(credentials::accept(bob, alice, credType)); + }); + + // After successful accept: alice (issuer) no longer owns the credential, + // bob owns it and sponsor covers his reserve. + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(ownerCount(env, bob) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + BEAST_EXPECT(env.le(credKeylet)->getAccountID(sfSponsor) == sponsor.id()); + + // Cleanup + env(credentials::deleteCred(bob, bob, alice, credType)); + env.close(); + } + } + + void + testEscrow(bool cosigning) + { + testcase("Escrow"); + using namespace test::jtx; + using namespace std::chrono_literals; + + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + Account const sponsor2("sponsor2"); + { + // Native Escrow + Env env{*this, testableAmendments()}; + auto const baseFee = env.current()->fees().base; + + env.fund(XRP(1000000), alice, bob, sponsor, sponsor2); + env.close(); + + // EscrowCreate + uint32_t seq = 0; + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + seq = env.seq(alice); + submit( + escrow::create(alice, bob, XRP(100)), + escrow::kCondition(escrow::kCb1), + escrow::kCancelTime(env.now() + 100s)); + }); + BEAST_EXPECT( + env.le(keylet::escrow(alice, seq))->getAccountID(sfSponsor) == sponsor.id()); + + // transfer sponsor + if (cosigning) + { + env(sponsor::transfer(alice, tfSponsorshipReassign, keylet::escrow(alice, seq).key), + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2)); + env.close(); + } + else + { + env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::SponseeAcc(alice)); + env.close(); + + env(sponsor::transfer(alice, tfSponsorshipReassign, keylet::escrow(alice, seq).key), + sponsor::As(sponsor2, spfSponsorReserve)); + env.close(); + } + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + + BEAST_EXPECT( + env.le(keylet::escrow(alice, seq))->getAccountID(sfSponsor) == sponsor2.id()); + + // EscrowFinish + env(escrow::finish(bob, alice, seq), + escrow::kCondition(escrow::kCb1), + escrow::kFulfillment(escrow::kFb1), + Fee(baseFee * 150)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + } + + Account const gw("gw"); + auto const usd = gw["usd"]; + { + // IOU Escrow + Env env{*this, testableAmendments()}; + auto const baseFee = env.current()->fees().base; + + env.fund(XRP(1000000), alice, bob, gw, sponsor, sponsor2); + env.close(); + + env(fset(gw, asfAllowTrustLineLocking)); + env.close(); + + env.trust(usd(1000000), alice); + env.close(); + env(pay(gw, alice, usd(10000))); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + + // EscrowCreate + uint32_t seq = 0; + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + seq = env.seq(alice); + submit( + escrow::create(alice, bob, usd(100)), + escrow::kCondition(escrow::kCb1), + escrow::kCancelTime(env.now() + 100s)); + }); + + BEAST_EXPECT( + env.le(keylet::escrow(alice, seq))->getAccountID(sfSponsor) == sponsor.id()); + + // EscrowFinish + testEachSponsorship( + env, + cosigning, + sponsor2, + bob, + 1, + 1, + tecNO_LINE_INSUF_RESERVE, + [&](Env& env, auto const& submit) { + submit( + escrow::finish(bob, alice, seq), + escrow::kCondition(escrow::kCb1), + escrow::kFulfillment(escrow::kFb1), + Fee(baseFee * 150)); + }); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + + BEAST_EXPECT( + env.le(keylet::trustLine(bob, gw, usd.currency))->getAccountID(sfHighSponsor) == + sponsor2.id()); + } + { + // IOU EscrowFinish recycles reserve when the same sponsor backs + // the escrow being removed and the destination line being created. + Env env{*this, testableAmendments()}; + auto const baseFee = env.current()->fees().base; + + env.fund(XRP(1000000), alice, bob, gw, sponsor); + env.close(); + + env(fset(gw, asfAllowTrustLineLocking)); + env.close(); + + env.trust(usd(1000000), alice); + env.close(); + env(pay(gw, alice, usd(10000))); + env.close(); + + uint32_t seq = 0; + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + seq = env.seq(alice); + submit( + escrow::create(alice, bob, usd(100)), + escrow::kCondition(escrow::kCb1), + escrow::kCancelTime(env.now() + 100s)); + }); + + BEAST_EXPECT( + env.le(keylet::escrow(alice, seq))->getAccountID(sfSponsor) == sponsor.id()); + + if (cosigning) + { + adjustAccountXRPBalance(env, sponsor, reserve(env, 1)); + } + else + { + env(sponsor::set_reserve(sponsor, 0, 1), sponsor::SponseeAcc(bob)); + env.close(); + } + + if (cosigning) + { + env(escrow::finish(bob, alice, seq), + escrow::kCondition(escrow::kCb1), + escrow::kFulfillment(escrow::kFb1), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor), + Fee(baseFee * 150), + Ter(tesSUCCESS)); + } + else + { + env(escrow::finish(bob, alice, seq), + escrow::kCondition(escrow::kCb1), + escrow::kFulfillment(escrow::kFb1), + sponsor::As(sponsor, spfSponsorReserve), + Fee(baseFee * 150), + Ter(tesSUCCESS)); + } + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(ownerCount(env, bob) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + + BEAST_EXPECT( + env.le(keylet::trustLine(bob, gw, usd.currency))->getAccountID(sfHighSponsor) == + sponsor.id()); + } + { + // IOU Escrow cancel re-creates the owner's trust line, and the + // cancel transaction's sponsor can cover that new line's reserve. + // Creating an IOU escrow moves the sender's balance to the issuer, + // so the sender can delete their now-zero trust line while the + // escrow is pending. Cancelling returns the funds, re-creating the + // line. + Env env{*this, testableAmendments()}; + + env.fund(XRP(1000000), alice, bob, gw, sponsor, sponsor2); + env.close(); + + env(fset(gw, asfAllowTrustLineLocking)); + env.close(); + + env.trust(usd(1000000), alice); + env.close(); + env(pay(gw, alice, usd(10000))); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + + // alice escrows her entire balance so the trust line can be removed + auto const cancelAfter = env.now() + 100s; + auto const seq = env.seq(alice); + env(escrow::create(alice, bob, usd(10000)), + escrow::kCondition(escrow::kCb1), + escrow::kCancelTime(cancelAfter)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 2); // trust line + escrow + + // alice deletes her now-zero trust line while the escrow is pending + env(trust(alice, usd(0))); + env.close(); + BEAST_EXPECT(!env.le(keylet::trustLine(alice, gw, usd.currency))); + BEAST_EXPECT(ownerCount(env, alice) == 1); // just the escrow + + // advance the ledger past the cancel time + for (; env.now() < cancelAfter; env.close()) + { + } + + // EscrowCancel by alice re-creates her trust line; the sponsor + // covers the new line's reserve. On the insufficient-reserve + // attempt the whole transaction rolls back, leaving the escrow + // intact to be cancelled on the success attempt. + bool const aliceLow = alice.id() < gw.id(); + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecNO_LINE_INSUF_RESERVE, + [&](Env& env, auto const& submit) { submit(escrow::cancel(alice, alice, seq)); }, + [&]() { + BEAST_EXPECT(!env.le(keylet::escrow(alice, seq))); + auto const trustSle = env.le(keylet::trustLine(alice, gw, usd.currency)); + BEAST_EXPECT(trustSle); + if (trustSle) + { + BEAST_EXPECT( + trustSle->getAccountID(aliceLow ? sfLowSponsor : sfHighSponsor) == + sponsor.id()); + BEAST_EXPECT( + !trustSle->isFieldPresent(aliceLow ? sfHighSponsor : sfLowSponsor)); + } + BEAST_EXPECT(ownerCount(env, alice) == 1); // re-created trust line + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + }); + } + { + // MPT Escrow + Env env{*this, testableAmendments()}; + env.fund(XRP(1000000), bob, sponsor); + env.close(); + + MPTTester mptGw(env, gw, {.holders = {alice}}); + mptGw.create( + {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer}); + mptGw.authorize({.account = alice}); + auto const mpt = mptGw["MPT"]; + env(pay(gw, alice, mpt(10'000))); + env.close(); + + // create Escrow from alice to bob + auto const seq = env.seq(alice); + env(escrow::create(alice, bob, mpt(100)), + escrow::kCondition(escrow::kCb1), + escrow::kCancelTime(env.now() + 100s)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 2); + BEAST_EXPECT(ownerCount(env, bob) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + + // finish Escrow: bob has no MPToken, so finishing creates one and + // the finish transaction's sponsor covers its reserve. The MPT + // create-token path returns tecINSUFFICIENT_RESERVE (not + // tecNO_LINE_INSUF_RESERVE) when the sponsor is underfunded. + testEachSponsorship( + env, + cosigning, + sponsor, + bob, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + submit( + escrow::finish(bob, alice, seq), + escrow::kCondition(escrow::kCb1), + escrow::kFulfillment(escrow::kFb1), + Fee(XRP(1))); + }); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(ownerCount(env, bob) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + BEAST_EXPECT( + env.le(keylet::mptoken(mptGw.issuanceID(), bob))->getAccountID(sfSponsor) == + sponsor.id()); + } + { + // MPT EscrowFinish has the same reserve recycling behavior as IOU + // when it creates the destination MPToken. + Env env{*this, testableAmendments()}; + auto const baseFee = env.current()->fees().base; + env.fund(XRP(1000000), bob, sponsor); + env.close(); + + MPTTester mptGw(env, gw, {.holders = {alice}}); + mptGw.create( + {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanEscrow | tfMPTCanTransfer}); + mptGw.authorize({.account = alice}); + auto const mpt = mptGw["MPT"]; + env(pay(gw, alice, mpt(10'000))); + env.close(); + + uint32_t seq = 0; + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + seq = env.seq(alice); + submit( + escrow::create(alice, bob, mpt(100)), + escrow::kCondition(escrow::kCb1), + escrow::kCancelTime(env.now() + 100s)); + }); + + BEAST_EXPECT( + env.le(keylet::escrow(alice, seq))->getAccountID(sfSponsor) == sponsor.id()); + + if (cosigning) + { + adjustAccountXRPBalance(env, sponsor, reserve(env, 1)); + } + else + { + env(sponsor::set_reserve(sponsor, 0, 1), sponsor::SponseeAcc(bob)); + env.close(); + } + + if (cosigning) + { + env(escrow::finish(bob, alice, seq), + escrow::kCondition(escrow::kCb1), + escrow::kFulfillment(escrow::kFb1), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor), + Fee(baseFee * 150), + Ter(tesSUCCESS)); + } + else + { + env(escrow::finish(bob, alice, seq), + escrow::kCondition(escrow::kCb1), + escrow::kFulfillment(escrow::kFb1), + sponsor::As(sponsor, spfSponsorReserve), + Fee(baseFee * 150), + Ter(tesSUCCESS)); + } + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(ownerCount(env, bob) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + + BEAST_EXPECT( + env.le(keylet::mptoken(mptGw.issuanceID(), bob))->getAccountID(sfSponsor) == + sponsor.id()); + } + + // A sponsored EscrowCreate must still verify that the source + // can fund the escrow amount and stay above its own base + // reserve. The sponsor covers the new object's owner + // increment, but cannot cover the source's base reserve. + { + Env env{*this, testableAmendments()}; + env.fund(XRP(10000), alice, bob, sponsor); + env.close(); + + // alice's balance is just above the base reserve. After + // locking escrowAmount she would dip below it. + adjustAccountXRPBalance(env, alice, accountReserve(env, 1) + XRP(1)); + + auto const escrowAmount = XRP(2); + auto const seq = env.seq(alice); + + if (cosigning) + { + env(escrow::create(alice, bob, escrowAmount), + escrow::kCondition(escrow::kCb1), + escrow::kCancelTime(env.now() + 100s), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor), + Ter(tecUNFUNDED)); + } + else + { + env(sponsor::set(sponsor, 0, 1, XRP(1)), sponsor::SponseeAcc(alice)); + env.close(); + + env(escrow::create(alice, bob, escrowAmount), + escrow::kCondition(escrow::kCb1), + escrow::kCancelTime(env.now() + 100s), + sponsor::As(sponsor, spfSponsorReserve), + Ter(tecUNFUNDED)); + } + env.close(); + + BEAST_EXPECT(!env.le(keylet::escrow(alice, seq))); + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + } + } + + void + testMPToken(bool cosigning) + { + testcase("MPToken"); + using namespace test::jtx; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + Account const sponsor2("sponsor2"); + + { + Env env{*this, testableAmendments()}; + env.fund(XRP(1000000), alice, bob, sponsor, sponsor2); + env.close(); + + // MPTokenIssuanceCreate + json::Value jv = {}; + jv[sfAccount] = alice.human(); + jv[sfTransactionType] = jss::MPTokenIssuanceCreate; + MPTID mptid; + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + mptid = makeMptID(env.seq(alice), alice.id()); + submit(jv); + }); + + // transfer sponsor + auto const mptIssuanceKeylet = keylet::mptokenIssuance(mptid); + + if (cosigning) + { + env(sponsor::transfer(alice, tfSponsorshipReassign, mptIssuanceKeylet.key), + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2)); + env.close(); + } + else + { + env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::SponseeAcc(alice)); + env.close(); + + env(sponsor::transfer(alice, tfSponsorshipReassign, mptIssuanceKeylet.key), + sponsor::As(sponsor2, spfSponsorReserve)); + env.close(); + } + + // MPTokenAuthorize + jv = {}; + jv[sfTransactionType] = jss::MPTokenAuthorize; + jv[sfAccount] = bob.human(); + jv[sfMPTokenIssuanceID] = to_string(mptid); + + if (cosigning) + { + adjustAccountXRPBalance(env, sponsor, reserve(env, 2)); + env(ticket::create(sponsor, 2)); // adjust for free mptoken + env.close(); + } + + testEachSponsorship( + env, + cosigning, + sponsor, + bob, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { submit(jv); }); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + + // transfer sponsor + auto const mptTokenKeylet = keylet::mptoken(mptid, bob); + if (cosigning) + { + env(sponsor::transfer(bob, tfSponsorshipReassign, mptTokenKeylet.key), + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2)); + env.close(); + } + else + { + env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::SponseeAcc(bob)); + env.close(); + + env(sponsor::transfer(bob, tfSponsorshipReassign, mptTokenKeylet.key), + sponsor::As(sponsor2, spfSponsorReserve)); + env.close(); + } + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(ownerCount(env, bob) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 2); + + // MPTokenAuthorize Unauthorize + jv = {}; + jv[sfTransactionType] = jss::MPTokenAuthorize; + jv[sfAccount] = bob.human(); + jv[sfMPTokenIssuanceID] = to_string(mptid); + jv[sfFlags] = tfMPTUnauthorize; + env(jv); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(ownerCount(env, bob) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + + // MPTokenIssuanceDestroy + jv = {}; + jv[sfTransactionType] = jss::MPTokenIssuanceDestroy; + jv[sfAccount] = alice.human(); + jv[sfMPTokenIssuanceID] = to_string(mptid); + env(jv); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + } + { + // check INSUFFICIENT_RESERVE for MPToken + Env env{*this, testableAmendments()}; + env.fund(XRP(1000000), alice, bob, sponsor); + env.close(); + + // MPTokenAuthorize + json::Value jv = {}; + jv[sfAccount] = alice.human(); + jv[sfTransactionType] = jss::MPTokenIssuanceCreate; + auto const mptid = makeMptID(env.seq(alice), alice.id()); + env(jv); + env.close(); + + // Create tickets so the sponsor is past free-tier reserve behavior. + std::uint32_t const ticketSeq{env.seq(sponsor) + 1}; + env(ticket::create(sponsor, 2)); + env.close(); + + jv = {}; + jv[sfTransactionType] = jss::MPTokenAuthorize; + jv[sfAccount] = bob.human(); + jv[sfMPTokenIssuanceID] = to_string(mptid); + // error (non-free mptoken) + if (cosigning) + { + adjustAccountXRPBalance(env, sponsor, reserve(env, 3) - drops(1)); + env(jv, + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor), + Ter(tecINSUFFICIENT_RESERVE)); + env.close(); + } + else + { + env(sponsor::set(sponsor, 0, std::nullopt, XRP(1)), sponsor::SponseeAcc(bob)); + env.close(); + + env(jv, sponsor::As(sponsor, spfSponsorReserve), Ter(tecINSUFFICIENT_RESERVE)); + env.close(); + } + + env(noop(sponsor), ticket::Use(ticketSeq)); + env.close(); + + // pass (free-tier mptoken for the holder, but the sponsor is still + // charged a reserve increment regardless of the ownerCount < 2 shortcut). + if (cosigning) + { + adjustAccountXRPBalance(env, sponsor, reserve(env, 2)); + env(jv, + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor), + Ter(tesSUCCESS)); + env.close(); + } + else + { + env(sponsor::set_reserve(sponsor, 0, 1), sponsor::SponseeAcc(bob)); + env.close(); + env(jv, sponsor::As(sponsor, spfSponsorReserve), Ter(tesSUCCESS)); + env.close(); + } + } + } + + void + testPayChan(bool cosigning) + { + testcase("PayChan"); + using namespace test::jtx; + using namespace std::literals::chrono_literals; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + Account const sponsor2("sponsor2"); + + { + Env env{*this, testableAmendments()}; + env.fund(XRP(1000000), alice, bob, sponsor, sponsor2); + env.close(); + + // PayChanCreate + auto const pk = alice.pk(); + auto const settleDelay = 10s; + uint256 chan; + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { + chan = paychan::channel(alice, bob, env.seq(alice)); + submit(paychan::create(alice, bob, XRP(100), settleDelay, pk)); + }); + + // the created PayChannel object carries the sponsor + BEAST_EXPECT(env.le(Keylet(ltPAYCHAN, chan))->getAccountID(sfSponsor) == sponsor.id()); + + // transfer sponsor + if (cosigning) + { + env(sponsor::transfer(alice, tfSponsorshipReassign, chan), + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2)); + env.close(); + } + else + { + env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::SponseeAcc(alice)); + env.close(); + + env(sponsor::transfer(alice, tfSponsorshipReassign, chan), + sponsor::As(sponsor2, spfSponsorReserve)); + env.close(); + } + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + BEAST_EXPECT(env.le(Keylet(ltPAYCHAN, chan))->getAccountID(sfSponsor) == sponsor2.id()); + + env.close(env.now() + settleDelay); + // PayChanClaim (delete PayChan) + env(paychan::claim(bob, chan), Txflags(tfClose)); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + } + + // A sponsored PaymentChannelCreate must still verify that the + // source can fund the channel amount and stay above its own + // base reserve. The sponsor covers the new object's owner + // increment, but cannot cover the source's base reserve. + { + Env env{*this, testableAmendments()}; + env.fund(XRP(10000), alice, bob, sponsor); + env.close(); + + // alice's balance is just above the base reserve. After + // locking channelAmount she would dip below it. + adjustAccountXRPBalance(env, alice, accountReserve(env, 1) + XRP(1)); + + auto const pk = alice.pk(); + auto const settleDelay = 10s; + auto const channelAmount = XRP(2); + auto const chan = paychan::channel(alice, bob, env.seq(alice)); + + if (cosigning) + { + env(paychan::create(alice, bob, channelAmount, settleDelay, pk), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor), + Ter(tecUNFUNDED)); + } + else + { + env(sponsor::set(sponsor, 0, 1, XRP(1)), sponsor::SponseeAcc(alice)); + env.close(); + + env(paychan::create(alice, bob, channelAmount, settleDelay, pk), + sponsor::As(sponsor, spfSponsorReserve), + Ter(tecUNFUNDED)); + } + env.close(); + + BEAST_EXPECT(!paychan::channelExists(*env.current(), chan)); + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + } + } + + void + testSignerList(bool cosigning) + { + testcase("SignerList"); + using namespace test::jtx; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + Account const sponsor2("sponsor2"); + + Env env{*this, testableAmendments()}; + env.fund(XRP(1000000), alice, bob, sponsor, sponsor2); + env.close(); + + // SignerListSet + testEachSponsorship( + env, + cosigning, + sponsor, + alice, + 1, + 1, + tecINSUFFICIENT_RESERVE, + [&](Env& env, auto const& submit) { submit(signers(alice, 1, {{bob, 1}})); }); + + // the created SignerList object carries the sponsor + BEAST_EXPECT(env.le(keylet::signerList(alice))->getAccountID(sfSponsor) == sponsor.id()); + + // transfer sponsor + if (cosigning) + { + // invalid signer list owner 1 + // account doesn't have signer list but specified signer list exists + env(sponsor::transfer(bob, tfSponsorshipReassign, keylet::signerList(alice).key), + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2), + Ter(tecNO_PERMISSION)); + // invalid signer list owner 2 + // account has signer list and specified signer list exists + env(signers(bob, 1, {{alice, 1}})); + env.close(); + env(sponsor::transfer(alice, tfSponsorshipReassign, keylet::signerList(bob).key), + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2), + Ter(tecNO_PERMISSION)); + env(sponsor::transfer(alice, tfSponsorshipReassign, keylet::signerList(alice).key), + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2)); + env.close(); + } + else + { + env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::SponseeAcc(alice)); + env.close(); + env(sponsor::transfer(alice, tfSponsorshipReassign, keylet::signerList(alice).key), + sponsor::As(sponsor2, spfSponsorReserve)); + env.close(); + } + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + BEAST_EXPECT(env.le(keylet::signerList(alice))->getAccountID(sfSponsor) == sponsor2.id()); + + // Delete + env(signers(alice, NoneT())); + env.close(); + + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); + } + + // Legacy (pre-MultiSignReserve) SignerLists lack lsfOneOwnerCount and cost + // 2 + signer_count owner units, whereas modern lists cost 1. + void + testLegacySignerListReserve() + { + testcase("Legacy SignerList sponsorship reserve"); + using namespace test::jtx; + + Account const alice("alice"); + Account const bob("bob"); + Account const carol("carol"); + Account const dave("dave"); + Account const sponsor("sponsor"); + + Env env{*this, testableAmendments()}; + env.fund(XRP(1000000), alice, bob, carol, dave, sponsor); + env.close(); + + // Modern 3-signer list: weight 1, lsfOneOwnerCount set. + env(signers(alice, 1, {{bob, 1}, {carol, 1}, {dave, 1}})); + env.close(); + + auto const signerListKeylet = keylet::signerList(alice.id()); + auto const sponsorKeylet = keylet::sponsorship(sponsor.id(), alice.id()); + std::uint32_t const legacyWeight = 5; // 2 + 3 signers + BEAST_EXPECT(ownerCount(env, alice) == 1); + + // Pre-fund exactly the legacy weight + env(sponsor::set_reserve(sponsor, 0, legacyWeight), sponsor::SponseeAcc(alice)); + env.close(); + if (auto const sle = env.le(sponsorKeylet); BEAST_EXPECT(sle)) + BEAST_EXPECT(sle->getFieldU32(sfRemainingOwnerCount) == legacyWeight); + + // Synthesize a pre-MultiSignReserve list: clear lsfOneOwnerCount and + // restore the owner's OwnerCount to the legacy weight. + env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal) -> bool { + auto signerList = std::make_shared(*view.read(signerListKeylet)); + auto account = std::make_shared(*view.read(keylet::account(alice.id()))); + signerList->clearFlag(lsfOneOwnerCount); + account->setFieldU32(sfOwnerCount, legacyWeight); + view.rawReplace(signerList); + view.rawReplace(account); + return true; + }); + if (auto const sle = env.le(signerListKeylet); BEAST_EXPECT(sle)) + BEAST_EXPECT((sle->getFlags() & lsfOneOwnerCount) == 0); + BEAST_EXPECT(ownerCount(env, alice) == legacyWeight); + + // Create must charge the full legacy weight (5), not 1: the bug bumped + // the counters by 1 and left 4 pre-funded units unspent. + env(sponsor::transfer(alice, tfSponsorshipCreate, signerListKeylet.key), + sponsor::As(sponsor, spfSponsorReserve)); + + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == legacyWeight); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == legacyWeight); + // All pre-funded units consumed (drained to absent). + if (auto const sle = env.le(sponsorKeylet); BEAST_EXPECT(sle)) + BEAST_EXPECT(!sle->isFieldPresent(sfRemainingOwnerCount)); + + // Delete unwinds the legacy weight; create bumped by the same amount, so + // the counters return to 0 (the bug bumped by 1 -> underflow on delete). + env(signers(alice, NoneT())); + + BEAST_EXPECT(!env.le(signerListKeylet)); + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + if (auto const sle = env.le(sponsorKeylet); BEAST_EXPECT(sle)) + BEAST_EXPECT(!sle->isFieldPresent(sfRemainingOwnerCount)); + } + + void + testSponsoredTrustLineNoFreeReserve() + { + // An account with ownerCount < 2 may create its first trust lines even + // without meeting the reserve. In any case, the sponsor pays the full + // reserve in all cases, even for the sponsee's very first trust line. + testcase("Sponsored trust line gets no free-reserve exception"); + using namespace test::jtx; + + Account const issuer("issuer"); + Account const alice("alice"); + Account const sponsor("sponsor"); + + Env env{*this, testableAmendments()}; + env.fund(XRP(10000), issuer, alice, sponsor); + env.close(); + + auto const usd = issuer["usd"]; + auto const lineKeylet = keylet::trustLine(alice, issuer, usd.currency); + + // Sponsor funded for exactly its base reserve + adjustAccountXRPBalance(env, sponsor, reserve(env, 0)); + + // alice's ownerCount is 0, so an unsponsored first trust line would be + // free; but because it is sponsored, the reserve check is enforced + // against the sponsor, which is one increment short. + env(trust(alice, usd(100)), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor), + Ter(tecNO_LINE_INSUF_RESERVE)); + env.close(); + + BEAST_EXPECT(!env.le(lineKeylet)); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + + // Give the sponsor has exactly one owner-reserve increment; the same + // sponsored first trust line now succeeds and the sponsor pays for it. + adjustAccountXRPBalance(env, sponsor, reserve(env, 1)); + + env(trust(alice, usd(100)), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); + env.close(); + + BEAST_EXPECT(env.le(lineKeylet)); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(ownerCount(env, alice) == 1); + } + + void + testCoSignReserveBoundedBySponsorshipBudget() + { + // sponsor co-signs, so a fee-only object (ReserveCount == 0) makes a co-signed + // reserve sponsorship fail -- with no fallback to the sponsor's balance. + testcase("Co-signed reserve sponsorship is bounded by Sponsorship budget"); + using namespace test::jtx; + + Env env{*this, testableAmendments()}; + Account const sponsor("sponsor"); + Account const sponsee("sponsee"); + env.fund(XRP(10000), sponsor, sponsee); + env.close(); + + // Prefund a FEE-only Sponsorship for the sponsee; ReserveCount + // defaults to 0. + env(sponsor::set_fee(sponsor, 0, XRP(100)), sponsor::SponseeAcc(sponsee)); + env.close(); + BEAST_EXPECT(env.le(keylet::sponsorship(sponsor, sponsee))); + + // Sponsee creates a Check with the sponsor co-signing the reserve. The + // fee-only Sponsorship's has ReserveCount (0), so this fails + // with tecINSUFFICIENT_RESERVE + env(check::create(sponsee, sponsor, XRP(1)), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor), + Ter(tecINSUFFICIENT_RESERVE)); + env.close(); + + BEAST_EXPECT(ownerCount(env, sponsee) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, sponsee) == 0); + + // Bumping the Sponsorship's ReserveCount budget makes the same + // co-signed reserve sponsorship succeed, the budget is what gates it. + env(sponsor::set_reserve(sponsor, 0, 1), sponsor::SponseeAcc(sponsee)); + env.close(); + + env(check::create(sponsee, sponsor, XRP(1)), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor), + Ter(tesSUCCESS)); + env.close(); + + BEAST_EXPECT(ownerCount(env, sponsee) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, sponsee) == 1); + } + + void + testTrustSet(bool cosigning) + { + testcase("TrustSet"); + using namespace test::jtx; + Account const alice("alice"); + Account const bob("bob"); + Account const charlie("charlie"); + Account const sponsor("sponsor"); + Account const sponsor2("sponsor2"); + + auto const validateSponsoredTrustline = + [&](std::shared_ptr const& sle, bool isIssuerHigh, Account const& sponsor) { + BEAST_EXPECT( + sle->getAccountID(isIssuerHigh ? sfLowSponsor : sfHighSponsor) == sponsor.id()); + BEAST_EXPECT(!sle->isFieldPresent(isIssuerHigh ? sfHighSponsor : sfLowSponsor)); + }; + + auto const& highAcc = alice > bob ? alice : bob; + auto const& lowAcc = alice > bob ? bob : alice; + + // create and delete + for (bool const isIssuerHigh : {false, true}) + { + Env env{*this, testableAmendments()}; + env.fund(XRP(1000000), alice, bob, charlie, sponsor, sponsor2); + env.close(); + + auto const& issuer = isIssuerHigh ? highAcc : lowAcc; + auto const& user = isIssuerHigh ? lowAcc : highAcc; + + auto const usd = issuer["usd"]; + auto const currency = usd.currency; + + // create TrustLine + if (cosigning) + { + adjustAccountXRPBalance(env, sponsor, reserve(env, 2)); + env(ticket::create(sponsor, 2)); // adjust for free trustline + env.close(); + } + + testEachSponsorship( + env, + cosigning, + sponsor, + user, + 1, + 1, + tecNO_LINE_INSUF_RESERVE, + [&](Env& env, auto const& submit) { submit(trust(user, usd(100))); }); + + auto const keylet = keylet::trustLine(user, issuer, currency); + + if (cosigning) + { + // invalid owner + env(sponsor::transfer(charlie, tfSponsorshipReassign, keylet.key), + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2), + Ter(tecNO_PERMISSION)); + // invalid reserve owner + env(sponsor::transfer(issuer, tfSponsorshipReassign, keylet.key), + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2), + Ter(tecNO_PERMISSION)); + env(sponsor::transfer(user, tfSponsorshipReassign, keylet.key), + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2)); + env.close(); + } + else + { + env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::SponseeAcc(user)); + env.close(); + env(sponsor::transfer(user, tfSponsorshipReassign, keylet.key), + sponsor::As(sponsor2, spfSponsorReserve)); + env.close(); + } + + // delete TrustLine + env(trust(user, usd(0))); + env.close(); + + BEAST_EXPECT(ownerCount(env, user) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, user) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + + BEAST_EXPECT(!env.le(keylet)); + } + + // update + for (bool const isIssuerHigh : {false, true}) + { + Env env{*this, testableAmendments()}; + env.fund(XRP(1000000), alice, bob, sponsor, sponsor2); + env.close(); + + auto const& issuer = isIssuerHigh ? highAcc : lowAcc; + auto const& user = isIssuerHigh ? lowAcc : highAcc; + + auto const usd = issuer["usd"]; + auto const currency = usd.currency; + + // create TrustLine from issuer + env(trust(issuer, user["usd"](100))); + env.close(); + + BEAST_EXPECT(env.le(keylet::trustLine(user, issuer, currency))); + + if (cosigning) + { + adjustAccountXRPBalance(env, sponsor, reserve(env, 2)); + env(ticket::create(sponsor, 2)); // adjust for free trustline + env.close(); + } + + testEachSponsorship( + env, + cosigning, + sponsor, + user, + 1, + 1, + tecINSUF_RESERVE_LINE, + [&](Env& env, auto const& submit) { submit(trust(user, usd(100))); }); + + auto const line = env.le(keylet::trustLine(user, issuer, currency)); + validateSponsoredTrustline(line, isIssuerHigh, sponsor); + + // update TrustLine from user to clear reserve + env(trust(user, usd(0))); + env.close(); + + BEAST_EXPECT(ownerCount(env, user) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, user) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(env.le(keylet::trustLine(user, issuer, currency))); + + // remove TrustLine from issuer + env(trust(issuer, user["usd"](0))); + env.close(); + BEAST_EXPECT(!env.le(keylet::trustLine(user, issuer, currency))); + } + + // both High and Low sponsored + { + Env env{*this, testableAmendments()}; + env.fund(XRP(1000000), alice, bob, sponsor); + env.close(); + + // create TrustLines + env(trust(alice, bob["usd"](100)), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); + env.close(); + env(trust(bob, alice["usd"](100)), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); + env.close(); + + auto sle = env.le(keylet::trustLine(alice, bob, alice["usd"].currency)); + BEAST_EXPECT(sle); + BEAST_EXPECT(sle->isFlag(lsfHighReserve)); + BEAST_EXPECT(sle->isFlag(lsfLowReserve)); + BEAST_EXPECT(sle->getAccountID(sfHighSponsor) == sponsor.id()); + BEAST_EXPECT(sle->getAccountID(sfLowSponsor) == sponsor.id()); + + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(ownerCount(env, bob) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 2); + + // clear TrustLines + env(trust(alice, bob["usd"](0))); + env.close(); + env(trust(bob, alice["usd"](0))); + env.close(); + + sle = env.le(keylet::trustLine(alice, bob, alice["usd"].currency)); + BEAST_EXPECT(!sle); + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(ownerCount(env, bob) == 0); + BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + } + } + + void + testAccountDelete() + { + testcase("AccountDelete"); + using namespace test::jtx; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + + { + // Delete Sponsor/Sponsee Account with ltSponsorship (tecHAS_OBLIGATIONS) + Env env{*this, testableAmendments()}; + env.fund(XRP(1000000), alice, bob, sponsor); + env.close(); + + // set sponsor + env(sponsor::set(sponsor, 0, 100, XRP(100)), + sponsor::SponseeAcc(alice), + Ter(tesSUCCESS)); + env.close(); + + incLgrSeqForAccDel(env, sponsor); + + auto const keylet = keylet::sponsorship(sponsor, alice); + auto const sponsorObj = env.le(keylet); + BEAST_EXPECT(sponsorObj); + + // AccountDelete + auto const requiredFee = drops(env.current()->fees().increment); + env(acctdelete(alice, bob), Fee(requiredFee), Ter(tecHAS_OBLIGATIONS)); + env(acctdelete(sponsor, bob), Fee(requiredFee), Ter(tecHAS_OBLIGATIONS)); + } + + { + // Delete SponsoredAccount + Env env{*this, testableAmendments()}; + env.memoize(alice); + env.fund(XRP(1000000), bob, sponsor); + env.close(); + + // create SponsoredAccount + env(pay(sponsor, alice, XRP(10000)), Txflags(tfSponsorCreatedAccount)); + env.close(); + + incLgrSeqForAccDel(env, alice); + + // AccountDelete: destination = non-sponsor + auto const requiredFee = drops(env.current()->fees().increment); + env(acctdelete(alice, bob), Fee(requiredFee), Ter(tecNO_SPONSOR_PERMISSION)); + + // The failed delete must leave the account sponsored by the original sponsor. + auto const aliceSle = env.le(keylet::account(alice)); + BEAST_EXPECT(aliceSle); + if (aliceSle) + BEAST_EXPECT(aliceSle->getAccountID(sfSponsor) == sponsor.id()); + + auto const sponsorSle = env.le(keylet::account(sponsor)); + BEAST_EXPECT(sponsorSle->getFieldU32(sfSponsoringAccountCount) == 1); + + incLgrSeqForAccDel(env, alice); + + // AccountDelete: destination = sponsor + env(acctdelete(alice, sponsor), Fee(requiredFee), Ter(tesSUCCESS)); + + auto const sponsorSle2 = env.le(keylet::account(sponsor)); + BEAST_EXPECT(!sponsorSle2->isFieldPresent(sfSponsoringAccountCount)); + } + + { + // Sponsor with sfSponsoringOwnerCount cannot delete (tecHAS_OBLIGATIONS) + Env env{*this, testableAmendments()}; + Account const gw("gw"); + env.fund(XRP(1000000), alice, bob, sponsor, gw); + env.close(); + + auto const usd = gw["usd"]; + + // Create a trust line for alice + env(trust(alice, usd(1000))); + env.close(); + + // Transfer reserve sponsorship of trust line to sponsor + auto const trustId = keylet::trustLine(alice, gw, usd.currency); + BEAST_EXPECT(env.le(trustId)); + + env(sponsor::transfer(alice, tfSponsorshipCreate, trustId.key), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); + env.close(); + + // Verify sfSponsoringOwnerCount is set on sponsor + auto const sponsorSle = env.le(keylet::account(sponsor)); + BEAST_EXPECT(sponsorSle->isFieldPresent(sfSponsoringOwnerCount)); + auto const sponsoringOwnerCount = sponsorSle->getFieldU32(sfSponsoringOwnerCount); + BEAST_EXPECT(sponsoringOwnerCount >= 1); + + incLgrSeqForAccDel(env, sponsor); + + // AccountDelete should fail + auto const requiredFee = drops(env.current()->fees().increment); + env(acctdelete(sponsor, bob), Fee(requiredFee), Ter(tecHAS_OBLIGATIONS)); + // The failed delete must not decrement the outstanding sponsored-object count. + auto const sponsorSleAfter = env.le(keylet::account(sponsor)); + BEAST_EXPECT(sponsorSleAfter->isFieldPresent(sfSponsoringOwnerCount)); + BEAST_EXPECT( + sponsorSleAfter->getFieldU32(sfSponsoringOwnerCount) == sponsoringOwnerCount); + } + + { + // Sponsor with sfSponsoringAccountCount cannot delete (tecHAS_OBLIGATIONS) + Env env{*this, testableAmendments()}; + env.memoize(alice); + env.fund(XRP(1000000), bob, sponsor); + env.close(); + + // Create SponsoredAccount (sets sfSponsoringAccountCount on sponsor) + env(pay(sponsor, alice, XRP(10000)), Txflags(tfSponsorCreatedAccount)); + env.close(); + + // Verify sfSponsoringAccountCount is set on sponsor + auto const sponsorSle = env.le(keylet::account(sponsor)); + BEAST_EXPECT(sponsorSle->isFieldPresent(sfSponsoringAccountCount)); + auto const sponsoringAccountCount = sponsorSle->getFieldU32(sfSponsoringAccountCount); + BEAST_EXPECT(sponsoringAccountCount == 1); + + incLgrSeqForAccDel(env, sponsor); + + // AccountDelete should fail + auto const requiredFee = drops(env.current()->fees().increment); + env(acctdelete(sponsor, bob), Fee(requiredFee), Ter(tecHAS_OBLIGATIONS)); + // The failed delete must not decrement the outstanding sponsored-account count. + auto const sponsorSleAfter = env.le(keylet::account(sponsor)); + BEAST_EXPECT(sponsorSleAfter->isFieldPresent(sfSponsoringAccountCount)); + BEAST_EXPECT( + sponsorSleAfter->getFieldU32(sfSponsoringAccountCount) == sponsoringAccountCount); + } + + { + // Account with sponsored objects should be deletable + Env env{*this, testableAmendments()}; + env.fund(XRP(1000000), alice, bob, sponsor); + env.close(); + + // Create sponsored delegate (a non-deletion-blocker) + env(deposit::auth(alice, bob), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); + + { + auto const sponsorSle = env.le(keylet::account(sponsor)); + auto const aliceSle = env.le(keylet::account(alice)); + BEAST_EXPECT(sponsorSle->at(sfSponsoringOwnerCount) == 1); + BEAST_EXPECT(aliceSle->at(sfOwnerCount) == 1); + BEAST_EXPECT(aliceSle->at(sfSponsoredOwnerCount) == 1); + } + + incLgrSeqForAccDel(env, alice); + + // AccountDelete should succeed + { + auto const requiredFee = drops(env.current()->fees().increment); + env(acctdelete(alice, bob), Fee(requiredFee), Ter(tesSUCCESS)); + BEAST_EXPECT(!env.le(keylet::account(alice))); + auto const sponsorSle = env.le(keylet::account(sponsor)); + BEAST_EXPECT(sponsorSle->at(sfSponsoringOwnerCount) == 0); + } + } + } + + void + testDelegatePermission() + { + testcase("DelegatePermission"); + using namespace test::jtx; + Account const alice("alice"); + Account const bob("bob"); + Account const carol("carol"); + + // + // SponsorshipTransfer is not delegable. + // + { + Env env{*this, testableAmendments()}; + env.fund(XRP(1000000), alice, bob, carol); + env.close(); + + auto const seq = env.seq(alice); + env(check::create(alice, bob, XRP(1))); + env.close(); + + auto const keylet = keylet::check(alice, seq); + + env(sponsor::transfer(alice, tfSponsorshipCreate, keylet.key), + sponsor::As(bob, spfSponsorReserve), + Sig(sfSponsorSignature, bob), + delegate::As(carol), + Ter(temINVALID)); + + env(delegate::set(alice, carol, {"SponsorshipTransfer"}), Ter(temMALFORMED)); + } + // + // test send SponsorshipSet on other's behalf + // + { + Env env{*this, testableAmendments()}; + env.fund(XRP(1000000), alice, bob, carol); + env.close(); + + env(sponsor::set(alice, 0, 100, XRP(100)), + sponsor::SponseeAcc(bob), + delegate::As(carol), + Ter(terNO_DELEGATE_PERMISSION)); + + env(delegate::set(alice, carol, {"SponsorshipSet"})); + env.close(); + + env(sponsor::set(alice, 0, 100, XRP(100)), + sponsor::SponseeAcc(bob), + delegate::As(carol), + Ter(tesSUCCESS)); + env.close(); + } + } + + void + testDelegateBlockReserveSponsor() + { + testcase("Delegate Block Reserve Sponsor"); + using namespace test::jtx; + Account const alice("alice"); + Account const bob("bob"); + Account const carol("carol"); + Account const sponsor("sponsor"); + + // Co-signed reserve sponsorship is blocked for delegated transactions. + { + Env env{*this, testableAmendments()}; + env.fund(XRP(1000000), alice, bob, carol, sponsor); + env.close(); + + env(delegate::set(alice, bob, {"CheckCreate"})); + env.close(); + + env(check::create(alice, carol, XRP(1)), + delegate::As(bob), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor), + Ter(temINVALID)); + } + + // Pre-funded reserve sponsorship is blocked for delegated transactions. + { + Env env{*this, testableAmendments()}; + env.fund(XRP(1000000), alice, bob, carol, sponsor); + env.close(); + + env(delegate::set(alice, bob, {"CheckCreate"})); + env(sponsor::set_reserve(sponsor, 0, 1), sponsor::SponseeAcc(bob)); + env.close(); + + env(check::create(alice, carol, XRP(1)), + delegate::As(bob), + sponsor::As(sponsor, spfSponsorReserve), + Ter(temINVALID)); + } + } + + void + testDelegateSponsorFeePayer() + { + testcase("Delegate Sponsor Fee Payer"); + using namespace test::jtx; + Account const alice("alice"); + Account const bob("bob"); + Account const carol("carol"); + Account const sponsor("sponsor"); + + // Co-signing: the sponsor account pays the delegated transaction fee. + { + Env env{*this, testableAmendments()}; + env.fund(XRP(10000), alice, bob, carol, sponsor); + env.close(); + + env(delegate::set(alice, bob, {"Payment"})); + env.close(); + + auto const aliceBalance = env.balance(alice); + auto const bobBalance = env.balance(bob); + auto const carolBalance = env.balance(carol); + auto const sponsorBalance = env.balance(sponsor); + auto const sendAmt = XRP(100); + auto const feeAmt = XRP(10); + + env(pay(alice, carol, sendAmt), + delegate::As(bob), + Fee(feeAmt), + sponsor::As(sponsor, spfSponsorFee), + Sig(sfSponsorSignature, sponsor)); + env.close(); + + BEAST_EXPECT(env.balance(alice) == aliceBalance - sendAmt); + BEAST_EXPECT(env.balance(bob) == bobBalance); + BEAST_EXPECT(env.balance(carol) == carolBalance + sendAmt); + // sponsor pays the fee + BEAST_EXPECT(env.balance(sponsor) == sponsorBalance - feeAmt); + } + + // Pre-funded: the Sponsorship object for sponsorship(sponsor, delegate) + // pays the fee. + { + Env env{*this, testableAmendments()}; + env.fund(XRP(10000), alice, bob, carol, sponsor); + env.close(); + + env(delegate::set(alice, bob, {"Payment"})); + env(sponsor::set_fee(sponsor, 0, XRP(100)), sponsor::SponseeAcc(bob)); + env.close(); + + auto const aliceBalance = env.balance(alice); + auto const bobBalance = env.balance(bob); + auto const carolBalance = env.balance(carol); + auto const sponsorBalance = env.balance(sponsor); + auto const sponsorFee = sponsor::sponsorshipFeeBalance(env, sponsor, bob); + auto const sendAmt = XRP(100); + auto const feeAmt = XRP(10); + + // verify sponsorship(sponsor, alice) is not present, because we are testing + // sponsorship(sponsor, bob) will pay the fee. bob is sfDelegate. + BEAST_EXPECT(!env.le(keylet::sponsorship(sponsor, alice))); + + env(pay(alice, carol, sendAmt), + delegate::As(bob), + Fee(feeAmt), + sponsor::As(sponsor, spfSponsorFee)); + env.close(); + + BEAST_EXPECT(env.balance(alice) == aliceBalance - sendAmt); + BEAST_EXPECT(env.balance(bob) == bobBalance); + BEAST_EXPECT(env.balance(carol) == carolBalance + sendAmt); + BEAST_EXPECT(env.balance(sponsor) == sponsorBalance); + // sponsorship(sponsor, bob) pays the fee, bob is sfDelegate + BEAST_EXPECT(sponsor::sponsorshipFeeBalance(env, sponsor, bob) == sponsorFee - feeAmt); + } + } + + void + testBatch() + { + testcase("Batch"); + using namespace test::jtx; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + + // + // outer transaction + // + { + // test outer transaction with co-signing sponsor + Env env{*this, testableAmendments()}; + env.fund(XRP(1000), alice, bob, sponsor); + env.close(); + + auto const seq = env.seq(alice); + env(batch::outer(alice, seq, XRP(1), tfAllOrNothing), + batch::Inner(noop(alice), seq + 1), + batch::Inner(ticket::create(alice, 1), seq + 2), + sponsor::As(sponsor, spfSponsorFee), + Sig(sfSponsorSignature, sponsor), + Ter(tesSUCCESS)); + env.close(); + + // does not affect reserve + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + + // Fee is paid by sponsor + BEAST_EXPECT(env.balance(alice) == XRP(1000)); + BEAST_EXPECT(env.balance(sponsor) == XRP(1000 - 1)); + } + { + Env env{*this, testableAmendments()}; + env.fund(XRP(1000), alice, bob, sponsor); + env.close(); + + // spfSponsorReserve on outer Batch is rejected + for (auto const flags : {spfSponsorReserve | spfSponsorFee, spfSponsorReserve}) + { + auto const seq = env.seq(alice); + env(batch::outer(alice, seq, XRP(1), tfAllOrNothing), + batch::Inner(noop(alice), seq + 1), + batch::Inner(noop(alice), seq + 2), + sponsor::As(sponsor, flags), + Sig(sfSponsorSignature, sponsor), + Ter(temINVALID_FLAG)); + env.close(); + } + } + { + // test outer transaction with prefunded sponsor + Env env{*this, testableAmendments()}; + env.fund(XRP(1000), alice, bob); + env.fund(XRP(1001), sponsor); + env.close(); + + env(sponsor::set(sponsor, 0, 100, XRP(100)), + sponsor::SponseeAcc(alice), + Fee(XRP(1)), + Ter(tesSUCCESS)); + env.close(); + + auto const seq = env.seq(alice); + env(batch::outer(alice, seq, XRP(1), tfAllOrNothing), + batch::Inner(noop(alice), seq + 1), + batch::Inner(ticket::create(alice, 1), seq + 2), + sponsor::As(sponsor, spfSponsorFee), + Ter(tesSUCCESS)); + env.close(); + + // does not affect reserve + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + + // Fee is paid by sponsor object + BEAST_EXPECT(env.balance(alice) == XRP(1000)); + BEAST_EXPECT(env.balance(sponsor) == XRP(900)); + + auto const sponsorshipSle = env.le(keylet::sponsorship(sponsor, alice)); + BEAST_EXPECT(sponsorshipSle); + BEAST_EXPECT(sponsorshipSle->at(sfFeeAmount) == XRP(100 - 1)); + BEAST_EXPECT(sponsorshipSle->at(sfRemainingOwnerCount) == 100); + } + // + // Inner transaction + // + { + // test invalid Inner transaction with co-signing sponsor + Account const signerAccount("signer"); + Env env{*this, testableAmendments()}; + env.fund(XRP(1000), alice, bob, sponsor, signerAccount); + env.close(); + + env(signers(sponsor, 1, {Signer(signerAccount, 1)})); + env.close(); + + { + auto jt = env.jtnofill( + noop(alice), + sponsor::As(sponsor, spfSponsorReserve | spfSponsorFee), + Sig(sfSponsorSignature, sponsor)); + jt.jv.removeMember(sfTxnSignature.jsonName); + + auto const seq = env.seq(alice); + // should fail because Inner transaction cannot include SponsorSignature with + // TxnSignature + BEAST_EXPECT(jt.jv[sfSponsorSignature.jsonName].isMember(sfTxnSignature.jsonName)); + env(batch::outer(alice, seq, XRP(1), tfAllOrNothing), + batch::Inner(jt.jv, seq + 1), + batch::Inner(ticket::create(alice, 1), seq + 2), + Ter(temBAD_SIGNATURE)); + } + + { + auto jt = env.jtnofill( + noop(alice), + sponsor::As(sponsor, spfSponsorReserve | spfSponsorFee), + Msig(sfSponsorSignature, sponsor, signerAccount)); + jt.jv.removeMember(sfTxnSignature.jsonName); + + auto const seq = env.seq(alice); + // should fail because Inner transaction cannot include SponsorSignature with + // Signers + BEAST_EXPECT(jt.jv[sfSponsorSignature.jsonName].isMember(sfSigners.jsonName)); + env(batch::outer(alice, seq, XRP(1), tfAllOrNothing), + batch::Inner(jt.jv, seq + 1), + batch::Inner(ticket::create(alice, 1), seq + 2), + Ter(temBAD_SIGNER)); + } + + { + auto jt = env.jtnofill( + noop(alice), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); + jt.jv.removeMember(sfTxnSignature.jsonName); + jt.jv[sfSponsorSignature.jsonName].removeMember(sfTxnSignature.jsonName); + jt.jv[sfSponsorSignature.jsonName][sfSigningPubKey.jsonName] = ""; + + auto const seq = env.seq(alice); + // should fail because BatchSigners does not have signer for SponsorSignature + env(batch::outer(alice, seq, XRP(1), tfAllOrNothing), + batch::Inner(jt.jv, seq + 1), + batch::Inner(ticket::create(alice, 1), seq + 2), + Ter(temBAD_SIGNER)); + } + } + + { + // test Inner transaction with prefunded sponsor + Env env{*this, testableAmendments()}; + env.fund(XRP(1000), alice, bob); + env.fund(XRP(1001), sponsor); + env.close(); + + env(sponsor::set(sponsor, 0, 100, XRP(100)), + sponsor::SponseeAcc(alice), + Fee(XRP(1)), + Ter(tesSUCCESS)); + env.close(); + BEAST_EXPECT(env.balance(sponsor) == XRP(900)); + + auto jt = env.jtnofill( + check::create(alice, bob, XRP(1)), sponsor::As(sponsor, spfSponsorReserve)); + // remove txn signature since it is filled by env.jtnofill() + jt.jv.removeMember(jss::TxnSignature); + + auto const seq = env.seq(alice); + env(batch::outer(alice, seq, XRP(1), tfAllOrNothing), + batch::Inner(noop(alice), seq + 1), + batch::Inner(jt.jv, seq + 2), + Ter(tesSUCCESS)); + env.close(); + + // affect sponsor reserve + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + + // Fee is paid by outer transaction originator (alice) + BEAST_EXPECT(env.balance(alice) == XRP(999)); + BEAST_EXPECT(env.balance(sponsor) == XRP(900)); + + // reserve count is decreased + auto const sponsorshipSle = env.le(keylet::sponsorship(sponsor, alice)); + BEAST_EXPECT(sponsorshipSle); + BEAST_EXPECT(sponsorshipSle->at(sfFeeAmount) == XRP(100)); + BEAST_EXPECT(sponsorshipSle->at(sfRemainingOwnerCount) == 99); + } + + { + // test Inner transaction with co-signing sponsor + Env env{*this, testableAmendments()}; + env.fund(XRP(1000), alice, bob, sponsor); + env.close(); + + auto jt = env.jtnofill( + check::create(alice, bob, XRP(1)), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); + // remove txn signature since it is filled by env.jtnofill() + jt.jv.removeMember(sfTxnSignature.jsonName); + jt.jv[sfSponsorSignature.jsonName].removeMember(sfTxnSignature.jsonName); + jt.jv[sfSponsorSignature.jsonName][sfSigningPubKey.jsonName] = ""; + + auto const seq = env.seq(alice); + env(batch::outer(alice, seq, XRP(1), tfAllOrNothing), + batch::Inner(noop(alice), seq + 1), + batch::Inner(jt.jv, seq + 2), + batch::Sig(sponsor), + Ter(tesSUCCESS)); + env.close(); + + // affect sponsor reserve + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + + // Fee is paid by outer transaction originator (alice) + BEAST_EXPECT(env.balance(alice) == XRP(999)); + BEAST_EXPECT(env.balance(sponsor) == XRP(1000)); + } + + // Inner tx with sfSponsor + spfSponsorFee (pre-funded) is rejected + { + Env env{*this, testableAmendments()}; + env.fund(XRP(1000), alice, bob, sponsor); + env.close(); + + // Create pre-funded sponsorship + env(sponsor::set(sponsor, 0, 0, XRP(1)), sponsor::SponseeAcc(alice), Fee(XRP(1))); + env.close(); + + auto const seq = env.seq(alice); + auto const batchFee = batch::calcBatchFee(env, 0, 2); + + // Create inner transaction with sponsor fee + auto innerPay = pay(alice, bob, XRP(1)); + innerPay[sfSponsor.jsonName] = sponsor.human(); + innerPay[sfSponsorFlags.jsonName] = static_cast(spfSponsorFee); + + // Should be rejected with temBAD_FEE + env(batch::outer(alice, seq, batchFee, tfAllOrNothing), + batch::Inner(innerPay, seq + 1), + batch::Inner(noop(alice), seq + 2), + Ter(temINVALID_FLAG)); + env.close(); + } + + // Inner tx with sfSponsor + spfSponsorFee (co-signed) is rejected + { + Env env{*this, testableAmendments()}; + env.fund(XRP(1000), alice, bob, sponsor); + env.close(); + + auto const seq = env.seq(alice); + auto const batchFee = batch::calcBatchFee(env, 1, 2); + + auto innerPay = pay(alice, bob, XRP(1)); + innerPay[sfSponsor.jsonName] = sponsor.human(); + innerPay[sfSponsorFlags.jsonName] = static_cast(spfSponsorFee); + + // Should be rejected with temBAD_FEE + env(batch::outer(alice, seq, batchFee, tfAllOrNothing), + batch::Inner(innerPay, seq + 1), + batch::Inner(noop(alice), seq + 2), + batch::Sig(sponsor), + Ter(temINVALID_FLAG)); + env.close(); + } + + // Inner tx with spfSponsorFee + spfSponsorReserve is rejected + { + Env env{*this, testableAmendments()}; + env.fund(XRP(1000), alice, bob, sponsor); + env.close(); + + auto const seq = env.seq(alice); + auto const batchFee = batch::calcBatchFee(env, 0, 2); + + auto innerTx = check::create(alice, bob, XRP(1)); + innerTx[sfSponsor.jsonName] = sponsor.human(); + innerTx[sfSponsorFlags.jsonName] = + static_cast(spfSponsorFee | spfSponsorReserve); + + // Should be rejected with temBAD_FEE (fee sponsorship check comes first) + env(batch::outer(alice, seq, batchFee, tfAllOrNothing), + batch::Inner(innerTx, seq + 1), + batch::Inner(noop(alice), seq + 2), + Ter(temINVALID_FLAG)); + env.close(); + } + + // Outer batch tx with sponsor fee is allowed + { + Env env{*this, testableAmendments()}; + env.fund(XRP(1000), alice, bob, sponsor); + env.close(); + + auto const seq = env.seq(bob); + auto const batchFee = batch::calcBatchFee(env, 0, 2); + + // Outer batch with sponsor fee should work fine + env(batch::outer(bob, seq, batchFee, tfAllOrNothing), + batch::Inner(noop(bob), seq + 1), + batch::Inner(noop(bob), seq + 2), + sponsor::As(sponsor, spfSponsorFee), + Sig(sfSponsorSignature, sponsor), + Ter(tesSUCCESS)); + env.close(); + + // Sponsor paid the fee + BEAST_EXPECT(env.balance(bob) == XRP(1000)); + } + } + + // Verify that the central allow-list in preflight1Sponsor rejects + // spfSponsorReserve for transaction types that v1 does not permit. + void + testReserveSponsorGate() + { + testcase("Reserve sponsor allow-list gate"); + using namespace test::jtx; + + Env env{*this, testableAmendments()}; + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + env.fund(XRP(10000), alice, bob, sponsor); + env.close(); + + env(sponsor::set(sponsor, 0, 10, XRP(10)), sponsor::SponseeAcc(alice)); + env.close(); + + auto checkBlocked = [&](json::Value const& jv) { + env(jv, + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor), + Ter(temINVALID_FLAG)); + }; + + checkBlocked(ticket::create(alice, 1)); + checkBlocked(offer(alice, XRP(100), bob["USD"](100))); + checkBlocked(did::setValid(alice)); + checkBlocked(token::mint(alice, 0u)); + checkBlocked(sponsor::set(alice, 0, 10, XRP(10))); + checkBlocked(acctdelete(alice, bob)); + checkBlocked(loan::set(alice, uint256(1), Number{1})); + } + + void + testSponsorReserve(bool cosigning) + { + testRequireFlag(); + testSponsorReserveSimple(cosigning); + testCheck(cosigning); + testCredential(cosigning); + testDelegate(cosigning); + testDepositPreauth(cosigning); + testEscrow(cosigning); + testMPToken(cosigning); + testPayChan(cosigning); + testSignerList(cosigning); + testTrustSet(cosigning); + } + + void + testZeroBalanceSponsoredPaymentFeePayerCheck() + { + // Zero-balance sponsored Payment: getFeePayer() consistency check + testcase("Sponsored Payment: minimal-balance account with sponsor-pays-fee"); + + using namespace jtx; + Env env{*this, testableAmendments()}; + Account const alice("alice"); + Account const sponsor("sponsor"); + Account const dest("dest"); + + auto const baseFee = env.current()->fees().base; + auto const baseReserve = env.current()->fees().reserve; + + // Fund sponsor and dest generously, alice with base reserve + 1 XRP for payment + env.fund(XRP(10000), sponsor, dest); + env.fund(baseReserve + XRP(1), alice); + env.close(); + + // Precondition: alice has base reserve + 1 XRP (enough for payment but not fee) + BEAST_EXPECT(env.balance(alice) == baseReserve + XRP(1)); + + // Alice tries to send a Payment to dest where sponsor pays the fee via spfSponsorFee. + // Passed even alice balance doesn't have enough to pay fee. + auto const preDest = env.balance(dest); + auto const preSponsor = env.balance(sponsor); + + // Alice sends 1 XRP to dest, sponsor pays the fee + env(pay(alice, dest, XRP(1)), + sponsor::As(sponsor, spfSponsorFee), + Sig(sfSponsorSignature, sponsor), + Fee(baseFee)); + env.close(); + + // Payment succeeded + // Alice's balance decreased by 1 XRP (the payment amount, NOT the fee) + BEAST_EXPECT(env.balance(alice) == baseReserve); + + // Dest received 1 XRP + BEAST_EXPECT(env.balance(dest) == preDest + XRP(1)); + + // Sponsor paid the fee (NOT alice) + BEAST_EXPECT(env.balance(sponsor) == preSponsor - baseFee); + } + + void + testTrustSetCounterpartySponsorMisroute() + { + // TrustSet's modify path applies the tx-level reserve sponsor to whichever + // side has its reserve gate trip on this update, regardless of whether that + // side belongs to the tx submitter. trustCreate only sets the submitter's + // reserve flag and snapshots the counterparty's asfDefaultRipple state into + // the line's NoRipple bit; if the counterparty later toggles asfDefaultRipple + // (the canonical issuer flow), the line and account flags disagree and on + // the submitter's next TrustSet the counterparty-side gate fires. Sponsor still will be + // checked if it can be applied to that end of the trustLine. + + testcase("TrustSet modify with sponsor does not misroute onto counterparty side"); + + using namespace test::jtx; + + Env env{*this, testableAmendments()}; + Account const alice{"alice_t2178"}; + Account const bob{"bob_t2178"}; + Account const carol{"carol_t2178"}; + + // Fund without auto-setting asfDefaultRipple + env.fund(XRP(100'000), alice, bob, carol); + env.close(); + + // Determine account ordering + bool const aliceIsHigh = alice.id() > bob.id(); + + // To trigger the bug, we need the COUNTERPARTY's reserve gate to trip + // We use issuer/holder terminology where: + // - holder creates the trust line (their reserve is set first) + // - issuer enables DefaultRipple after (creates flag mismatch) + // - holder's second TrustSet triggers issuer's reserve gate + + auto const issuer = aliceIsHigh ? bob : alice; + auto const holder = aliceIsHigh ? alice : bob; + auto const usd = issuer["USD"]; + + // Issuer must NOT have DefaultRipple set initially + // Clear it explicitly (env.fund may have set it) + env(fclear(issuer, asfDefaultRipple)); + env.close(); + + // Holder creates the trust line first (holder's reserve flag is set) + // At this point, issuer does NOT have DefaultRipple set, so + // the NoRipple bit on issuer's side is set according to issuer's current flag + env(trust(holder, usd(1'000))); + env.close(); + + // Issuer now enables asfDefaultRipple (canonical issuer flow) + // This creates a mismatch: issuer's account flag says DefaultRipple=true + // but the trust line's NoRipple bit on issuer's side is still set + env(fset(issuer, asfDefaultRipple)); + env.close(); + + SF_ACCOUNT const& issuerSponsorField = aliceIsHigh ? sfLowSponsor : sfHighSponsor; + SF_ACCOUNT const& holderSponsorField = aliceIsHigh ? sfHighSponsor : sfLowSponsor; + + auto const lineKey = keylet::trustLine(alice, bob, usd.currency); + auto const sleLineBefore = env.le(lineKey); + if (!BEAST_EXPECT(sleLineBefore)) + return; + BEAST_EXPECT(!sleLineBefore->isFieldPresent(sfLowSponsor)); + BEAST_EXPECT(!sleLineBefore->isFieldPresent(sfHighSponsor)); + + auto const carolBefore = sponsoringOwnerCount(env, carol); + BEAST_EXPECT(carolBefore == 0); + auto const issuerSponsoredBefore = sponsoredOwnerCount(env, issuer); + BEAST_EXPECT(issuerSponsoredBefore == 0); + + // Holder modifies the trust line with Carol as sponsor + // This should trigger the issuer's reserve gate because of the DefaultRipple mismatch + // Carol (sponsor) should NOT be applied to issuer's side (issuer != tx submitter) + env(trust(holder, usd(2'000)), + sponsor::As(carol, spfSponsorReserve), + Sig(sfSponsorSignature, carol), + Ter(tesSUCCESS)); + env.close(); + + auto const sleLineAfter = env.le(lineKey); + if (!BEAST_EXPECT(sleLineAfter)) + return; + + // Carol only agreed to back the holder, not the issuer + BEAST_EXPECT(!sleLineAfter->isFieldPresent(issuerSponsorField)); + + // Holder's side also has no sponsor because holder's reserve flag was + // already set on the FIRST TrustSet (no sponsor in scope then) + BEAST_EXPECT(!sleLineAfter->isFieldPresent(holderSponsorField)); + + // Carol's sponsoring count should remain unchanged (no misroute) + auto const carolAfter = sponsoringOwnerCount(env, carol); + BEAST_EXPECT(carolAfter == carolBefore); + + // Issuer's sponsored count should remain unchanged (no misroute) + auto const issuerSponsoredAfter = sponsoredOwnerCount(env, issuer); + BEAST_EXPECT(issuerSponsoredAfter == issuerSponsoredBefore); + } + + void + testSelfEscrowFinishReserveGate() + { + testcase("Self-escrow finish reserve order gated by amendment"); + using namespace test::jtx; + using namespace std::chrono_literals; + + // Finishing a self-escrow (source == destination) whose trust line + // was deleted while the escrow was outstanding auto-creates the line, + // and the outcome of that reserve check depends on whether the escrow + // reserve is released before delivery (Sponsor) or after (legacy). + // With the source's balance in the one-increment window + // [reserve(1), reserve(2)), the legacy order requires reserve(2) and + // fails, while the Sponsor order requires reserve(1) and succeeds. + auto runTest = [&](FeatureBitset features, TER expected) { + Account const alice("alice"); + Account const gw("gw"); + auto const usd = gw["usd"]; + + Env env{*this, features}; + auto const baseFee = env.current()->fees().base; + + env.fund(XRP(10000), alice, gw); + env.close(); + + env(fset(gw, asfAllowTrustLineLocking)); + env.close(); + + env.trust(usd(1000), alice); + env.close(); + env(pay(gw, alice, usd(100))); + env.close(); + + // Escrow alice's entire USD balance to herself. The escrowed + // IOUs return to the issuer, zeroing the line balance. + auto const seq = env.seq(alice); + env(escrow::create(alice, alice, usd(100)), + escrow::kCondition(escrow::kCb1), + escrow::kCancelTime(env.now() + 100s)); + env.close(); + + // Delete the now-empty trust line. Both accounts have + // DefaultRipple set (jtx fund does that), so a plain limit-0 + // TrustSet returns the line to its default state. + env(trust(alice, usd(0))); + env.close(); + BEAST_EXPECT(!env.le(keylet::trustLine(alice, gw, usd.currency))); + BEAST_EXPECT(ownerCount(env, alice) == 1); // just the escrow + + // Put alice's balance in the window. Pay the excess away + // directly: adjustAccountXRPBalance needs the Sponsor amendment. + STAmount const target = reserve(env, 1) + XRP(1); + env(pay(alice, env.master, env.balance(alice) - target - baseFee), Fee(baseFee)); + env.close(); + BEAST_EXPECT(env.balance(alice) == target); + + env(escrow::finish(alice, alice, seq), + escrow::kCondition(escrow::kCb1), + escrow::kFulfillment(escrow::kFb1), + Fee(baseFee * 150), + Ter(expected)); + env.close(); + + if (expected == tesSUCCESS) + { + BEAST_EXPECT(!env.le(keylet::escrow(alice, seq))); + BEAST_EXPECT(env.le(keylet::trustLine(alice, gw, usd.currency))); + BEAST_EXPECT(env.balance(alice, usd) == usd(100)); + BEAST_EXPECT(ownerCount(env, alice) == 1); // the new line + } + else + { + BEAST_EXPECT(env.le(keylet::escrow(alice, seq))); + BEAST_EXPECT(!env.le(keylet::trustLine(alice, gw, usd.currency))); + BEAST_EXPECT(ownerCount(env, alice) == 1); // still the escrow + } + }; + + // Pre-amendment: legacy order — the escrow still counts against the + // reserve while the auto-created line is checked. + runTest(testableAmendments() - featureSponsor, tecNO_LINE_INSUF_RESERVE); + + // Post-amendment: the escrow reserve is recycled into the new line. + runTest(testableAmendments(), tesSUCCESS); + } + + void + testFeeSponsoredVaultInvariant() + { + // The ValidVault invariant checks that the vault's balance and the + // depositor's balance change by equal amounts. For XRP vaults it adds the + // fee back into the depositor's balance change: normally the depositor + // pays the fee, so their balance drops by (deposit amount + fee), and + // adding the fee back leaves just the deposit amount to compare against + // the vault. But when a fee sponsor pays, the depositor's balance drops + // by only the deposit amount, so the fee must NOT be added back or the + // equal-amount check fails. + testcase("Fee-sponsored VaultDeposit/VaultWithdraw pass ValidVault invariant"); + using namespace test::jtx; + + Env env{*this, testableAmendments()}; + Account const alice("alice"); + Account const sponsor("sponsor"); + env.fund(XRP(10000), alice, sponsor); + env.close(); + + PrettyAsset const xrpAsset{xrpIssue(), 1'000'000}; + Vault const vault{env}; + auto [vaultTx, vaultKeylet] = vault.create({.owner = alice, .asset = xrpAsset}); + env(vaultTx); + env.close(); + + // Control: the same deposit shape, unsponsored, succeeds. + env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = xrpAsset(100)}), + Ter(tesSUCCESS)); + env.close(); + + // Fee-sponsored (co-signed) deposit succeeds. + env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = xrpAsset(100)}), + Fee(XRP(1)), + sponsor::As(sponsor, spfSponsorFee), + Sig(sfSponsorSignature, sponsor), + Ter(tesSUCCESS)); + env.close(); + + // The same helper (deltaAssetsTxAccount) drives the withdraw path, so a + // fee-sponsored withdrawal back to the depositor's own account also + // passes on the destination side. + env(vault.withdraw({.depositor = alice, .id = vaultKeylet.key, .amount = xrpAsset(50)}), + Fee(XRP(1)), + sponsor::As(sponsor, spfSponsorFee), + Sig(sfSponsorSignature, sponsor), + Ter(tesSUCCESS)); + env.close(); + } + + void + testSponsoredObjectDeletionRefund() + { + // Deleting a co-signed reserve-sponsored object must + // refund the sponsor's SponsoringOwnerCount back to zero. + testcase("Sponsored object deletion refunds sponsor owner count"); + using namespace test::jtx; + + Env env{*this, testableAmendments()}; + Account const sponsor("sponsor"); + env.fund(XRP(100000), sponsor); + env.close(); + + // Sponsored Check deletion - verify decreaseOwnerCountForObject + // (used by CheckCancel) reads the object's sfSponsor field and refunds + // the sponsor's owner count. + { + testcase(" — Check deletion"); + + Account const checkOwner("check_owner"); + Account const dest("check_dest"); + env.fund(XRP(100000), checkOwner, dest); + env.close(); + + // Create a check with co-signed reserve sponsorship. This bumps the + // sponsor's sfSponsoringOwnerCount rather than the check owner's. + auto const checkSeq = env.seq(checkOwner); + env(check::create(checkOwner, dest, XRP(1)), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); + env.close(); + + auto sponsorCountBefore = sponsoringOwnerCount(env, sponsor); + BEAST_EXPECT(sponsorCountBefore == 1); // check costs 1 owner count + + // Cancel (delete) the check. + env(check::cancel(checkOwner, keylet::check(checkOwner, checkSeq).key)); + env.close(); + + auto sponsorCountAfter = sponsoringOwnerCount(env, sponsor); + BEAST_EXPECT(sponsorCountAfter == 0); // fully refunded + } + + // Sponsored TrustSet (trust line) deletion - verify the sponsor + // refund works when a sponsored trust line is deleted + { + testcase(" — TrustLine deletion"); + + Account const issuer("issuer"); + Account const holder("holder"); + env.fund(XRP(100000), issuer, holder); + env.close(); + + auto const usd = issuer["USD"]; + + // Create trust line with sponsorship on the holder side + env(trust(holder, usd(1000)), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); + env.close(); + + auto sponsorCountBefore = sponsoringOwnerCount(env, sponsor); + BEAST_EXPECT(sponsorCountBefore == 1); // trust line costs 1 + + // Delete the trust line by clearing it to default + env(trust(holder, usd(0))); + env.close(); + + auto sponsorCountAfter = sponsoringOwnerCount(env, sponsor); + BEAST_EXPECT(sponsorCountAfter == 0); // fully refunded + } + } + +protected: + void + testSponsor() + { + testDisabled(); + testInvalidSponsorshipSet(); + testPseudoAccountSponsorship(); + + testSingleSigning(); + testMultiSigning(); + + testInvalidSponsorField(); + + testSimpleSponsorshipSet(); + + testPreFundAndCosign(); + testSponsoredFreeTierReserve(); + + testTransferSponsor(); + testLegacySignerListReserve(); + testSponsorFee(); + testSponsorAccount(); + + testAccountDelete(); + + testDelegatePermission(); + testDelegateBlockReserveSponsor(); + testDelegateSponsorFeePayer(); + + testBatch(); + + testSponsoredTrustLineNoFreeReserve(); + testCoSignReserveBoundedBySponsorshipBudget(); + testReserveSponsorGate(); + + testZeroBalanceSponsoredPaymentFeePayerCheck(); + testTrustSetCounterpartySponsorMisroute(); + testSelfEscrowFinishReserveGate(); + + testFeeSponsoredVaultInvariant(); + testSponsoredObjectDeletionRefund(); + } + + void + testTxSponsor(bool cosigning) + { + testSponsorReserve(cosigning); + } + +public: + void + run() override + { + testSponsor(); + } +}; + +class SponsorTxCosigning_test : public Sponsor_test +{ + void + run() override + { + testTxSponsor(true); + } +}; + +class SponsorTxPrefunded_test : public Sponsor_test +{ + void + run() override + { + testTxSponsor(false); + } +}; + +BEAST_DEFINE_TESTSUITE(Sponsor, app, xrpl); +BEAST_DEFINE_TESTSUITE(SponsorTxCosigning, app, xrpl); +BEAST_DEFINE_TESTSUITE(SponsorTxPrefunded, app, xrpl); + +} // namespace xrpl::test diff --git a/src/test/app/Ticket_test.cpp b/src/test/app/Ticket_test.cpp index 626cdbe44d..5700503830 100644 --- a/src/test/app/Ticket_test.cpp +++ b/src/test/app/Ticket_test.cpp @@ -503,7 +503,7 @@ class Ticket_test : public beast::unit_test::Suite Account const alice{"alice"}; // Fund alice not quite enough to make the reserve for a Ticket. - env.fund(env.current()->fees().accountReserve(1) - drops(1), alice); + env.fund(env.current()->fees().accountReserve(1, 1) - drops(1), alice); env.close(); env(ticket::create(alice, 1), Ter(tecINSUFFICIENT_RESERVE)); @@ -511,7 +511,8 @@ class Ticket_test : public beast::unit_test::Suite env.require(Owners(alice, 0), tickets(alice, 0)); // Give alice enough to exactly meet the reserve for one Ticket. - env(pay(env.master, alice, env.current()->fees().accountReserve(1) - env.balance(alice))); + env(pay( + env.master, alice, env.current()->fees().accountReserve(1, 1) - env.balance(alice))); env.close(); env(ticket::create(alice, 1)); @@ -524,7 +525,7 @@ class Ticket_test : public beast::unit_test::Suite env( pay(env.master, alice, - env.current()->fees().accountReserve(250) - drops(1) - env.balance(alice))); + env.current()->fees().accountReserve(250, 1) - drops(1) - env.balance(alice))); env.close(); // alice doesn't quite have the reserve for a total of 250 @@ -535,7 +536,8 @@ class Ticket_test : public beast::unit_test::Suite // Give alice enough so she can make the reserve for all 250 // Tickets. - env(pay(env.master, alice, env.current()->fees().accountReserve(250) - env.balance(alice))); + env(pay( + env.master, alice, env.current()->fees().accountReserve(250, 1) - env.balance(alice))); env.close(); std::uint32_t const ticketSeq{env.seq(alice) + 1}; diff --git a/src/test/app/TrustSet_test.cpp b/src/test/app/TrustSet_test.cpp index 15abc3bd06..e24373fd21 100644 --- a/src/test/app/TrustSet_test.cpp +++ b/src/test/app/TrustSet_test.cpp @@ -191,7 +191,7 @@ public: auto const txFee = env.current()->fees().base; auto const baseReserve = env.current()->fees().reserve; - auto const threelineReserve = env.current()->fees().accountReserve(3); + auto const threelineReserve = env.current()->fees().accountReserve(3, 1); env.fund(XRP(10000), gwA, gwB, assistor); diff --git a/src/test/app/TxQ_test.cpp b/src/test/app/TxQ_test.cpp index 91eb26da7e..3175e742d9 100644 --- a/src/test/app/TxQ_test.cpp +++ b/src/test/app/TxQ_test.cpp @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include #include #include @@ -2335,6 +2337,43 @@ public: BEAST_EXPECT(env.balance(alice) == drops(5)); } + void + testSponsorTxCannotQueue() + { + using namespace jtx; + testcase("disallow sponsored transaction from being queued"); + + Env env(*this, makeConfig({{Keys::kMinimumTxnInLedgerStandalone, "3"}})); + + auto sponsor = Account("sponsor"); + auto sponsee = Account("sponsee"); + auto filler = Account("filler"); + + env.fund(XRP(50000), noripple(sponsor, sponsee)); + env.close(); + env.fund(XRP(50000), noripple(filler)); + env.close(); + + fillQueue(env, filler); + checkMetrics(*this, env, 0, 6, 4, 3); + + // Sponsored transactions are not allowed to be queued. + env(noop(sponsee), + sponsor::As(sponsor, spfSponsorFee), + Sig(sfSponsorSignature, sponsor), + Ter(telCAN_NOT_QUEUE)); + checkMetrics(*this, env, 0, 6, 4, 3); + + // Sponsored transactions may still apply directly if they pay the + // open ledger fee. They just cannot be held in the queue. + env(noop(sponsee), + sponsor::As(sponsor, spfSponsorFee), + Sig(sfSponsorSignature, sponsor), + Fee(openLedgerCost(env)), + Ter(tesSUCCESS)); + checkMetrics(*this, env, 0, 6, 5, 3); + } + void testDelegateTxCannotQueue() { @@ -4698,6 +4737,7 @@ public: testBlockersSeq(); testBlockersTicket(); testInFlightBalance(); + testSponsorTxCannotQueue(); testDelegateTxCannotQueue(); testConsequences(); } diff --git a/src/test/app/Vault_test.cpp b/src/test/app/Vault_test.cpp index 3877e08f7e..4de8c9c616 100644 --- a/src/test/app/Vault_test.cpp +++ b/src/test/app/Vault_test.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -1804,7 +1805,7 @@ class Vault_test : public beast::unit_test::Suite auto const [acctReserve, incReserve] = [this]() -> std::pair { Env const env{*this, testableAmendments()}; return { - env.current()->fees().accountReserve(0).drops() / kDropsPerXrp.drops(), + env.current()->fees().accountReserve(0, 1).drops() / kDropsPerXrp.drops(), env.current()->fees().increment.drops() / kDropsPerXrp.drops()}; }(); @@ -2686,7 +2687,7 @@ class Vault_test : public beast::unit_test::Suite auto const [acctReserve, incReserve] = [this]() -> std::pair { Env const env{*this, testableAmendments()}; return { - env.current()->fees().accountReserve(0).drops() / kDropsPerXrp.drops(), + env.current()->fees().accountReserve(0, 1).drops() / kDropsPerXrp.drops(), env.current()->fees().increment.drops() / kDropsPerXrp.drops()}; }(); @@ -5927,8 +5928,9 @@ class Vault_test : public beast::unit_test::Suite token->setFieldVL(*field, gMakeZeroBuffer(kEcGamalEncryptedTotalLength)); sb.update(token); + auto const dummyTx = *env.jt(noop(holder)).stx; BEAST_EXPECT( - removeEmptyHolding(sb, holder.id(), MPTIssue(mpt.issuanceID()), j) == + removeEmptyHolding({sb, dummyTx}, holder.id(), MPTIssue(mpt.issuanceID()), j) == tecHAS_OBLIGATIONS); BEAST_EXPECT(sb.peek(tokenKeylet) != nullptr); } diff --git a/src/test/app/XChain_test.cpp b/src/test/app/XChain_test.cpp index 6c8377d8e3..4b007eea13 100644 --- a/src/test/app/XChain_test.cpp +++ b/src/test/app/XChain_test.cpp @@ -141,7 +141,7 @@ struct SEnv XRPAmount reserve(std::uint32_t count) { - return env.current()->fees().accountReserve(count); + return env.current()->fees().accountReserve(count, 1); } XRPAmount @@ -370,7 +370,7 @@ struct XChain_test : public beast::unit_test::Suite, public jtx::XChainBridgeObj XRPAmount reserve(std::uint32_t count) { - return XEnv(*this).env.current()->fees().accountReserve(count); + return XEnv(*this).env.current()->fees().accountReserve(count, 1); } XRPAmount diff --git a/src/test/jtx/Env.h b/src/test/jtx/Env.h index ba28d19738..04cbce953a 100644 --- a/src/test/jtx/Env.h +++ b/src/test/jtx/Env.h @@ -561,6 +561,24 @@ public: [[nodiscard]] std::uint32_t ownerCount(Account const& account) const; + /** Return the number of sponsored objects owned by an account. + * Throws if the account does not exist. + */ + [[nodiscard]] std::uint32_t + sponsoredOwnerCount(Account const& account) const; + + /** Return the number of sponsoring objects owned by an account. + * Throws if the account does not exist. + */ + [[nodiscard]] std::uint32_t + sponsoringOwnerCount(Account const& account) const; + + /** Return the number of sponsoring accounts owned by an account. + * Throws if the account does not exist. + */ + [[nodiscard]] std::uint32_t + sponsoringAccountCount(Account const& account) const; + /** Return an account root. @return empty if the account does not exist. */ diff --git a/src/test/jtx/JTx.h b/src/test/jtx/JTx.h index da105e01aa..ed585ebb6e 100644 --- a/src/test/jtx/JTx.h +++ b/src/test/jtx/JTx.h @@ -37,7 +37,7 @@ struct JTx // Functions that sign the transaction from the Account std::vector> mainSigners; // Functions that sign something else after the mainSigners, such as - // sfCounterpartySignature + // sfCounterpartySignature and sfSponsorSignature std::vector> postSigners; JTx() = default; diff --git a/src/test/jtx/TestHelpers.h b/src/test/jtx/TestHelpers.h index cb2193db99..4c18d7343c 100644 --- a/src/test/jtx/TestHelpers.h +++ b/src/test/jtx/TestHelpers.h @@ -379,6 +379,18 @@ checkArraySize(json::Value const& val, unsigned int size); std::uint32_t ownerCount(test::jtx::Env const& env, test::jtx::Account const& account); +// Helper function that returns the sponsored owner count on an account. +std::uint32_t +sponsoredOwnerCount(test::jtx::Env const& env, test::jtx::Account const& account); + +// Helper function that returns the sponsoring owner count on an account. +std::uint32_t +sponsoringOwnerCount(test::jtx::Env const& env, test::jtx::Account const& account); + +// Helper function that returns the sponsoring account count on an account. +std::uint32_t +sponsoringAccountCount(test::jtx::Env const& env, test::jtx::Account const& account); + [[nodiscard]] inline bool checkVL(Slice const& result, std::string const& expected) diff --git a/src/test/jtx/impl/AMMTest.cpp b/src/test/jtx/impl/AMMTest.cpp index 46c5e44bd3..3b4aae20e2 100644 --- a/src/test/jtx/impl/AMMTest.cpp +++ b/src/test/jtx/impl/AMMTest.cpp @@ -197,7 +197,7 @@ AMMTestBase::testAMM(std::function const& cb, TestAM XRPAmount AMMTest::reserve(jtx::Env& env, std::uint32_t count) { - return env.current()->fees().accountReserve(count); + return env.current()->fees().accountReserve(count, 1); } XRPAmount diff --git a/src/test/jtx/impl/Env.cpp b/src/test/jtx/impl/Env.cpp index db45deb6de..3f6aca9fcb 100644 --- a/src/test/jtx/impl/Env.cpp +++ b/src/test/jtx/impl/Env.cpp @@ -271,6 +271,33 @@ Env::ownerCount(Account const& account) const return sle->getFieldU32(sfOwnerCount); } +std::uint32_t +Env::sponsoredOwnerCount(Account const& account) const +{ + auto const sle = le(account); + if (!sle) + Throw("missing account root"); + return sle->getFieldU32(sfSponsoredOwnerCount); +} + +std::uint32_t +Env::sponsoringOwnerCount(Account const& account) const +{ + auto const sle = le(account); + if (!sle) + Throw("missing account root"); + return sle->getFieldU32(sfSponsoringOwnerCount); +} + +std::uint32_t +Env::sponsoringAccountCount(Account const& account) const +{ + auto const sle = le(account); + if (!sle) + Throw("missing account root"); + return sle->getFieldU32(sfSponsoringAccountCount); +} + std::uint32_t Env::seq(Account const& account) const { diff --git a/src/test/jtx/impl/TestHelpers.cpp b/src/test/jtx/impl/TestHelpers.cpp index 95c8a68436..4d3869b4f9 100644 --- a/src/test/jtx/impl/TestHelpers.cpp +++ b/src/test/jtx/impl/TestHelpers.cpp @@ -94,6 +94,24 @@ ownerCount(Env const& env, Account const& account) return env.ownerCount(account); } +std::uint32_t +sponsoredOwnerCount(Env const& env, Account const& account) +{ + return env.sponsoredOwnerCount(account); +} + +std::uint32_t +sponsoringOwnerCount(Env const& env, Account const& account) +{ + return env.sponsoringOwnerCount(account); +} + +std::uint32_t +sponsoringAccountCount(Env const& env, Account const& account) +{ + return env.sponsoringAccountCount(account); +} + /* Path finding */ /******************************************************************************/ void diff --git a/src/test/jtx/impl/multisign.cpp b/src/test/jtx/impl/multisign.cpp index 66c5a4f27d..d948042bda 100644 --- a/src/test/jtx/impl/multisign.cpp +++ b/src/test/jtx/impl/multisign.cpp @@ -64,7 +64,8 @@ Msig::operator()(Env& env, JTx& jt) const { auto const mySigners = signers; auto callback = [subField = subField, mySigners, &env](Env&, JTx& jtx) { - // Where to put the signature. Supports sfCounterPartySignature. + // Where to put the signature. Supports sfCounterPartySignature and + // sfSponsorSignature. auto& sigObject = subField ? jtx[*subField] : jtx.jv; // The signing pub key is only required at the top level. diff --git a/src/test/jtx/impl/owners.cpp b/src/test/jtx/impl/owners.cpp index 2ff93757f0..ed10ca6930 100644 --- a/src/test/jtx/impl/owners.cpp +++ b/src/test/jtx/impl/owners.cpp @@ -44,6 +44,24 @@ Owners::operator()(Env& env) const env.test.expect(env.le(account_)->getFieldU32(sfOwnerCount) == value_); } +void +SponsoredOwners::operator()(Env& env) const +{ + env.test.expect(env.le(account_)->getFieldU32(sfSponsoredOwnerCount) == value_); +} + +void +SponsoringOwners::operator()(Env& env) const +{ + env.test.expect(env.le(account_)->getFieldU32(sfSponsoringOwnerCount) == value_); +} + +void +SponsoringAccountCount::operator()(Env& env) const +{ + env.test.expect(env.le(account_)->getFieldU32(sfSponsoringAccountCount) == value_); +} + } // namespace test::jtx } // namespace xrpl diff --git a/src/test/jtx/impl/sig.cpp b/src/test/jtx/impl/sig.cpp index 7140fad3de..e0123073b1 100644 --- a/src/test/jtx/impl/sig.cpp +++ b/src/test/jtx/impl/sig.cpp @@ -18,7 +18,7 @@ Sig::operator()(Env&, JTx& jt) const // VFALCO Inefficient pre-C++14 auto const account = *account_; auto callback = [subField = subField_, account](Env&, JTx& jtx) { - // Where to put the signature. Supports sfCounterPartySignature. + // Where to put the signature. Supports sfCounterPartySignature and sfSponsorSignature. auto& sigObject = subField ? jtx[*subField] : jtx.jv; jtx::sign(jtx.jv, account, sigObject); diff --git a/src/test/jtx/impl/sponsor.cpp b/src/test/jtx/impl/sponsor.cpp new file mode 100644 index 0000000000..cdf68800f5 --- /dev/null +++ b/src/test/jtx/impl/sponsor.cpp @@ -0,0 +1,98 @@ +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::test::jtx::sponsor { + +json::Value +set(jtx::Account const& account, + uint32_t flags, + std::optional const reserveCount, + std::optional const feeAmount, + std::optional const maxFee) +{ + json::Value jv; + jv[jss::TransactionType] = jss::SponsorshipSet; + jv[jss::Account] = account.human(); + jv[sfFlags.jsonName] = flags; + if (reserveCount) + jv[sfRemainingOwnerCount.jsonName] = *reserveCount; + if (feeAmount) + jv[sfFeeAmount.jsonName] = feeAmount->getJson(JsonOptions::Values::None); + if (maxFee) + jv[sfMaxFee.jsonName] = maxFee->getJson(JsonOptions::Values::None); + return jv; +} + +json::Value +del(jtx::Account const& account) +{ + json::Value jv; + jv[jss::TransactionType] = jss::SponsorshipSet; + jv[jss::Account] = account.human(); + jv[sfFlags.jsonName] = tfDeleteObject; + return jv; +} + +json::Value +transfer(jtx::Account const& account, uint32_t flags, std::optional const& index) +{ + json::Value jv; + jv[jss::TransactionType] = jss::SponsorshipTransfer; + jv[jss::Account] = account.human(); + jv[sfFlags.jsonName] = flags; + if (index) + jv[sfObjectID.jsonName] = to_string(*index); + return jv; +} + +void +CounterpartySponsor::operator()(Env& env, JTx& jt) const +{ + jt.jv[sfCounterpartySponsor.jsonName] = sponsor_.human(); +} + +void +SponseeAcc::operator()(Env& env, JTx& jt) const +{ + jt.jv[sfSponsee.jsonName] = sponsee_.human(); +} + +void +As::operator()(Env& env, JTx& jt) const +{ + jt.jv[sfSponsor.jsonName] = sponsor_.human(); + jt.jv[sfSponsorFlags.jsonName] = flags_; +} + +json::Value +ledgerEntry(jtx::Env& env, jtx::Account const& sponsor, jtx::Account const& sponsee) +{ + json::Value jvParams; + jvParams[jss::ledger_index] = jss::validated; + jvParams[jss::sponsorship][jss::sponsor] = sponsor.human(); + jvParams[jss::sponsorship][jss::sponsee] = sponsee.human(); + return env.rpc("json", "ledger_entry", to_string(jvParams)); +} + +STAmount +sponsorshipFeeBalance(jtx::Env& env, jtx::Account const& sponsor, jtx::Account const& sponsee) +{ + return env.le(keylet::sponsorship(sponsor, sponsee))->getFieldAmount(sfFeeAmount).xrp(); +} + +} // namespace xrpl::test::jtx::sponsor diff --git a/src/test/jtx/owners.h b/src/test/jtx/owners.h index 0fb38b407c..3f64601244 100644 --- a/src/test/jtx/owners.h +++ b/src/test/jtx/owners.h @@ -64,6 +64,60 @@ public: operator()(Env& env) const; }; +/** Match the account's SponsoredOwnerCount field: the number of owned + objects whose reserve is sponsored by another account. */ +class SponsoredOwners +{ +private: + Account account_; + std::uint32_t value_; + +public: + SponsoredOwners(Account account, std::uint32_t value) + : account_(std::move(account)), value_(value) + { + } + + void + operator()(Env& env) const; +}; + +/** Match the account's SponsoringOwnerCount field: the number of objects + (owned by other accounts) whose reserve this account sponsors. */ +class SponsoringOwners +{ +private: + Account account_; + std::uint32_t value_; + +public: + SponsoringOwners(Account account, std::uint32_t value) + : account_(std::move(account)), value_(value) + { + } + + void + operator()(Env& env) const; +}; + +/** Match the account's SponsoringAccountCount field: the number of accounts + whose base reserve this account sponsors. */ +class SponsoringAccountCount +{ +private: + Account account_; + std::uint32_t value_; + +public: + SponsoringAccountCount(Account account, std::uint32_t value) + : account_(std::move(account)), value_(value) + { + } + + void + operator()(Env& env) const; +}; + /** Match the number of trust lines in the account's owner directory */ using lines = OwnerCount; diff --git a/src/test/jtx/sponsor.h b/src/test/jtx/sponsor.h new file mode 100644 index 0000000000..43d55d7246 --- /dev/null +++ b/src/test/jtx/sponsor.h @@ -0,0 +1,105 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +namespace xrpl::test::jtx::sponsor { + +json::Value +set(jtx::Account const& account, + std::uint32_t flags, + std::optional const reserveCount = std::nullopt, + std::optional const feeAmount = std::nullopt, + std::optional const maxFee = std::nullopt); + +inline json::Value +set_fee( + jtx::Account const& account, + std::uint32_t flags, + STAmount feeAmount, + std::optional maxFee = std::nullopt) +{ + return set(account, flags, std::nullopt, std::move(feeAmount), std::move(maxFee)); +} + +inline json::Value +set_reserve(jtx::Account const& account, std::uint32_t flags, std::uint32_t reserveCount) +{ + return set(account, flags, reserveCount); +} + +inline json::Value +set_max_fee(jtx::Account const& account, std::uint32_t flags, STAmount maxFee) +{ + return set(account, flags, std::nullopt, std::nullopt, std::move(maxFee)); +} + +json::Value +del(jtx::Account const& account); + +json::Value +transfer( + jtx::Account const& account, + uint32_t flags, + std::optional const& index = std::nullopt); + +struct CounterpartySponsor +{ +private: + jtx::Account sponsor_; + +public: + CounterpartySponsor(jtx::Account account) : sponsor_(std::move(account)) + { + } + + void + operator()(jtx::Env&, jtx::JTx& jtx) const; +}; + +struct SponseeAcc +{ +private: + jtx::Account sponsee_; + +public: + SponseeAcc(jtx::Account account) : sponsee_(std::move(account)) + { + } + + void + operator()(jtx::Env&, jtx::JTx& jtx) const; +}; + +struct As +{ +private: + jtx::Account sponsor_; + std::uint32_t flags_; + +public: + As(jtx::Account account, std::uint32_t flags = 0) : sponsor_(std::move(account)), flags_(flags) + { + } + + void + operator()(jtx::Env&, jtx::JTx& jtx) const; +}; + +json::Value +ledgerEntry(jtx::Env& env, jtx::Account const& sponsor, jtx::Account const& sponsee); + +STAmount +sponsorshipFeeBalance(jtx::Env& env, jtx::Account const& sponsor, jtx::Account const& sponsee); + +} // namespace xrpl::test::jtx::sponsor diff --git a/src/test/ledger/PaymentSandbox_test.cpp b/src/test/ledger/PaymentSandbox_test.cpp index f59b75091a..75f7410f94 100644 --- a/src/test/ledger/PaymentSandbox_test.cpp +++ b/src/test/ledger/PaymentSandbox_test.cpp @@ -1,17 +1,23 @@ #include #include #include +#include #include #include #include #include #include +#include +#include +#include +#include #include #include #include #include #include +#include #include #include #include @@ -19,7 +25,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -236,7 +244,7 @@ class PaymentSandbox_test : public beast::unit_test::Suite auto const startingAmount = accountHolds(pv, alice, iss.currency, iss.account, FreezeHandling::IgnoreFreeze, j); - BEAST_EXPECT(issueIOU(pv, alice, toCredit, iss, j) == tesSUCCESS); + BEAST_EXPECT(issueIOU(pv, alice, toCredit, iss, {}, j) == tesSUCCESS); BEAST_EXPECT( accountHolds( pv, alice, iss.currency, iss.account, FreezeHandling::IgnoreFreeze, j) == @@ -330,7 +338,7 @@ class PaymentSandbox_test : public beast::unit_test::Suite }; auto reserve = [](jtx::Env& env, std::uint32_t count) -> XRPAmount { - return env.current()->fees().accountReserve(count); + return env.current()->fees().accountReserve(count, 1); }; Env env(*this, features); @@ -389,6 +397,217 @@ class PaymentSandbox_test : public beast::unit_test::Suite BEAST_EXPECT(balance.getIssuer() == usd.account.id()); } + void + testOwnerCountHook(FeatureBitset features) + { + // Test that PaymentSandbox::adjustOwnerCountHook and ownerCountHook + // correctly track and return the maximum owner counts during a payment. + testcase("ownerCountHook"); + + using namespace jtx; + Env env(*this, features); + Account const alice("alice"); + Account const sponsor("sponsor"); + + env.fund(XRP(10000), alice, sponsor); + env.close(); + + ApplyViewImpl av(&*env.current(), TapNone); + PaymentSandbox sb(&av); + + // Test basic owner count hook without sponsor + { + auto const aliceSle = sb.peek(keylet::account(alice)); + BEAST_EXPECT(aliceSle); + + OwnerCounts const initial(aliceSle); + OwnerCounts updated = initial; + updated.owner = initial.owner + 2; + + // Simulate adjusting owner count + sb.adjustOwnerCountHook(alice, initial, updated); + + // ownerCountHook should return the max value + OwnerCounts const retrieved = sb.ownerCountHook(alice, initial); + BEAST_EXPECT(retrieved.owner == updated.owner); + BEAST_EXPECT(retrieved.sponsored == updated.sponsored); + BEAST_EXPECT(retrieved.sponsoring == updated.sponsoring); + } + + // Test owner count hook with sponsor-related counts + { + auto const sponsorSle = sb.peek(keylet::account(sponsor)); + BEAST_EXPECT(sponsorSle); + + OwnerCounts const sponsorInitial(sponsorSle); + OwnerCounts sponsorUpdated = sponsorInitial; + sponsorUpdated.owner = sponsorInitial.owner + 1; + sponsorUpdated.sponsoring = sponsorInitial.sponsoring + 1; + + sb.adjustOwnerCountHook(sponsor, sponsorInitial, sponsorUpdated); + + OwnerCounts const sponsorRetrieved = sb.ownerCountHook(sponsor, sponsorInitial); + BEAST_EXPECT(sponsorRetrieved.owner == sponsorUpdated.owner); + BEAST_EXPECT(sponsorRetrieved.sponsoring == sponsorUpdated.sponsoring); + } + + // Test with stacked PaymentSandboxes + { + PaymentSandbox sb2(&sb); + + auto const aliceSle = sb2.peek(keylet::account(alice)); + OwnerCounts const current(aliceSle); + OwnerCounts further = current; + further.owner = current.owner + 3; + + sb2.adjustOwnerCountHook(alice, current, further); + + // The nested sandbox should see the max from both levels + OwnerCounts const retrieved = sb2.ownerCountHook(alice, OwnerCounts()); + BEAST_EXPECT(retrieved.owner >= further.owner); + } + + // Test that max logic works correctly + { + auto const aliceSle = sb.peek(keylet::account(alice)); + OwnerCounts const current(aliceSle); + OwnerCounts lower = current; + lower.owner = (current.owner > 0) ? current.owner - 1 : 0; + + // Adjusting to a lower value + sb.adjustOwnerCountHook(alice, current, lower); + + // Should still return the higher value seen previously + OwnerCounts const retrieved = sb.ownerCountHook(alice, OwnerCounts()); + BEAST_EXPECT(retrieved.owner >= lower.owner); + } + } + + void + testOwnerCountWithTransaction(FeatureBitset features) + { + // Test that owner count hooks work correctly during actual transactions. + // This verifies that when transactions modify owner counts (by creating + // or deleting ledger objects), the hooks properly track these changes. + testcase( + std::string("ownerCountWithTransaction") + + (features[featureSponsor] ? " with sponsor" : " without sponsor")); + + using namespace jtx; + + auto reserve = [](jtx::Env& env, std::uint32_t count) -> XRPAmount { + return env.current()->fees().accountReserve(count, 1); + }; + + Env env(*this, features); + Account const gw("gw"); + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor("sponsor"); + + auto const usd = gw["USD"]; + + // Fund accounts. Alice starts with exactly enough for base reserve + 2 objects + env.fund(XRP(10000), gw, bob, sponsor); + env.fund(reserve(env, 3) + XRP(100), alice); // Base + 2 objects + extra for fees + env.close(); + + // Verify initial state - no owner count + BEAST_EXPECT(ownerCount(env, alice) == 0); + BEAST_EXPECT(ownerCount(env, bob) == 0); + + // Create a trust line - this increases owner count + env(trust(alice, usd(1000))); + env.close(); + + // alice now has 1 object (owner count = 1) + BEAST_EXPECT(ownerCount(env, alice) == 1); + + // Create an offer - this further increases owner count + env(trust(bob, usd(1000))); + env(pay(gw, alice, usd(100))); + env.close(); + + auto const aliceOfferSeq = env.seq(alice); // Capture the sequence before creating offer + env(offer(alice, usd(50), XRP(50))); + env.close(); + + // alice now has 2 objects (trust line + offer) + BEAST_EXPECT(ownerCount(env, alice) == 2); + + // If sponsor feature is enabled, test sponsorship transfer + if (features[featureSponsor]) + { + auto const trustId = keylet::trustLine(alice, gw, usd.currency); + BEAST_EXPECT(env.le(trustId)); + + // Transfer sponsorship - sponsor now sponsors alice's trust line + env(sponsor::transfer(alice, tfSponsorshipCreate, trustId.key), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); + env.close(); + + // alice still has 2 objects but 1 is sponsored + BEAST_EXPECT(ownerCount(env, alice) == 2); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + // sponsor's sponsoring count should increase + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + } + + // Verify alice's available balance respects the reserve + auto const aliceSle = env.le(keylet::account(alice)); + BEAST_EXPECT(aliceSle); + + auto const aliceBalance = aliceSle->getFieldAmount(sfBalance); + // With sponsor, 1 object is sponsored so only 1 counts for reserve + auto const aliceReserve = reserve(env, features[featureSponsor] ? 1 : 2); + + // alice should have limited available balance after accounting for reserve + auto const available = aliceBalance.xrp() - aliceReserve; + if (features[featureSponsor]) + { + // With sponsor, alice has more available (1 sponsored object = less reserve) + BEAST_EXPECT(available > XRP(150)); + } + else + { + BEAST_EXPECT(available < XRP(150)); // Most of the balance is in reserve + } + + // Try to send nearly all balance - should fail due to reserve in both cases + auto const tooMuch = aliceBalance.xrp() - XRP(1); + env(pay(alice, bob, tooMuch), Ter(tecUNFUNDED_PAYMENT)); + env.close(); + + // Verify owner count hasn't changed + BEAST_EXPECT(ownerCount(env, alice) == 2); + + // Cancel the offer - this decreases owner count + env(offerCancel(alice, aliceOfferSeq)); + env.close(); + + // alice now has 1 object (just the trust line) + BEAST_EXPECT(ownerCount(env, alice) == 1); + + if (features[featureSponsor]) + { + // Verify sponsored count stayed the same (trust line is still sponsored) + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + } + + // Now alice should have more available balance (less reserve needed) + auto const aliceSle2 = env.le(keylet::account(alice)); + auto const aliceBalance2 = aliceSle2->getFieldAmount(sfBalance); + // With sponsor, trust line is still sponsored so 0 objects for reserve + // Without sponsor, 1 object for reserve + auto const aliceReserve2 = reserve(env, features[featureSponsor] ? 0 : 1); + auto const available2 = aliceBalance2.xrp() - aliceReserve2; + + // available2 should be greater than available (less reserve needed) + BEAST_EXPECT(available2 > available); + } + public: void run() override @@ -399,11 +618,16 @@ public: testTinyBalance(features); testReserve(features); testBalanceHook(features); + testOwnerCountHook(features); }; using namespace jtx; auto const sa = testableAmendments(); testAll(sa - featurePermissionedDEX); testAll(sa); + + // Test owner count with transactions + testOwnerCountWithTransaction(sa - featureSponsor); + testOwnerCountWithTransaction(sa); } }; diff --git a/src/test/rpc/AccountObjects_test.cpp b/src/test/rpc/AccountObjects_test.cpp index 31b20b37d4..6ba6e7de9f 100644 --- a/src/test/rpc/AccountObjects_test.cpp +++ b/src/test/rpc/AccountObjects_test.cpp @@ -9,8 +9,11 @@ #include // IWYU pragma: keep #include #include +#include +#include #include #include +#include #include #include @@ -22,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -200,6 +204,15 @@ public: resp[jss::result][jss::error_message] == "Invalid field 'limit', not unsigned integer."); } + // test error on sponsored param not a boolean + { + json::Value params; + params[jss::account] = bob.human(); + params[jss::sponsored] = "true"; + auto resp = env.rpc("json", "account_objects", to_string(params)); + BEAST_EXPECT( + resp[jss::result][jss::error_message] == "Invalid field 'sponsored', not boolean."); + } // test errors on marker { Account const gw{"G"}; @@ -605,6 +618,7 @@ public: BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::amm), 0)); BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::did), 0)); BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::permissioned_domain), 0)); + BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::sponsorship), 0)); // we expect invalid field type reported for the following types BEAST_EXPECT(acctObjsTypeIsInvalid(acctObjs(gw, jss::amendments))); @@ -926,6 +940,30 @@ public: BEAST_EXPECT(ticket[sfTicketSequence.jsonName].asUInt() == seq + 1); } + { + // Create a sponsorship + env(sponsor::set(alice, tfSponsorshipSetRequireSignForFee, 200, XRP(100), drops(10)), + sponsor::SponseeAcc(gw)); + env.close(); + + // Find the sponsorship. + for (auto const& acct : {alice, gw}) + { + json::Value const resp = acctObjs(acct, jss::sponsorship); + BEAST_EXPECT(acctObjsIsSize(resp, 1)); + + auto const& sponsorship = resp[jss::result][jss::account_objects][0u]; + + BEAST_EXPECT(sponsorship[sfOwner.jsonName] == alice.human()); + BEAST_EXPECT(sponsorship[sfSponsee.jsonName] == gw.human()); + BEAST_EXPECT( + sponsorship[sfFlags.jsonName].asUInt() == tfSponsorshipSetRequireSignForFee); + BEAST_EXPECT(sponsorship[sfRemainingOwnerCount.jsonName].asUInt() == 200); + BEAST_EXPECT(sponsorship[sfFeeAmount.jsonName].asUInt() == 100000000); + BEAST_EXPECT(sponsorship[sfMaxFee.jsonName].asUInt() == 10); + } + } + { // See how "deletion_blockers_only" handles gw's directory. json::Value params; @@ -940,7 +978,8 @@ public: jss::NFTokenPage.cStr(), jss::RippleState.cStr(), jss::PayChannel.cStr(), - jss::PermissionedDomain.cStr()}; + jss::PermissionedDomain.cStr(), + jss::Sponsorship.cStr()}; std::ranges::sort(v); return v; }(); @@ -1350,6 +1389,233 @@ public: } } + void + testSponsoredFilter() + { + testcase("SponsoredFilter"); + using namespace jtx; + + Env env(*this, testableAmendments()); + Account const alice("alice"); + Account const bob("bob"); + Account const sponsor1("sponsor1"); + Account const gw("gw"); + auto const usd = gw["USD"]; + + env.fund(XRP(10000), alice, bob, sponsor1, gw); + env.close(); + + // Helper to call account_objects with sponsored filter + auto acctObjsSponsored = [](Env& testEnv, + AccountID const& acct, + bool sponsored, + std::optional const& type = std::nullopt) { + json::Value params; + params[jss::account] = to_string(acct); + params[jss::sponsored] = sponsored; + if (type) + params[jss::type] = *type; + params[jss::ledger_index] = "validated"; + return testEnv.rpc("json", "account_objects", to_string(params)); + }; + + // Create a trust line for bob (not sponsored) + env(trust(bob, usd(1000))); + env.close(); + + // sponsored=true should not find any objects for bob (doesn't have any sponsored objects) + { + auto const resp = acctObjsSponsored(env, bob.id(), true); + auto const& objs = resp[jss::result][jss::account_objects]; + BEAST_EXPECT(objs.size() == 0); + } + + // Now sponsor bob's trust line + auto const trustId = keylet::trustLine(bob, gw, usd.currency); + if (!BEAST_EXPECT(env.le(trustId))) + return; + + env(sponsor::transfer(bob, tfSponsorshipCreate, trustId.key), + sponsor::As(sponsor1, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor1)); + env.close(); + + // Verify trust line has sponsor field + { + auto const sle = env.le(trustId); + if (!BEAST_EXPECT(sle)) + return; + BEAST_EXPECT(sle->isFieldPresent(sfHighSponsor) || sle->isFieldPresent(sfLowSponsor)); + } + + // sponsored=true on bob should include the sponsored trust line + { + auto const resp = acctObjsSponsored(env, bob.id(), true); + auto const& objs = resp[jss::result][jss::account_objects]; + if (!BEAST_EXPECT(objs.size() == 1)) + return; + + auto const& obj = objs[0u]; + BEAST_EXPECT(obj[sfLedgerEntryType.jsonName] == jss::RippleState); + BEAST_EXPECT( + obj.isMember(sfHighSponsor.jsonName) || obj.isMember(sfLowSponsor.jsonName)); + } + + // sponsored=false on bob should NOT include the sponsored trust line + { + auto const resp = acctObjsSponsored(env, bob.id(), false); + BEAST_EXPECT(resp[jss::result][jss::account_objects].size() == 0); + } + + // A trust line sponsored on either side is classified as sponsored + // for both parties. + { + Env env(*this, testableAmendments()); + Account const issuer("issuer"); + Account const user("user"); + Account const sponsor("sponsor"); + auto const usd = issuer["USD"]; + + env.fund(XRP(10000), issuer, user, sponsor); + env.close(); + + env(trust(issuer, user["USD"](100))); + env.close(); + + env(trust(user, usd(100))); + env.close(); + + auto const trustId = keylet::trustLine(user, issuer, usd.currency); + if (!BEAST_EXPECT(env.le(trustId))) + return; + + env(sponsor::transfer(user, tfSponsorshipCreate, trustId.key), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); + env.close(); + + auto const line = env.le(trustId); + if (!BEAST_EXPECT(line)) + return; + + auto const userIsHigh = line->getFieldAmount(sfHighLimit).getIssuer() == user.id(); + auto const& userSponsorField = userIsHigh ? sfHighSponsor : sfLowSponsor; + auto const& issuerSponsorField = userIsHigh ? sfLowSponsor : sfHighSponsor; + + BEAST_EXPECT(line->isFieldPresent(userSponsorField)); + BEAST_EXPECT(!line->isFieldPresent(issuerSponsorField)); + + { + auto const resp = acctObjsSponsored(env, user.id(), true, jss::state); + auto const& objs = resp[jss::result][jss::account_objects]; + if (BEAST_EXPECT(objs.size() == 1)) + BEAST_EXPECT(objs[0u][sfLedgerEntryType.jsonName] == jss::RippleState); + } + { + auto const resp = acctObjsSponsored(env, user.id(), false, jss::state); + auto const& objs = resp[jss::result][jss::account_objects]; + BEAST_EXPECT(objs.size() == 0); + } + { + auto const resp = acctObjsSponsored(env, issuer.id(), true, jss::state); + auto const& objs = resp[jss::result][jss::account_objects]; + if (BEAST_EXPECT(objs.size() == 1)) + BEAST_EXPECT(objs[0u][sfLedgerEntryType.jsonName] == jss::RippleState); + } + { + auto const resp = acctObjsSponsored(env, issuer.id(), false, jss::state); + auto const& objs = resp[jss::result][jss::account_objects]; + BEAST_EXPECT(objs.size() == 0); + } + } + + // A sponsored Check is classified as sponsored in both the writer's + // and the destination's results. + { + Env env(*this, testableAmendments()); + Account const owner("owner"); + Account const dest("dest"); + Account const sponsor("sponsor"); + + env.fund(XRP(10000), owner, dest, sponsor); + env.close(); + + auto const checkSeq = env.seq(owner); + env(check::create(owner, dest, XRP(1))); + env.close(); + + auto const checkId = keylet::check(owner, checkSeq); + if (!BEAST_EXPECT(env.le(checkId))) + return; + + env(sponsor::transfer(owner, tfSponsorshipCreate, checkId.key), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); + env.close(); + + { + auto const sle = env.le(checkId); + if (!BEAST_EXPECT(sle)) + return; + BEAST_EXPECT(sle->isFieldPresent(sfSponsor)); + } + + for (auto const& acct : {owner.id(), dest.id()}) + { + { + auto const resp = acctObjsSponsored(env, acct, true, jss::check); + auto const& objs = resp[jss::result][jss::account_objects]; + if (BEAST_EXPECT(objs.size() == 1)) + BEAST_EXPECT(objs[0u][sfLedgerEntryType.jsonName] == jss::Check); + } + { + auto const resp = acctObjsSponsored(env, acct, false, jss::check); + BEAST_EXPECT(resp[jss::result][jss::account_objects].size() == 0); + } + } + } + + // A Sponsorship object is visible to both sides. + { + Env env(*this, testableAmendments()); + Account const owner("owner"); + Account const sponsee("sponsee"); + + env.fund(XRP(10000), owner, sponsee); + env.close(); + + env(sponsor::set(owner, 0, 100, XRP(100)), sponsor::SponseeAcc(sponsee)); + env.close(); + + auto const sponsorshipKeylet = keylet::sponsorship(owner, sponsee); + if (!BEAST_EXPECT(env.le(sponsorshipKeylet))) + return; + + { + auto const resp = acctObjsSponsored(env, owner.id(), false, jss::sponsorship); + auto const& objs = resp[jss::result][jss::account_objects]; + if (BEAST_EXPECT(objs.size() == 1)) + BEAST_EXPECT(objs[0u][sfLedgerEntryType.jsonName] == jss::Sponsorship); + } + { + auto const resp = acctObjsSponsored(env, sponsee.id(), false, jss::sponsorship); + auto const& objs = resp[jss::result][jss::account_objects]; + if (BEAST_EXPECT(objs.size() == 1)) + BEAST_EXPECT(objs[0u][sfLedgerEntryType.jsonName] == jss::Sponsorship); + } + { + auto const resp = acctObjsSponsored(env, owner.id(), true, jss::sponsorship); + auto const& objs = resp[jss::result][jss::account_objects]; + BEAST_EXPECT(objs.size() == 0); + } + { + auto const resp = acctObjsSponsored(env, sponsee.id(), true, jss::sponsorship); + auto const& objs = resp[jss::result][jss::account_objects]; + BEAST_EXPECT(objs.size() == 0); + } + } + } + void run() override { @@ -1360,6 +1626,7 @@ public: testNFTsMarker(); testAccountNFTs(); testAccountObjectMarker(); + testSponsoredFilter(); } }; diff --git a/src/test/rpc/AccountTx_test.cpp b/src/test/rpc/AccountTx_test.cpp index ace8743e1e..a7f37d39b9 100644 --- a/src/test/rpc/AccountTx_test.cpp +++ b/src/test/rpc/AccountTx_test.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -773,7 +774,7 @@ class AccountTx_test : public beast::unit_test::Suite // All it takes is a large enough XRP payment to resurrect // becky's account. Try too small a payment. - env(pay(alice, becky, drops(env.current()->fees().accountReserve(0)) - drops(1)), + env(pay(alice, becky, drops(env.current()->fees().accountReserve(0, 1)) - drops(1)), Ter(tecNO_DST_INSUF_XRP)); env.close(); @@ -888,6 +889,82 @@ class AccountTx_test : public beast::unit_test::Suite checkAliceAcctTx(9, jss::Payment); } + void + testSponsorship() + { + // test all sponsored transactions are in sponsor and sponsee's account + // tx list + testcase("Sponsorship"); + + using namespace test::jtx; + Env env(*this); + Account const alice("alice"); + Account const sponsor("sponsor"); + Account const sponsor2("sponsor2"); + env.fund(XRP(10000), alice, sponsor, sponsor2); + env.close(); + + // check the latest sponsorship-related txn is in account tx list + auto const checkTx = [&](Account const& account, json::StaticString txType) { + json::Value params; + params[jss::account] = account.human(); + params[jss::limit] = 100; + auto const jv = env.rpc("json", "account_tx", to_string(params))[jss::result]; + + auto const& tx0(jv[jss::transactions][0u][jss::tx]); + BEAST_EXPECT(tx0[jss::TransactionType] == txType); + + std::string const txHash{ + env.tx()->getJson(JsonOptions::Values::None)[jss::hash].asString()}; + BEAST_EXPECT(tx0[jss::hash] == txHash); + }; + + // fee sponsorship + env(noop(alice), sponsor::As(sponsor, spfSponsorFee), Sig(sfSponsorSignature, sponsor)); + env.close(); + checkTx(alice, jss::AccountSet); + checkTx(sponsor, jss::AccountSet); + + // set sponsor + env(sponsor::set(sponsor, 0, 100, XRP(100)), sponsor::SponseeAcc(alice), Ter(tesSUCCESS)); + env.close(); + checkTx(alice, jss::SponsorshipSet); + checkTx(sponsor, jss::SponsorshipSet); + + // create an object with sponsor + auto const checkId = keylet::check(alice, env.seq(alice)).key; + env(check::create(alice, sponsor, XRP(1)), sponsor::As(sponsor, spfSponsorReserve)); + env.close(); + checkTx(alice, jss::CheckCreate); + checkTx(sponsor, jss::CheckCreate); + + // transfer object sponsorship + env(sponsor::transfer(alice, tfSponsorshipReassign, checkId), + sponsor::As(sponsor2, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor2)); + env.close(); + checkTx(alice, jss::SponsorshipTransfer); + checkTx(sponsor, jss::SponsorshipTransfer); + checkTx(sponsor2, jss::SponsorshipTransfer); + + // delete the sponsored object + env(check::cancel(alice, checkId), + sponsor::As(sponsor, spfSponsorFee), + Sig(sfSponsorSignature, sponsor)); + env.close(); + checkTx(alice, jss::CheckCancel); + checkTx(sponsor, jss::CheckCancel); + checkTx(sponsor2, jss::CheckCancel); + + // account sponsorship + env(sponsor::transfer(alice, tfSponsorshipCreate), + sponsor::As(sponsor, spfSponsorReserve), + Sig(sfSponsorSignature, sponsor)); + env.close(); + checkTx(alice, jss::SponsorshipTransfer); + checkTx(sponsor, jss::SponsorshipTransfer); + } + public: void run() override @@ -896,6 +973,7 @@ public: testContents(); testAccountDelete(); testMPT(); + testSponsorship(); } }; BEAST_DEFINE_TESTSUITE(AccountTx, rpc, xrpl); diff --git a/src/test/rpc/LedgerEntry_test.cpp b/src/test/rpc/LedgerEntry_test.cpp index c7ef5cef2d..d321ab39aa 100644 --- a/src/test/rpc/LedgerEntry_test.cpp +++ b/src/test/rpc/LedgerEntry_test.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -93,6 +94,8 @@ std::vector> gMappings{ {jss::oracle_document_id, FieldType::UInt32Field}, {jss::owner, FieldType::AccountField}, {jss::seq, FieldType::UInt32Field}, + {jss::sponsor, FieldType::AccountField}, + {jss::sponsee, FieldType::AccountField}, {jss::subject, FieldType::AccountField}, {jss::ticket_seq, FieldType::UInt32Field}, }; @@ -107,7 +110,7 @@ getFieldType(json::StaticString fieldName) return it->second; } - Throw("`mappings` is missing field " + std::string(fieldName.cStr())); + Throw("`gMappings` is missing field " + std::string(fieldName.cStr())); } std::string @@ -1884,6 +1887,59 @@ class LedgerEntry_test : public beast::unit_test::Suite runLedgerEntryTest(env, jss::signer_list); } + void + testSponsorship() + { + testcase("Sponsorship"); + + using namespace test::jtx; + + Env env{*this}; + Account const alice{"alice"}; + Account const bob{"bob"}; + env.fund(XRP(10000), alice, bob); + env.close(); + env(sponsor::set(alice, 0, 100), sponsor::SponseeAcc(bob)); + env.close(); + std::string const ledgerHash{to_string(env.closed()->header().hash)}; + auto const sponsorshipIndex = to_string(keylet::sponsorship(alice.id(), bob.id()).key); + + { + // Request by sponsor and sponsee. + json::Value jvParams; + jvParams[jss::sponsorship][jss::sponsor] = alice.human(); + jvParams[jss::sponsorship][jss::sponsee] = bob.human(); + jvParams[jss::ledger_hash] = ledgerHash; + auto const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Sponsorship); + BEAST_EXPECT(jrr[jss::node][sfOwner.jsonName] == alice.human()); + BEAST_EXPECT(jrr[jss::node][sfSponsee.jsonName] == bob.human()); + BEAST_EXPECT(sponsorshipIndex == jrr[jss::node][jss::index].asString()); + } + { + // Request by index. + json::Value jvParams; + jvParams[jss::sponsorship] = sponsorshipIndex; + jvParams[jss::ledger_hash] = ledgerHash; + json::Value const jrr = + env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result]; + BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Sponsorship); + BEAST_EXPECT(jrr[jss::node][sfOwner.jsonName] == alice.human()); + BEAST_EXPECT(jrr[jss::node][sfSponsee.jsonName] == bob.human()); + BEAST_EXPECT(sponsorshipIndex == jrr[jss::node][jss::index].asString()); + } + { + // Check all malformed cases. + runLedgerEntryTest( + env, + jss::sponsorship, + { + {.fieldName = jss::sponsor, .malformedErrorMsg = "malformedSponsor"}, + {.fieldName = jss::sponsee, .malformedErrorMsg = "malformedSponsee"}, + }); + } + } + void testTicket() { @@ -2676,6 +2732,7 @@ public: testPayChan(); testRippleState(); testSignerList(); + testSponsorship(); testTicket(); testDID(); testInvalidOracleLedgerEntry(); diff --git a/src/test/rpc/Simulate_test.cpp b/src/test/rpc/Simulate_test.cpp index 0d00360a58..16df733a66 100644 --- a/src/test/rpc/Simulate_test.cpp +++ b/src/test/rpc/Simulate_test.cpp @@ -364,6 +364,37 @@ class Simulate_test : public beast::unit_test::Suite auto const resp = env.rpc("json", "simulate", to_string(params)); BEAST_EXPECT(resp[jss::result][jss::error_message] == "Invalid field 'tx.Signers[0]'."); } + { + // Non-object SponsorSignature field + json::Value params; + json::Value txJson = json::ValueType::Object; + txJson[jss::TransactionType] = jss::AccountSet; + txJson[jss::Account] = env.master.human(); + txJson[sfSponsorSignature] = ""; + params[jss::tx_json] = txJson; + + auto const resp = env.rpc("json", "simulate", to_string(params)); + BEAST_EXPECT( + resp[jss::result][jss::error_message] == + "Invalid field 'SponsorSignature', not object."); + } + { + // Invalid SponsorSignature.Signers field + json::Value params; + json::Value txJson = json::ValueType::Object; + txJson[jss::TransactionType] = jss::AccountSet; + txJson[jss::Account] = env.master.human(); + json::Value sponsorSignature = json::ValueType::Object; + sponsorSignature[sfSigners] = "1"; + txJson[sfSponsorSignature] = sponsorSignature; + params[jss::tx_json] = txJson; + + auto const resp = env.rpc("json", "simulate", to_string(params)); + BEAST_EXPECTS( + resp[jss::result][jss::error_message] == + "Invalid field 'tx.SponsorSignature.Signers'.", + resp.toStyledString()); + } { // Invalid transaction json::Value params; @@ -561,6 +592,75 @@ class Simulate_test : public beast::unit_test::Suite // test without autofill testTx(env, tx, validateOutput); } + + { + // autofill sponsor signature + + auto validateOutput = [&](json::Value const& resp, json::Value const& tx) { + auto result = resp[jss::result]; + checkBasicReturnValidity( + result, tx, env.seq(env.master), env.current()->fees().base); + + BEAST_EXPECT(result[jss::engine_result] == "tesSUCCESS"); + BEAST_EXPECT(result[jss::engine_result_code] == 0); + BEAST_EXPECT( + result[jss::engine_result_message] == + "The simulated transaction would have been applied."); + + if (BEAST_EXPECT(result.isMember(jss::meta) || result.isMember(jss::meta_blob))) + { + json::Value const metadata = getJsonMetadata(result); + + if (BEAST_EXPECT(metadata.isMember(sfAffectedNodes.jsonName))) + { + BEAST_EXPECT(metadata[sfAffectedNodes.jsonName].size() == 2); + + auto node = metadata[sfAffectedNodes.jsonName][0u]; + if (BEAST_EXPECT(node.isMember(sfModifiedNode.jsonName))) + { + auto modifiedNode = node[sfModifiedNode]; + BEAST_EXPECT(modifiedNode[sfLedgerEntryType] == "AccountRoot"); + auto previousFields = modifiedNode[sfPreviousFields]; + BEAST_EXPECT(!previousFields.isMember(sfBalance.jsonName)); + } + + auto node2 = metadata[sfAffectedNodes.jsonName][1u]; + if (BEAST_EXPECT(node2.isMember(sfModifiedNode.jsonName))) + { + auto modifiedNode = node2[sfModifiedNode]; + BEAST_EXPECT(modifiedNode[sfLedgerEntryType] == "AccountRoot"); + + auto previousFields = modifiedNode[sfPreviousFields]; + BEAST_EXPECT(previousFields.isMember(sfBalance.jsonName)); + } + } + BEAST_EXPECT(metadata[sfTransactionIndex.jsonName] == 0); + BEAST_EXPECT(metadata[sfTransactionResult.jsonName] == "tesSUCCESS"); + } + }; + + Account const sponsor("sponsor"); + env.fund(XRP(10000), sponsor); + env.close(); + + json::Value tx; + + tx[jss::Account] = env.master.human(); + tx[jss::TransactionType] = jss::AccountSet; + tx[sfDomain.jsonName] = kNewDomain; + tx[sfSponsor.jsonName] = sponsor.human(); + tx[sfSponsorFlags.jsonName] = spfSponsorFee; + tx[sfSponsorSignature.jsonName] = json::ValueType::Object; + + // test with autofill + testTx(env, tx, validateOutput); + + tx[sfSponsorSignature.jsonName][sfTxnSignature.jsonName] = ""; + tx[sfSponsorSignature.jsonName][sfSigningPubKey.jsonName] = ""; + + // test without autofill + testTx(env, tx, validateOutput); + } } void @@ -763,6 +863,58 @@ class Simulate_test : public beast::unit_test::Suite } } + void + testSuccessfulSponsoredTransactionMultisigned() + { + testcase("Successful sponsored multi-signed transaction"); + + using namespace jtx; + Env env(*this); + Account const sponsor("sponsor"); + Account const signer("signer"); + env.fund(XRP(10000), sponsor, signer); + env.close(); + + env(signers(sponsor, 1, {{signer, 1}})); + env.close(); + + auto validateOutput = [&](json::Value const& resp, json::Value const& tx) { + auto const result = resp[jss::result]; + // Verifies Fee autofill counts nested sponsor-signature signers. + auto const expectedFee = env.current()->fees().base * 2; + checkBasicReturnValidity(result, tx, env.seq(env.master), expectedFee); + + BEAST_EXPECT(result[jss::engine_result] == "tesSUCCESS"); + BEAST_EXPECT(result[jss::engine_result_code] == 0); + BEAST_EXPECT( + result[jss::engine_result_message] == + "The simulated transaction would have been applied."); + + if (BEAST_EXPECT(result.isMember(jss::meta) || result.isMember(jss::meta_blob))) + { + json::Value const metadata = getJsonMetadata(result); + BEAST_EXPECT(metadata[sfTransactionResult.jsonName] == "tesSUCCESS"); + } + }; + + json::Value tx; + tx[jss::Account] = env.master.human(); + tx[jss::TransactionType] = jss::AccountSet; + tx[sfDomain] = "123ABC"; + tx[sfSponsor.jsonName] = sponsor.human(); + tx[sfSponsorFlags.jsonName] = spfSponsorFee; + tx[sfSponsorSignature.jsonName] = json::ValueType::Object; + tx[sfSponsorSignature.jsonName][sfSigners.jsonName] = json::ValueType::Array; + + json::Value signerObj; + signerObj[sfSigner][jss::Account] = signer.human(); + tx[sfSponsorSignature.jsonName][sfSigners.jsonName].append(signerObj); + + // Leave Fee unset so simulate must autofill it after sponsor signer normalization. + BEAST_EXPECT(!tx.isMember(jss::Fee)); + testTx(env, tx, validateOutput, false); + } + void testTransactionSigningFailure() { @@ -1195,6 +1347,7 @@ public: testTransactionNonTecFailure(); testTransactionTecFailure(); testSuccessfulTransactionMultisigned(); + testSuccessfulSponsoredTransactionMultisigned(); testTransactionSigningFailure(); testInvalidSingleAndMultiSigningTransaction(); testMultisignedBadPubKey(); diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/AccountRootTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/AccountRootTests.cpp index e967b614b7..17c80899f9 100644 --- a/src/tests/libxrpl/protocol_autogen/ledger_entries/AccountRootTests.cpp +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/AccountRootTests.cpp @@ -40,6 +40,9 @@ TEST(AccountRootTests, BuilderSettersRoundTrip) auto const mintedNFTokensValue = canonical_UINT32(); auto const burnedNFTokensValue = canonical_UINT32(); auto const firstNFTokenSequenceValue = canonical_UINT32(); + auto const sponsoredOwnerCountValue = canonical_UINT32(); + auto const sponsoringOwnerCountValue = canonical_UINT32(); + auto const sponsoringAccountCountValue = canonical_UINT32(); auto const aMMIDValue = canonical_UINT256(); auto const vaultIDValue = canonical_UINT256(); auto const loanBrokerIDValue = canonical_UINT256(); @@ -67,6 +70,9 @@ TEST(AccountRootTests, BuilderSettersRoundTrip) builder.setMintedNFTokens(mintedNFTokensValue); builder.setBurnedNFTokens(burnedNFTokensValue); builder.setFirstNFTokenSequence(firstNFTokenSequenceValue); + builder.setSponsoredOwnerCount(sponsoredOwnerCountValue); + builder.setSponsoringOwnerCount(sponsoringOwnerCountValue); + builder.setSponsoringAccountCount(sponsoringAccountCountValue); builder.setAMMID(aMMIDValue); builder.setVaultID(vaultIDValue); builder.setLoanBrokerID(loanBrokerIDValue); @@ -228,6 +234,30 @@ TEST(AccountRootTests, BuilderSettersRoundTrip) EXPECT_TRUE(entry.hasFirstNFTokenSequence()); } + { + auto const& expected = sponsoredOwnerCountValue; + auto const actualOpt = entry.getSponsoredOwnerCount(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfSponsoredOwnerCount"); + EXPECT_TRUE(entry.hasSponsoredOwnerCount()); + } + + { + auto const& expected = sponsoringOwnerCountValue; + auto const actualOpt = entry.getSponsoringOwnerCount(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfSponsoringOwnerCount"); + EXPECT_TRUE(entry.hasSponsoringOwnerCount()); + } + + { + auto const& expected = sponsoringAccountCountValue; + auto const actualOpt = entry.getSponsoringAccountCount(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfSponsoringAccountCount"); + EXPECT_TRUE(entry.hasSponsoringAccountCount()); + } + { auto const& expected = aMMIDValue; auto const actualOpt = entry.getAMMID(); @@ -285,6 +315,9 @@ TEST(AccountRootTests, BuilderFromSleRoundTrip) auto const mintedNFTokensValue = canonical_UINT32(); auto const burnedNFTokensValue = canonical_UINT32(); auto const firstNFTokenSequenceValue = canonical_UINT32(); + auto const sponsoredOwnerCountValue = canonical_UINT32(); + auto const sponsoringOwnerCountValue = canonical_UINT32(); + auto const sponsoringAccountCountValue = canonical_UINT32(); auto const aMMIDValue = canonical_UINT256(); auto const vaultIDValue = canonical_UINT256(); auto const loanBrokerIDValue = canonical_UINT256(); @@ -311,6 +344,9 @@ TEST(AccountRootTests, BuilderFromSleRoundTrip) sle->at(sfMintedNFTokens) = mintedNFTokensValue; sle->at(sfBurnedNFTokens) = burnedNFTokensValue; sle->at(sfFirstNFTokenSequence) = firstNFTokenSequenceValue; + sle->at(sfSponsoredOwnerCount) = sponsoredOwnerCountValue; + sle->at(sfSponsoringOwnerCount) = sponsoringOwnerCountValue; + sle->at(sfSponsoringAccountCount) = sponsoringAccountCountValue; sle->at(sfAMMID) = aMMIDValue; sle->at(sfVaultID) = vaultIDValue; sle->at(sfLoanBrokerID) = loanBrokerIDValue; @@ -566,6 +602,45 @@ TEST(AccountRootTests, BuilderFromSleRoundTrip) expectEqualField(expected, *fromBuilderOpt, "sfFirstNFTokenSequence"); } + { + auto const& expected = sponsoredOwnerCountValue; + + auto const fromSleOpt = entryFromSle.getSponsoredOwnerCount(); + auto const fromBuilderOpt = entryFromBuilder.getSponsoredOwnerCount(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfSponsoredOwnerCount"); + expectEqualField(expected, *fromBuilderOpt, "sfSponsoredOwnerCount"); + } + + { + auto const& expected = sponsoringOwnerCountValue; + + auto const fromSleOpt = entryFromSle.getSponsoringOwnerCount(); + auto const fromBuilderOpt = entryFromBuilder.getSponsoringOwnerCount(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfSponsoringOwnerCount"); + expectEqualField(expected, *fromBuilderOpt, "sfSponsoringOwnerCount"); + } + + { + auto const& expected = sponsoringAccountCountValue; + + auto const fromSleOpt = entryFromSle.getSponsoringAccountCount(); + auto const fromBuilderOpt = entryFromBuilder.getSponsoringAccountCount(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfSponsoringAccountCount"); + expectEqualField(expected, *fromBuilderOpt, "sfSponsoringAccountCount"); + } + { auto const& expected = aMMIDValue; @@ -697,6 +772,12 @@ TEST(AccountRootTests, OptionalFieldsReturnNullopt) EXPECT_FALSE(entry.getBurnedNFTokens().has_value()); EXPECT_FALSE(entry.hasFirstNFTokenSequence()); EXPECT_FALSE(entry.getFirstNFTokenSequence().has_value()); + EXPECT_FALSE(entry.hasSponsoredOwnerCount()); + EXPECT_FALSE(entry.getSponsoredOwnerCount().has_value()); + EXPECT_FALSE(entry.hasSponsoringOwnerCount()); + EXPECT_FALSE(entry.getSponsoringOwnerCount().has_value()); + EXPECT_FALSE(entry.hasSponsoringAccountCount()); + EXPECT_FALSE(entry.getSponsoringAccountCount().has_value()); EXPECT_FALSE(entry.hasAMMID()); EXPECT_FALSE(entry.getAMMID().has_value()); EXPECT_FALSE(entry.hasVaultID()); diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/RippleStateTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/RippleStateTests.cpp index a51ce55f6f..de0769793b 100644 --- a/src/tests/libxrpl/protocol_autogen/ledger_entries/RippleStateTests.cpp +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/RippleStateTests.cpp @@ -31,6 +31,8 @@ TEST(RippleStateTests, BuilderSettersRoundTrip) auto const highNodeValue = canonical_UINT64(); auto const highQualityInValue = canonical_UINT32(); auto const highQualityOutValue = canonical_UINT32(); + auto const highSponsorValue = canonical_ACCOUNT(); + auto const lowSponsorValue = canonical_ACCOUNT(); RippleStateBuilder builder{ balanceValue, @@ -46,6 +48,8 @@ TEST(RippleStateTests, BuilderSettersRoundTrip) builder.setHighNode(highNodeValue); builder.setHighQualityIn(highQualityInValue); builder.setHighQualityOut(highQualityOutValue); + builder.setHighSponsor(highSponsorValue); + builder.setLowSponsor(lowSponsorValue); builder.setLedgerIndex(index); builder.setFlags(0x1u); @@ -134,6 +138,22 @@ TEST(RippleStateTests, BuilderSettersRoundTrip) EXPECT_TRUE(entry.hasHighQualityOut()); } + { + auto const& expected = highSponsorValue; + auto const actualOpt = entry.getHighSponsor(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfHighSponsor"); + EXPECT_TRUE(entry.hasHighSponsor()); + } + + { + auto const& expected = lowSponsorValue; + auto const actualOpt = entry.getLowSponsor(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfLowSponsor"); + EXPECT_TRUE(entry.hasLowSponsor()); + } + EXPECT_TRUE(entry.hasLedgerIndex()); auto const ledgerIndex = entry.getLedgerIndex(); ASSERT_TRUE(ledgerIndex.has_value()); @@ -158,6 +178,8 @@ TEST(RippleStateTests, BuilderFromSleRoundTrip) auto const highNodeValue = canonical_UINT64(); auto const highQualityInValue = canonical_UINT32(); auto const highQualityOutValue = canonical_UINT32(); + auto const highSponsorValue = canonical_ACCOUNT(); + auto const lowSponsorValue = canonical_ACCOUNT(); auto sle = std::make_shared(RippleState::entryType, index); @@ -172,6 +194,8 @@ TEST(RippleStateTests, BuilderFromSleRoundTrip) sle->at(sfHighNode) = highNodeValue; sle->at(sfHighQualityIn) = highQualityInValue; sle->at(sfHighQualityOut) = highQualityOutValue; + sle->at(sfHighSponsor) = highSponsorValue; + sle->at(sfLowSponsor) = lowSponsorValue; RippleStateBuilder builderFromSle{sle}; EXPECT_TRUE(builderFromSle.validate()); @@ -310,6 +334,32 @@ TEST(RippleStateTests, BuilderFromSleRoundTrip) expectEqualField(expected, *fromBuilderOpt, "sfHighQualityOut"); } + { + auto const& expected = highSponsorValue; + + auto const fromSleOpt = entryFromSle.getHighSponsor(); + auto const fromBuilderOpt = entryFromBuilder.getHighSponsor(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfHighSponsor"); + expectEqualField(expected, *fromBuilderOpt, "sfHighSponsor"); + } + + { + auto const& expected = lowSponsorValue; + + auto const fromSleOpt = entryFromSle.getLowSponsor(); + auto const fromBuilderOpt = entryFromBuilder.getLowSponsor(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfLowSponsor"); + expectEqualField(expected, *fromBuilderOpt, "sfLowSponsor"); + } + EXPECT_EQ(entryFromSle.getKey(), index); EXPECT_EQ(entryFromBuilder.getKey(), index); } @@ -384,5 +434,9 @@ TEST(RippleStateTests, OptionalFieldsReturnNullopt) EXPECT_FALSE(entry.getHighQualityIn().has_value()); EXPECT_FALSE(entry.hasHighQualityOut()); EXPECT_FALSE(entry.getHighQualityOut().has_value()); + EXPECT_FALSE(entry.hasHighSponsor()); + EXPECT_FALSE(entry.getHighSponsor().has_value()); + EXPECT_FALSE(entry.hasLowSponsor()); + EXPECT_FALSE(entry.getLowSponsor().has_value()); } } diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/SponsorshipTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/SponsorshipTests.cpp new file mode 100644 index 0000000000..e1d9ff15b9 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/SponsorshipTests.cpp @@ -0,0 +1,329 @@ +// Auto-generated unit tests for ledger entry Sponsorship + + +#include + +#include + +#include +#include +#include + +#include + +namespace xrpl::ledger_entries { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed for both the +// builder's STObject and the wrapper's SLE. +TEST(SponsorshipTests, BuilderSettersRoundTrip) +{ + uint256 const index{1u}; + + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const ownerValue = canonical_ACCOUNT(); + auto const sponseeValue = canonical_ACCOUNT(); + auto const feeAmountValue = canonical_AMOUNT(); + auto const maxFeeValue = canonical_AMOUNT(); + auto const remainingOwnerCountValue = canonical_UINT32(); + auto const ownerNodeValue = canonical_UINT64(); + auto const sponseeNodeValue = canonical_UINT64(); + + SponsorshipBuilder builder{ + previousTxnIDValue, + previousTxnLgrSeqValue, + ownerValue, + sponseeValue, + ownerNodeValue, + sponseeNodeValue + }; + + builder.setFeeAmount(feeAmountValue); + builder.setMaxFee(maxFeeValue); + builder.setRemainingOwnerCount(remainingOwnerCountValue); + + builder.setLedgerIndex(index); + builder.setFlags(0x1u); + + EXPECT_TRUE(builder.validate()); + + auto const entry = builder.build(index); + + EXPECT_TRUE(entry.validate()); + + { + auto const& expected = previousTxnIDValue; + auto const actual = entry.getPreviousTxnID(); + expectEqualField(expected, actual, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + auto const actual = entry.getPreviousTxnLgrSeq(); + expectEqualField(expected, actual, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = ownerValue; + auto const actual = entry.getOwner(); + expectEqualField(expected, actual, "sfOwner"); + } + + { + auto const& expected = sponseeValue; + auto const actual = entry.getSponsee(); + expectEqualField(expected, actual, "sfSponsee"); + } + + { + auto const& expected = ownerNodeValue; + auto const actual = entry.getOwnerNode(); + expectEqualField(expected, actual, "sfOwnerNode"); + } + + { + auto const& expected = sponseeNodeValue; + auto const actual = entry.getSponseeNode(); + expectEqualField(expected, actual, "sfSponseeNode"); + } + + { + auto const& expected = feeAmountValue; + auto const actualOpt = entry.getFeeAmount(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfFeeAmount"); + EXPECT_TRUE(entry.hasFeeAmount()); + } + + { + auto const& expected = maxFeeValue; + auto const actualOpt = entry.getMaxFee(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfMaxFee"); + EXPECT_TRUE(entry.hasMaxFee()); + } + + { + auto const& expected = remainingOwnerCountValue; + auto const actualOpt = entry.getRemainingOwnerCount(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfRemainingOwnerCount"); + EXPECT_TRUE(entry.hasRemainingOwnerCount()); + } + + EXPECT_TRUE(entry.hasLedgerIndex()); + auto const ledgerIndex = entry.getLedgerIndex(); + ASSERT_TRUE(ledgerIndex.has_value()); + EXPECT_EQ(*ledgerIndex, index); + EXPECT_EQ(entry.getKey(), index); +} + +// 2 & 4) Start from an SLE, set fields directly on it, construct a builder +// from that SLE, build a new wrapper, and verify all fields (and validate()). +TEST(SponsorshipTests, BuilderFromSleRoundTrip) +{ + uint256 const index{2u}; + + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const ownerValue = canonical_ACCOUNT(); + auto const sponseeValue = canonical_ACCOUNT(); + auto const feeAmountValue = canonical_AMOUNT(); + auto const maxFeeValue = canonical_AMOUNT(); + auto const remainingOwnerCountValue = canonical_UINT32(); + auto const ownerNodeValue = canonical_UINT64(); + auto const sponseeNodeValue = canonical_UINT64(); + + auto sle = std::make_shared(Sponsorship::entryType, index); + + sle->at(sfPreviousTxnID) = previousTxnIDValue; + sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; + sle->at(sfOwner) = ownerValue; + sle->at(sfSponsee) = sponseeValue; + sle->at(sfFeeAmount) = feeAmountValue; + sle->at(sfMaxFee) = maxFeeValue; + sle->at(sfRemainingOwnerCount) = remainingOwnerCountValue; + sle->at(sfOwnerNode) = ownerNodeValue; + sle->at(sfSponseeNode) = sponseeNodeValue; + + SponsorshipBuilder builderFromSle{sle}; + EXPECT_TRUE(builderFromSle.validate()); + + auto const entryFromBuilder = builderFromSle.build(index); + + Sponsorship entryFromSle{sle}; + EXPECT_TRUE(entryFromBuilder.validate()); + EXPECT_TRUE(entryFromSle.validate()); + + { + auto const& expected = previousTxnIDValue; + + auto const fromSle = entryFromSle.getPreviousTxnID(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnID(); + + expectEqualField(expected, fromSle, "sfPreviousTxnID"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnID"); + } + + { + auto const& expected = previousTxnLgrSeqValue; + + auto const fromSle = entryFromSle.getPreviousTxnLgrSeq(); + auto const fromBuilder = entryFromBuilder.getPreviousTxnLgrSeq(); + + expectEqualField(expected, fromSle, "sfPreviousTxnLgrSeq"); + expectEqualField(expected, fromBuilder, "sfPreviousTxnLgrSeq"); + } + + { + auto const& expected = ownerValue; + + auto const fromSle = entryFromSle.getOwner(); + auto const fromBuilder = entryFromBuilder.getOwner(); + + expectEqualField(expected, fromSle, "sfOwner"); + expectEqualField(expected, fromBuilder, "sfOwner"); + } + + { + auto const& expected = sponseeValue; + + auto const fromSle = entryFromSle.getSponsee(); + auto const fromBuilder = entryFromBuilder.getSponsee(); + + expectEqualField(expected, fromSle, "sfSponsee"); + expectEqualField(expected, fromBuilder, "sfSponsee"); + } + + { + auto const& expected = ownerNodeValue; + + auto const fromSle = entryFromSle.getOwnerNode(); + auto const fromBuilder = entryFromBuilder.getOwnerNode(); + + expectEqualField(expected, fromSle, "sfOwnerNode"); + expectEqualField(expected, fromBuilder, "sfOwnerNode"); + } + + { + auto const& expected = sponseeNodeValue; + + auto const fromSle = entryFromSle.getSponseeNode(); + auto const fromBuilder = entryFromBuilder.getSponseeNode(); + + expectEqualField(expected, fromSle, "sfSponseeNode"); + expectEqualField(expected, fromBuilder, "sfSponseeNode"); + } + + { + auto const& expected = feeAmountValue; + + auto const fromSleOpt = entryFromSle.getFeeAmount(); + auto const fromBuilderOpt = entryFromBuilder.getFeeAmount(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfFeeAmount"); + expectEqualField(expected, *fromBuilderOpt, "sfFeeAmount"); + } + + { + auto const& expected = maxFeeValue; + + auto const fromSleOpt = entryFromSle.getMaxFee(); + auto const fromBuilderOpt = entryFromBuilder.getMaxFee(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfMaxFee"); + expectEqualField(expected, *fromBuilderOpt, "sfMaxFee"); + } + + { + auto const& expected = remainingOwnerCountValue; + + auto const fromSleOpt = entryFromSle.getRemainingOwnerCount(); + auto const fromBuilderOpt = entryFromBuilder.getRemainingOwnerCount(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfRemainingOwnerCount"); + expectEqualField(expected, *fromBuilderOpt, "sfRemainingOwnerCount"); + } + + EXPECT_EQ(entryFromSle.getKey(), index); + EXPECT_EQ(entryFromBuilder.getKey(), index); +} + +// 3) Verify wrapper throws when constructed from wrong ledger entry type. +TEST(SponsorshipTests, WrapperThrowsOnWrongEntryType) +{ + uint256 const index{3u}; + + // Build a valid ledger entry of a different type + // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq + // Check requires: Account, Destination, SendMax, Sequence, OwnerNode, DestinationNode, PreviousTxnID, PreviousTxnLgrSeq + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(Sponsorship{wrongEntry.getSle()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong ledger entry type. +TEST(SponsorshipTests, BuilderThrowsOnWrongEntryType) +{ + uint256 const index{4u}; + + // Build a valid ledger entry of a different type + TicketBuilder wrongBuilder{ + canonical_ACCOUNT(), + canonical_UINT64(), + canonical_UINT32(), + canonical_UINT256(), + canonical_UINT32()}; + auto wrongEntry = wrongBuilder.build(index); + + EXPECT_THROW(SponsorshipBuilder{wrongEntry.getSle()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(SponsorshipTests, OptionalFieldsReturnNullopt) +{ + uint256 const index{3u}; + + auto const previousTxnIDValue = canonical_UINT256(); + auto const previousTxnLgrSeqValue = canonical_UINT32(); + auto const ownerValue = canonical_ACCOUNT(); + auto const sponseeValue = canonical_ACCOUNT(); + auto const ownerNodeValue = canonical_UINT64(); + auto const sponseeNodeValue = canonical_UINT64(); + + SponsorshipBuilder builder{ + previousTxnIDValue, + previousTxnLgrSeqValue, + ownerValue, + sponseeValue, + ownerNodeValue, + sponseeNodeValue + }; + + auto const entry = builder.build(index); + + // Verify optional fields are not present + EXPECT_FALSE(entry.hasFeeAmount()); + EXPECT_FALSE(entry.getFeeAmount().has_value()); + EXPECT_FALSE(entry.hasMaxFee()); + EXPECT_FALSE(entry.getMaxFee().has_value()); + EXPECT_FALSE(entry.hasRemainingOwnerCount()); + EXPECT_FALSE(entry.getRemainingOwnerCount().has_value()); +} +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/SponsorshipSetTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/SponsorshipSetTests.cpp new file mode 100644 index 0000000000..dce8cfca3f --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/SponsorshipSetTests.cpp @@ -0,0 +1,261 @@ +// Auto-generated unit tests for transaction SponsorshipSet + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsSponsorshipSetTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::Secp256k1, generateSeed("testSponsorshipSet")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const counterpartySponsorValue = canonical_ACCOUNT(); + auto const sponseeValue = canonical_ACCOUNT(); + auto const feeAmountValue = canonical_AMOUNT(); + auto const maxFeeValue = canonical_AMOUNT(); + auto const remainingOwnerCountValue = canonical_UINT32(); + + SponsorshipSetBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setCounterpartySponsor(counterpartySponsorValue); + builder.setSponsee(sponseeValue); + builder.setFeeAmount(feeAmountValue); + builder.setMaxFee(maxFeeValue); + builder.setRemainingOwnerCount(remainingOwnerCountValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields + { + auto const& expected = counterpartySponsorValue; + auto const actualOpt = tx.getCounterpartySponsor(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCounterpartySponsor should be present"; + expectEqualField(expected, *actualOpt, "sfCounterpartySponsor"); + EXPECT_TRUE(tx.hasCounterpartySponsor()); + } + + { + auto const& expected = sponseeValue; + auto const actualOpt = tx.getSponsee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfSponsee should be present"; + expectEqualField(expected, *actualOpt, "sfSponsee"); + EXPECT_TRUE(tx.hasSponsee()); + } + + { + auto const& expected = feeAmountValue; + auto const actualOpt = tx.getFeeAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfFeeAmount should be present"; + expectEqualField(expected, *actualOpt, "sfFeeAmount"); + EXPECT_TRUE(tx.hasFeeAmount()); + } + + { + auto const& expected = maxFeeValue; + auto const actualOpt = tx.getMaxFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMaxFee should be present"; + expectEqualField(expected, *actualOpt, "sfMaxFee"); + EXPECT_TRUE(tx.hasMaxFee()); + } + + { + auto const& expected = remainingOwnerCountValue; + auto const actualOpt = tx.getRemainingOwnerCount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfRemainingOwnerCount should be present"; + expectEqualField(expected, *actualOpt, "sfRemainingOwnerCount"); + EXPECT_TRUE(tx.hasRemainingOwnerCount()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsSponsorshipSetTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::Secp256k1, generateSeed("testSponsorshipSetFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const counterpartySponsorValue = canonical_ACCOUNT(); + auto const sponseeValue = canonical_ACCOUNT(); + auto const feeAmountValue = canonical_AMOUNT(); + auto const maxFeeValue = canonical_AMOUNT(); + auto const remainingOwnerCountValue = canonical_UINT32(); + + // Build an initial transaction + SponsorshipSetBuilder initialBuilder{ + accountValue, + sequenceValue, + feeValue + }; + + initialBuilder.setCounterpartySponsor(counterpartySponsorValue); + initialBuilder.setSponsee(sponseeValue); + initialBuilder.setFeeAmount(feeAmountValue); + initialBuilder.setMaxFee(maxFeeValue); + initialBuilder.setRemainingOwnerCount(remainingOwnerCountValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + SponsorshipSetBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields + { + auto const& expected = counterpartySponsorValue; + auto const actualOpt = rebuiltTx.getCounterpartySponsor(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCounterpartySponsor should be present"; + expectEqualField(expected, *actualOpt, "sfCounterpartySponsor"); + } + + { + auto const& expected = sponseeValue; + auto const actualOpt = rebuiltTx.getSponsee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfSponsee should be present"; + expectEqualField(expected, *actualOpt, "sfSponsee"); + } + + { + auto const& expected = feeAmountValue; + auto const actualOpt = rebuiltTx.getFeeAmount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfFeeAmount should be present"; + expectEqualField(expected, *actualOpt, "sfFeeAmount"); + } + + { + auto const& expected = maxFeeValue; + auto const actualOpt = rebuiltTx.getMaxFee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMaxFee should be present"; + expectEqualField(expected, *actualOpt, "sfMaxFee"); + } + + { + auto const& expected = remainingOwnerCountValue; + auto const actualOpt = rebuiltTx.getRemainingOwnerCount(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfRemainingOwnerCount should be present"; + expectEqualField(expected, *actualOpt, "sfRemainingOwnerCount"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsSponsorshipSetTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::Secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(SponsorshipSet{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsSponsorshipSetTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::Secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(SponsorshipSetBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsSponsorshipSetTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::Secp256k1, generateSeed("testSponsorshipSetNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + + SponsorshipSetBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasCounterpartySponsor()); + EXPECT_FALSE(tx.getCounterpartySponsor().has_value()); + EXPECT_FALSE(tx.hasSponsee()); + EXPECT_FALSE(tx.getSponsee().has_value()); + EXPECT_FALSE(tx.hasFeeAmount()); + EXPECT_FALSE(tx.getFeeAmount().has_value()); + EXPECT_FALSE(tx.hasMaxFee()); + EXPECT_FALSE(tx.getMaxFee().has_value()); + EXPECT_FALSE(tx.hasRemainingOwnerCount()); + EXPECT_FALSE(tx.getRemainingOwnerCount().has_value()); +} + +} diff --git a/src/tests/libxrpl/protocol_autogen/transactions/SponsorshipTransferTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/SponsorshipTransferTests.cpp new file mode 100644 index 0000000000..c8521b7b20 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/SponsorshipTransferTests.cpp @@ -0,0 +1,198 @@ +// Auto-generated unit tests for transaction SponsorshipTransfer + + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsSponsorshipTransferTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::Secp256k1, generateSeed("testSponsorshipTransfer")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const objectIDValue = canonical_UINT256(); + auto const sponseeValue = canonical_ACCOUNT(); + + SponsorshipTransferBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; + + // Set optional fields + builder.setObjectID(objectIDValue); + builder.setSponsee(sponseeValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields + { + auto const& expected = objectIDValue; + auto const actualOpt = tx.getObjectID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfObjectID should be present"; + expectEqualField(expected, *actualOpt, "sfObjectID"); + EXPECT_TRUE(tx.hasObjectID()); + } + + { + auto const& expected = sponseeValue; + auto const actualOpt = tx.getSponsee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfSponsee should be present"; + expectEqualField(expected, *actualOpt, "sfSponsee"); + EXPECT_TRUE(tx.hasSponsee()); + } + +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsSponsorshipTransferTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::Secp256k1, generateSeed("testSponsorshipTransferFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const objectIDValue = canonical_UINT256(); + auto const sponseeValue = canonical_ACCOUNT(); + + // Build an initial transaction + SponsorshipTransferBuilder initialBuilder{ + accountValue, + sequenceValue, + feeValue + }; + + initialBuilder.setObjectID(objectIDValue); + initialBuilder.setSponsee(sponseeValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + SponsorshipTransferBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + // Verify optional fields + { + auto const& expected = objectIDValue; + auto const actualOpt = rebuiltTx.getObjectID(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfObjectID should be present"; + expectEqualField(expected, *actualOpt, "sfObjectID"); + } + + { + auto const& expected = sponseeValue; + auto const actualOpt = rebuiltTx.getSponsee(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfSponsee should be present"; + expectEqualField(expected, *actualOpt, "sfSponsee"); + } + +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsSponsorshipTransferTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::Secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(SponsorshipTransfer{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsSponsorshipTransferTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = + generateKeyPair(KeyType::Secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(SponsorshipTransferBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsSponsorshipTransferTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::Secp256k1, generateSeed("testSponsorshipTransferNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + + SponsorshipTransferBuilder builder{ + accountValue, + sequenceValue, + feeValue + }; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasObjectID()); + EXPECT_FALSE(tx.getObjectID().has_value()); + EXPECT_FALSE(tx.hasSponsee()); + EXPECT_FALSE(tx.getSponsee().has_value()); +} + +} diff --git a/src/xrpld/app/misc/detail/TxQ.cpp b/src/xrpld/app/misc/detail/TxQ.cpp index f6d00974b4..041d2ade1e 100644 --- a/src/xrpld/app/misc/detail/TxQ.cpp +++ b/src/xrpld/app/misc/detail/TxQ.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -408,6 +409,9 @@ TxQ::canBeHeld( // Disallow delegated transactions from being queued. if (tx.isFieldPresent(sfDelegate)) return telCAN_NOT_QUEUE; + // Disallow fee-sponsored transactions from being queued. + if (isFeeSponsored(tx)) + return telCAN_NOT_QUEUE; { // To be queued and relayed, the transaction needs to diff --git a/src/xrpld/rpc/detail/TransactionSign.cpp b/src/xrpld/rpc/detail/TransactionSign.cpp index 8c3a5ea245..b8731a2289 100644 --- a/src/xrpld/rpc/detail/TransactionSign.cpp +++ b/src/xrpld/rpc/detail/TransactionSign.cpp @@ -1256,7 +1256,7 @@ transactionSignFor( // The array must be sorted and validated. // For delegated transactions, the delegate account is // the one forbidden from appearing in its own Signers array. - auto err = sortAndValidateSigners(signers, sttx->getFeePayer()); + auto err = sortAndValidateSigners(signers, sttx->getInitiator()); if (RPC::containsError(err)) return err; } @@ -1423,9 +1423,9 @@ transactionSubmitMultiSigned( } // The array must be sorted and validated. - // For delegated transactions, getFeePayer() returns sfDelegate, + // For delegated transactions, getInitiator() returns sfDelegate, // that account is the one forbidden from appearing in its own Signers array. - auto err = sortAndValidateSigners(signers, stTx->getFeePayer()); + auto err = sortAndValidateSigners(signers, stTx->getInitiator()); if (RPC::containsError(err)) return err; diff --git a/src/xrpld/rpc/handlers/account/AccountObjects.cpp b/src/xrpld/rpc/handlers/account/AccountObjects.cpp index 8375feb4e7..8967509262 100644 --- a/src/xrpld/rpc/handlers/account/AccountObjects.cpp +++ b/src/xrpld/rpc/handlers/account/AccountObjects.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,7 @@ namespace xrpl { @param dirIndex Begin gathering account objects from this directory. @param entryIndex Begin gathering objects from this directory node. @param limit Maximum number of objects to find. + @param sponsoredFilter If set, only return objects whose sponsored state matches the value. @param jvResult A JSON result that holds the request objects. */ bool @@ -43,6 +45,7 @@ getAccountObjects( uint256 dirIndex, uint256 entryIndex, std::uint32_t const limit, + std::optional const sponsoredFilter, json::Value& jvResult) { // check if dirIndex is valid @@ -55,6 +58,12 @@ getAccountObjects( return it != typeFilter.end(); }; + auto sponsoredMatchesFilter = [&sponsoredFilter](std::optional const& sponsor) { + if (!sponsoredFilter.has_value()) + return true; + return sponsor.has_value() == *sponsoredFilter; + }; + // if dirIndex != 0, then all NFTs have already been returned. only // iterate NFT pages if the filter says so AND dirIndex == 0 bool iterateNFTPages = @@ -93,7 +102,10 @@ getAccountObjects( while (currentPage) { - jvObjects.append(currentPage->getJson(JsonOptions::Values::None)); + std::optional const nftSponsor = currentPage->at(~sfSponsor); + bool const canAppendNFT = sponsoredMatchesFilter(nftSponsor); + if (canAppendNFT) + jvObjects.append(currentPage->getJson()); auto const npm = (*currentPage)[~sfNextPageMin]; if (npm) { @@ -179,11 +191,32 @@ getAccountObjects( { auto const sleNode = ledger.read(keylet::child(*entryIter)); - if (!typeFilter.has_value() || - typeMatchesFilter(typeFilter.value(), sleNode->getType())) + bool canAppend = true; + + if (typeFilter.has_value() && + !typeMatchesFilter(typeFilter.value(), sleNode->getType())) + canAppend = false; + + // An object counts as sponsored no matter which party's directory + // it was found through; the sponsorship need not belong to + // `account`'s side. + std::optional sponsor; + if (sleNode->getType() == ltRIPPLE_STATE) { - jvObjects.append(sleNode->getJson(JsonOptions::Values::None)); + sponsor = getLedgerEntryReserveSponsorID(sleNode, sfHighSponsor); + if (!sponsor) + sponsor = getLedgerEntryReserveSponsorID(sleNode, sfLowSponsor); } + else if (isLedgerEntrySupportedBySponsorship(*sleNode)) + { + sponsor = getLedgerEntryReserveSponsorID(sleNode); + } + + if (!sponsoredMatchesFilter(sponsor)) + canAppend = false; + + if (canAppend) + jvObjects.append(sleNode->getJson(JsonOptions::Values::None)); if (++itemsAdded == limitLeft) { @@ -271,6 +304,7 @@ doAccountObjects(RPC::JsonContext& context) {.name = jss::mptoken, .type = ltMPTOKEN}, {.name = jss::permissioned_domain, .type = ltPERMISSIONED_DOMAIN}, {.name = jss::vault, .type = ltVAULT}, + {.name = jss::sponsorship, .type = ltSPONSORSHIP}, }; typeFilter.emplace(); @@ -329,7 +363,18 @@ doAccountObjects(RPC::JsonContext& context) return RPC::invalidFieldError(jss::marker); } - if (!getAccountObjects(*ledger, accountID, typeFilter, dirIndex, entryIndex, limit, result)) + std::optional sponsoredFilter; + if (params.isMember(jss::sponsored)) + { + auto const& sponsoredJv = params[jss::sponsored]; + if (!sponsoredJv.isBool()) + return RPC::expectedFieldError(jss::sponsored, "boolean"); + + sponsoredFilter = sponsoredJv.asBool(); + } + + if (!getAccountObjects( + *ledger, accountID, typeFilter, dirIndex, entryIndex, limit, sponsoredFilter, result)) return RPC::invalidFieldError(jss::marker); result[jss::account] = toBase58(accountID); diff --git a/src/xrpld/rpc/handlers/ledger/LedgerEntry.cpp b/src/xrpld/rpc/handlers/ledger/LedgerEntry.cpp index 2dd7ae34d4..784be779bb 100644 --- a/src/xrpld/rpc/handlers/ledger/LedgerEntry.cpp +++ b/src/xrpld/rpc/handlers/ledger/LedgerEntry.cpp @@ -719,6 +719,28 @@ parseSignerList( return parseObjectID(params, fieldName, "hex string"); } +static std::expected +parseSponsorship( + json::Value const& params, + json::StaticString const fieldName, + [[maybe_unused]] unsigned const apiVersion) +{ + if (!params.isObject()) + return parseObjectID(params, fieldName); + + auto const sponsorID = + LedgerEntryHelpers::requiredAccountID(params, jss::sponsor, "malformedSponsor"); + if (!sponsorID) + return std::unexpected(sponsorID.error()); + + auto const sponseeID = + LedgerEntryHelpers::requiredAccountID(params, jss::sponsee, "malformedSponsee"); + if (!sponseeID) + return std::unexpected(sponseeID.error()); + + return keylet::sponsorship(*sponsorID, *sponseeID).key; +} + static std::expected parseTicket( json::Value const& params, diff --git a/src/xrpld/rpc/handlers/transaction/Simulate.cpp b/src/xrpld/rpc/handlers/transaction/Simulate.cpp index 2a6c2280e3..0f163c7356 100644 --- a/src/xrpld/rpc/handlers/transaction/Simulate.cpp +++ b/src/xrpld/rpc/handlers/transaction/Simulate.cpp @@ -77,7 +77,7 @@ getAutofillSequence(json::Value const& txJson, RPC::JsonContext& context) } static std::optional -autofillSignature(json::Value& sigObject) +autofillSignature(json::Value& sigObject, std::string const& fieldPrefix = "tx") { if (!sigObject.isMember(jss::SigningPubKey)) { @@ -88,14 +88,17 @@ autofillSignature(json::Value& sigObject) if (sigObject.isMember(jss::Signers)) { if (!sigObject[jss::Signers].isArray()) - return RPC::invalidFieldError("tx.Signers"); + return RPC::invalidFieldError(fieldPrefix + ".Signers"); // check multisigned signers for (unsigned index = 0; index < sigObject[jss::Signers].size(); index++) { auto& signer = sigObject[jss::Signers][index]; if (!signer.isObject() || !signer.isMember(jss::Signer) || !signer[jss::Signer].isObject()) - return RPC::invalidFieldError("tx.Signers[" + std::to_string(index) + "]"); + { + return RPC::invalidFieldError( + fieldPrefix + ".Signers[" + std::to_string(index) + "]"); + } if (!signer[jss::Signer].isMember(jss::SigningPubKey)) { @@ -132,26 +135,19 @@ autofillSignature(json::Value& sigObject) static std::optional autofillTx(json::Value& txJson, RPC::JsonContext& context) { - if (!txJson.isMember(jss::Fee)) - { - // autofill Fee - // Must happen after all the other autofills happen - // Error handling/messaging works better that way - auto feeOrError = RPC::getCurrentNetworkFee( - context.role, - context.app.config(), - context.app.getFeeTrack(), - context.app.getTxQ(), - context.app, - txJson); - if (feeOrError.isMember(jss::error)) - return feeOrError; - txJson[jss::Fee] = feeOrError; - } - if (auto error = autofillSignature(txJson)) return error; + if (txJson.isMember(sfSponsorSignature.jsonName)) + { + auto& sponsorSignature = txJson[sfSponsorSignature.jsonName]; + if (!sponsorSignature.isObject()) + return RPC::objectFieldError(sfSponsorSignature.jsonName); + + if (auto const error = autofillSignature(sponsorSignature, "tx.SponsorSignature")) + return error; + } + if (!txJson.isMember(jss::Sequence)) { auto const seq = getAutofillSequence(txJson, context); @@ -167,6 +163,22 @@ autofillTx(json::Value& txJson, RPC::JsonContext& context) txJson[jss::NetworkID] = to_string(networkId); } + if (!txJson.isMember(jss::Fee)) + { + // Autofill Fee after normalizing nested signer fields so the fee + // estimator sees the full transaction shape. + auto feeOrError = RPC::getCurrentNetworkFee( + context.role, + context.app.config(), + context.app.getFeeTrack(), + context.app.getTxQ(), + context.app, + txJson); + if (feeOrError.isMember(jss::error)) + return feeOrError; + txJson[jss::Fee] = feeOrError; + } + return std::nullopt; } From 8306ac7710d86bdfd26a84597fc7769afbd3f539 Mon Sep 17 00:00:00 2001 From: Ed Hennis Date: Fri, 10 Jul 2026 20:18:31 -0400 Subject: [PATCH 2/5] fix: Improve Number addition/subtraction rounding (#7369) Co-authored-by: xrplf-ai-reviewer[bot] <266832837+xrplf-ai-reviewer[bot]@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- include/xrpl/basics/Number.h | 123 +++--- src/libxrpl/basics/Number.cpp | 593 ++++++++++++++++++---------- src/libxrpl/protocol/Rules.cpp | 45 ++- src/libxrpl/protocol/STNumber.cpp | 5 +- src/test/app/Invariants_test.cpp | 7 +- src/tests/libxrpl/basics/Number.cpp | 575 +++++++++++++++++++++++---- 6 files changed, 994 insertions(+), 354 deletions(-) diff --git a/include/xrpl/basics/Number.h b/include/xrpl/basics/Number.h index 073da12f89..28c7b1dbda 100644 --- a/include/xrpl/basics/Number.h +++ b/include/xrpl/basics/Number.h @@ -14,7 +14,6 @@ #include #include #include -#include #include namespace xrpl { @@ -55,37 +54,43 @@ namespace detail { * compile time. Doing it at runtime would be pretty wasteful and * inefficient. */ -constexpr std::size_t kInt64Digits = 20; -consteval std::array +constexpr std::size_t kUint64Digits = 20; +[[maybe_unused]] constexpr std::size_t kUint128Digits = 39; + +template +consteval std::array buildPowersOfTen() { - std::array result{}; + std::array result{}; - std::uint64_t power = 1; + T power = 1; std::size_t exponent = 0; // end the loop early so it doesn't overflow; for (; exponent < result.size() - 1; ++exponent, power *= 10) { result[exponent] = power; - if (power > std::numeric_limits::max() / 10) + if (power > std::numeric_limits::max() / 10) throw std::logic_error("Power of 10 table is too big"); } result[exponent] = power; - if (power < std::numeric_limits::max() / 10) - throw std::logic_error("Power of 10 table is not big enough for the uint64_t type"); + if (power < std::numeric_limits::max() / 10) + throw std::logic_error("Power of 10 table is not big enough for the given type"); return result; } } // namespace detail -constexpr std::array kPowerOfTen = detail::buildPowersOfTen(); +template +constexpr std::array kPowerOfTenImpl = detail::buildPowersOfTen(); + +constexpr auto kPowerOfTen = kPowerOfTenImpl; static_assert(kPowerOfTen[0] == 1); static_assert(kPowerOfTen[1] == 10); static_assert(kPowerOfTen[10] == 10'000'000'000); static_assert( - isPowerOfTen(kPowerOfTen.back()) && *logTen(kPowerOfTen.back()) == detail::kInt64Digits - 1); + isPowerOfTen(kPowerOfTen.back()) && *logTen(kPowerOfTen.back()) == detail::kUint64Digits - 1); /** MantissaRange defines a range for the mantissa of a normalized Number. * @@ -124,17 +129,37 @@ struct MantissaRange final { using rep = std::uint64_t; + // NOLINTBEGIN(readability-enum-initial-value) + // The values don't matter, except for Large enum class MantissaScale { + // Small can be removed when either featureSingleAssetVault or featureLendingProtocol are + // retired Small, // LargeLegacy can be removed when fixCleanup3_2_0 is retired LargeLegacy, - Large, + // Large320 can be removed when fixCleanup3_3_0 is retired + Large320, + // If Large330 is ever the only remaining "Large*" entry, it can be renamed to just "Large". + Large330, + // Large is a de-facto alias for "the latest", and is only here for backward compatibility + // in the extremely unlikely case that a downstream project made use of it. Note that + // because the behavior changed, this may still be a breaking change. + Large = Large330, }; + // NOLINTEND(readability-enum-initial-value) - // This entire enum can be removed when fixCleanup3_2_0 is retired - enum class CuspRoundingFix : bool { - Disabled = false, - Enabled = true, + // This entire enum can be removed when the last relevant amendment is retired + enum class CuspRoundingFix : std::uint8_t { + // Disabled can be removed when fixCleanup3_2_0 is retired + Disabled = 0, + // Enabled320 can be removed when fixCleanup3_3_0 is retired + Enabled320 = 1, + // If we ever get to the point that there's only one entry, remove the entire enum + Enabled330 = 2, + // Enabled is a de-facto alias for "the latest", and is only here for backward compatibility + // in the extremely unlikely case that a downstream project made use of it. Note that + // because the behavior changed, this may still be a breaking change. + Enabled = Enabled330, }; explicit constexpr MantissaRange(MantissaScale sc) : scale(sc) @@ -145,13 +170,27 @@ struct MantissaRange final int const log{getExponent(scale)}; rep const min{getMin(scale, log)}; rep const max{(min * 10) - 1}; - CuspRoundingFix const cuspRoundingFixEnabled{isCuspFixEnabled(scale)}; - - static MantissaRange const& - getMantissaRange(MantissaScale scale); + CuspRoundingFix const cuspRoundingFix{isCuspFixEnabled(scale)}; static std::set const& - getAllScales(); + getAllScales() + { + static std::set const kScales = { + MantissaRange::MantissaScale::Small, + MantissaRange::MantissaScale::LargeLegacy, + MantissaRange::MantissaScale::Large320, + MantissaRange::MantissaScale::Large330, + }; + return kScales; + } + + class Access + { + static constexpr MantissaRange const& + mantissaRange(MantissaScale scale); + + friend Number; + }; private: static constexpr int @@ -162,7 +201,8 @@ private: case MantissaScale::Small: return 15; case MantissaScale::LargeLegacy: - case MantissaScale::Large: + case MantissaScale::Large320: + case MantissaScale::Large330: return 18; // LCOV_EXCL_START default: @@ -191,17 +231,16 @@ private: case MantissaScale::Small: case MantissaScale::LargeLegacy: return CuspRoundingFix::Disabled; - case MantissaScale::Large: - return CuspRoundingFix::Enabled; + case MantissaScale::Large320: + return CuspRoundingFix::Enabled320; + case MantissaScale::Large330: + return CuspRoundingFix::Enabled330; default: // If called in a constexpr context, this throw assures that the build fails if an // invalid scale is used. throw std::runtime_error("Unknown mantissa scale"); // LCOV_EXCL_LINE } } - - static std::unordered_map const& - getRanges(); }; // Like std::integral, but only 64-bit integral types. @@ -554,9 +593,15 @@ private: // changing the values inside the range. static thread_local std::reference_wrapper kRange; + class Guard; + void normalize(MantissaRange const& range); + // Guard has the fields that we need, as well as MantissaRange, so if we have a guard, use that + void + normalize(Guard const& guard); + /** Normalize Number components to an arbitrary range. * * min/maxMantissa are parameters because this function is used by both @@ -571,7 +616,7 @@ private: int& exponent, internalrep const& minMantissa, internalrep const& maxMantissa, - MantissaRange::CuspRoundingFix cuspRoundingFixEnabled); + MantissaRange::CuspRoundingFix cuspRoundingFix); template friend void @@ -581,7 +626,7 @@ private: int& exponent, MantissaRange::rep const& minMantissa, MantissaRange::rep const& maxMantissa, - MantissaRange::CuspRoundingFix cuspRoundingFixEnabled, + MantissaRange::CuspRoundingFix cuspRoundingFix, bool dropped); [[nodiscard]] bool @@ -599,8 +644,6 @@ private: // UB, and can vary across compilers. static internalrep externalToInternal(rep mantissa); - - class Guard; }; constexpr Number::Number(bool negative, internalrep mantissa, int exponent, Unchecked) noexcept @@ -862,21 +905,11 @@ squelch(Number const& x, Number const& limit) noexcept return x; } -inline std::string -to_string(MantissaRange::MantissaScale const& scale) -{ - switch (scale) - { - case MantissaRange::MantissaScale::Small: - return "small"; - case MantissaRange::MantissaScale::LargeLegacy: - return "largeLegacy"; - case MantissaRange::MantissaScale::Large: - return "large"; - default: - throw std::runtime_error("Bad scale"); - } -} +std::string +to_string(MantissaRange::MantissaScale const& scale); + +std::string +to_string(Number::RoundingMode const& round); class SaveNumberRoundMode { diff --git a/src/libxrpl/basics/Number.cpp b/src/libxrpl/basics/Number.cpp index 4d7a821040..0a1d558421 100644 --- a/src/libxrpl/basics/Number.cpp +++ b/src/libxrpl/basics/Number.cpp @@ -14,7 +14,6 @@ #include #include #include -#include #include #ifdef _MSC_VER @@ -31,74 +30,103 @@ namespace xrpl { thread_local Number::RoundingMode Number::mode = Number::RoundingMode::ToNearest; thread_local std::reference_wrapper Number::kRange = - MantissaRange::getMantissaRange(MantissaRange::MantissaScale::Large); + MantissaRange::Access::mantissaRange(MantissaRange::MantissaScale::Large330); -std::set const& -MantissaRange::getAllScales() +std::string +to_string(MantissaRange::MantissaScale const& scale) { - static std::set const kScales = { - MantissaRange::MantissaScale::Small, - MantissaRange::MantissaScale::LargeLegacy, - MantissaRange::MantissaScale::Large, - }; - return kScales; + switch (scale) + { + case MantissaRange::MantissaScale::Small: + return "Small"; + case MantissaRange::MantissaScale::LargeLegacy: + return "LargeLegacy"; + case MantissaRange::MantissaScale::Large320: + return "Large320"; + case MantissaRange::MantissaScale::Large330: + return "Large330"; + default: + throw std::runtime_error("Bad scale"); // LCOV_EXCL_LINE + } } -std::unordered_map const& -MantissaRange::getRanges() +std::string +to_string(Number::RoundingMode const& round) { - static auto const kMap = []() { - std::unordered_map map; - for (auto const scale : getAllScales()) - { - map.emplace(scale, scale); - } - - // Use these constexpr declarations to do static_asserts to verify the MantissaRanges are - // created correctly, but nothing else. - { - [[maybe_unused]] - constexpr static MantissaRange kRange{MantissaRange::MantissaScale::Small}; - static_assert(isPowerOfTen(kRange.min)); - static_assert(kRange.min == 1'000'000'000'000'000LL); - static_assert(kRange.max == 9'999'999'999'999'999LL); - static_assert(kRange.log == 15); - static_assert(kRange.min < Number::kMaxRep); - static_assert(kRange.max < Number::kMaxRep); - static_assert(kRange.cuspRoundingFixEnabled == CuspRoundingFix::Disabled); - } - { - [[maybe_unused]] - constexpr static MantissaRange kRange{MantissaRange::MantissaScale::LargeLegacy}; - static_assert(isPowerOfTen(kRange.min)); - static_assert(kRange.min == 1'000'000'000'000'000'000ULL); - static_assert(kRange.max == rep(9'999'999'999'999'999'999ULL)); - static_assert(kRange.log == 18); - static_assert(kRange.min < Number::kMaxRep); - static_assert(kRange.max > Number::kMaxRep); - static_assert(kRange.cuspRoundingFixEnabled == CuspRoundingFix::Disabled); - } - { - [[maybe_unused]] - constexpr static MantissaRange kRange{MantissaRange::MantissaScale::Large}; - static_assert(isPowerOfTen(kRange.min)); - static_assert(kRange.min == 1'000'000'000'000'000'000ULL); - static_assert(kRange.max == rep(9'999'999'999'999'999'999ULL)); - static_assert(kRange.log == 18); - static_assert(kRange.min < Number::kMaxRep); - static_assert(kRange.max > Number::kMaxRep); - static_assert(kRange.cuspRoundingFixEnabled == CuspRoundingFix::Enabled); - } - return map; - }(); - - return kMap; + switch (round) + { + case Number::RoundingMode::ToNearest: + return "ToNearest"; + case Number::RoundingMode::TowardsZero: + return "TowardsZero"; + case Number::RoundingMode::Downward: + return "Downward"; + case Number::RoundingMode::Upward: + return "Upward"; + default: + throw std::runtime_error("Bad rounding mode"); // LCOV_EXCL_LINE + } } -MantissaRange const& -MantissaRange::getMantissaRange(MantissaScale scale) +constexpr MantissaRange const& +MantissaRange::Access::mantissaRange(MantissaScale scale) { - return getRanges().at(scale); + static constexpr MantissaRange kSmall{MantissaScale::Small}; + static constexpr MantissaRange kLegacy{MantissaScale::LargeLegacy}; + static constexpr MantissaRange kLarge320{MantissaScale::Large320}; + static constexpr MantissaRange kLarge330{MantissaScale::Large330}; + + switch (scale) + { + case MantissaScale::Small: + return kSmall; + case MantissaScale::LargeLegacy: + return kLegacy; + case MantissaScale::Large320: + return kLarge320; + case MantissaScale::Large330: + return kLarge330; + } + throw std::logic_error("Unknown mantissa scale"); + + // static_asserts are checked at compile time, so it doesn't matter where in the function they + // are located. For readability of the main body, put them after it. + + // Small + static_assert(isPowerOfTen(kSmall.min)); + static_assert(kSmall.min == 1'000'000'000'000'000LL); + static_assert(kSmall.max == 9'999'999'999'999'999LL); + static_assert(kSmall.log == 15); + static_assert(kSmall.min < Number::kMaxRep); + static_assert(kSmall.max < Number::kMaxRep); + static_assert(kSmall.cuspRoundingFix == CuspRoundingFix::Disabled); + + // LargeLegacy + static_assert(isPowerOfTen(kLegacy.min)); + static_assert(kLegacy.min == 1'000'000'000'000'000'000ULL); + static_assert(kLegacy.max == rep(9'999'999'999'999'999'999ULL)); + static_assert(kLegacy.log == 18); + static_assert(kLegacy.min < Number::kMaxRep); + static_assert(kLegacy.max > Number::kMaxRep); + static_assert(kLegacy.cuspRoundingFix == CuspRoundingFix::Disabled); + + // Large320 + static_assert(isPowerOfTen(kLarge320.min)); + static_assert(kLarge320.min == 1'000'000'000'000'000'000ULL); + static_assert(kLarge320.max == rep(9'999'999'999'999'999'999ULL)); + static_assert(kLarge320.log == 18); + static_assert(kLarge320.min < Number::kMaxRep); + static_assert(kLarge320.max > Number::kMaxRep); + static_assert(kLarge320.cuspRoundingFix == CuspRoundingFix::Enabled320); + + // Large330 + static_assert(isPowerOfTen(kLarge330.min)); + static_assert(kLarge330.min == 1'000'000'000'000'000'000ULL); + static_assert(kLarge330.max == rep(9'999'999'999'999'999'999ULL)); + static_assert(kLarge330.log == 18); + static_assert(kLarge330.min < Number::kMaxRep); + static_assert(kLarge330.max > Number::kMaxRep); + static_assert(kLarge330.cuspRoundingFix == CuspRoundingFix::Enabled330); } Number::RoundingMode @@ -124,7 +152,7 @@ Number::setMantissaScale(MantissaRange::MantissaScale scale) { if (!MantissaRange::getAllScales().contains(scale)) logicError("Unknown mantissa scale"); - kRange = MantissaRange::getMantissaRange(scale); + kRange = MantissaRange::Access::mantissaRange(scale); } // Optimization equivalent to: @@ -155,15 +183,38 @@ divu10(uint128_t& u) return r; } -// Guard - -// The Guard class is used to temporarily add extra digits of -// precision to an operation. This enables the final result -// to be correctly rounded to the internal precision of Number. - template concept UnsignedMantissa = std::is_unsigned_v || std::is_same_v; +/** Guard + + The Guard class is used to temporarily add extra digits of + precision to an operation. This enables the final result + to be correctly rounded to the internal precision of Number. + + At its core, the Guard really only needs three pieces of information to determine how to round: + 1. The rounding mode + 2. The last digit dropped from the mantissa (i.e. the first digit after the decimal point). + (first byte of digits_) + 3. Whether any other non-zero digits were dropped from the mantissa. (remaining bytes of digits_ + and xbit_) + + Upward and Downward rounding modes round the unsigned mantissa toward or away from zero + depending on whether the sign is negative (sbit_). For positive values, Upward is away, and + Downward is toward. For negative values, that's reversed. For simplicity, I'm going to describe + the logic using "TowardZero" and "AwayFromZero". + + * TowardZero is the easiest rounding mode. It always rounds down. digits_ and xbit_ are + irrelevant. + * AwayFromZero is almost as simple. If both "digits_" and "xbit_" are zero (0), it rounds down. + Else it rounds up. + * ToNearest is only a little more complicated. If the last dropped digit is < 5, then round + down. If it is > 5, round up. If it is exactly 5, and there are _any_ other digits (the + remainder of "digits_" or "xbit_"), round up, else round to even. + + The current implementation stores 16 digits in "digits_" so that digits can be "pop"ped back + out if needed during subtraction (negative addition) operations. +*/ class Number::Guard { std::uint64_t digits_{0}; // 16 decimal guard digits @@ -171,7 +222,21 @@ class Number::Guard std::uint8_t sbit_ : 1 {0}; // the sign of the guard digits public: - explicit Guard() = default; + internalrep const minMantissa; + internalrep const maxMantissa; + MantissaRange::CuspRoundingFix const cuspRoundingFix; + + explicit Guard( + internalrep const& minMantissa, + internalrep const& maxMantissa, + MantissaRange::CuspRoundingFix cuspRoundingFix) + : minMantissa(minMantissa), maxMantissa(maxMantissa), cuspRoundingFix(cuspRoundingFix) + { + } + + explicit Guard(MantissaRange const& range) : Guard(range.min, range.max, range.cuspRoundingFix) + { + } // set & test the sign bit void @@ -194,6 +259,10 @@ public: unsigned pop() noexcept; + // if true, there are no digits in the guard, including dropped digits (xbit_) + [[nodiscard]] bool + empty() const noexcept; + /** Drop a digit from the mantissa, and increment the exponent, storing the dropped digit in * this Guard. * @@ -206,28 +275,37 @@ public: void doDropDigit(T& mantissa, int& exponent) noexcept; + enum class Round { + // The result is exact. No rounding is needed. Only used if cuspRoundingFix is Enabled330 or + // higher. + Exact = -2, + // Round down. Since we use integer math, that usually means no change is needed. + // Exceptions are for when the result is between kMaxRep and kMaxRepUp (round to kMaxRep), + // or after subtraction where _any_ remainder will modify the result. The latter is what + // distinguishes Exact from Down. + Down = -1, + // The result was exactly half-way between two integers. This will round to even. + Even = 0, + // Round up. Always adds 1 (or subtracts 1 in some cases if cuspRoundingFix is not + // Enabled) + Up = 1, + }; + // Indicate round direction: 1 is up, -1 is down, 0 is even // This enables the client to round towards nearest, and on // tie, round towards even. - [[nodiscard]] int + [[nodiscard]] Round round() const noexcept; // Modify the result to the correctly rounded value template void - doRoundUp( - bool& negative, - T& mantissa, - int& exponent, - internalrep const& minMantissa, - internalrep const& maxMantissa, - MantissaRange::CuspRoundingFix cuspRoundingFixEnabled, - std::string location); + doRoundUp(bool& negative, T& mantissa, int& exponent, std::string location); // Modify the result to the correctly rounded value template void - doRoundDown(bool& negative, T& mantissa, int& exponent, internalrep const& minMantissa); + doRoundDown(bool& negative, T& mantissa, int& exponent); // Modify the result to the correctly rounded value void @@ -239,7 +317,7 @@ private: template void - bringIntoRange(bool& negative, T& mantissa, int& exponent, internalrep const& minMantissa); + bringIntoRange(bool& negative, T& mantissa, int& exponent); }; inline void @@ -289,6 +367,12 @@ Number::Guard::pop() noexcept return d; } +inline bool +Number::Guard::empty() const noexcept +{ + return digits_ == 0 && !xbit_; +} + template void Number::Guard::doDropDigit(T& mantissa, int& exponent) noexcept @@ -314,50 +398,52 @@ Number::Guard::doDropDigit(uint128_t& mantissa, int& exponent) noexce // -1 if Guard is less than half // 0 if Guard is exactly half // 1 if Guard is greater than half -int +Number::Guard::Round Number::Guard::round() const noexcept { - auto mode = Number::getround(); + // Local "mode" shadows and has the same value as the static thread_local "Number::mode". + // This ensures the overhead of loading the thread_local is only incurred once. + auto const mode = Number::getround(); + + if (cuspRoundingFix >= MantissaRange::CuspRoundingFix::Enabled330 && empty()) + { + // No remainder + return Round::Exact; + } if (mode == RoundingMode::TowardsZero) - return -1; + return Round::Down; - if (mode == RoundingMode::Downward) + // Also Towards Zero + if ((mode == RoundingMode::Downward && !sbit_) || (mode == RoundingMode::Upward && sbit_)) { - if (sbit_) - { - if (digits_ > 0 || xbit_) - return 1; - } - return -1; + return Round::Down; } - if (mode == RoundingMode::Upward) + // Away from Zero. Since we checked sbit_ in the previous block, we don't need to check it + // again. + if (mode == RoundingMode::Downward || mode == RoundingMode::Upward) { - if (sbit_) - return -1; - if (digits_ > 0 || xbit_) - return 1; - return -1; + if (empty()) + return Round::Down; + return Round::Up; } + XRPL_ASSERT( + mode == RoundingMode::ToNearest, "xrpl::Number::Guard::Round : fallthrough to ToNearest"); // assume round to nearest if mode is not one of the predefined values if (digits_ > 0x5000'0000'0000'0000) - return 1; + return Round::Up; if (digits_ < 0x5000'0000'0000'0000) - return -1; + return Round::Down; if (xbit_) - return 1; - return 0; + return Round::Up; + return Round::Even; } template void -Number::Guard::bringIntoRange( - bool& negative, - T& mantissa, - int& exponent, - internalrep const& minMantissa) +Number::Guard::bringIntoRange(bool& negative, T& mantissa, int& exponent) { // Bring mantissa back into the minMantissa / maxMantissa range AFTER // rounding @@ -378,22 +464,15 @@ Number::Guard::bringIntoRange( template void -Number::Guard::doRoundUp( - bool& negative, - T& mantissa, - int& exponent, - internalrep const& minMantissa, - internalrep const& maxMantissa, - MantissaRange::CuspRoundingFix cuspRoundingFixEnabled, - std::string location) +Number::Guard::doRoundUp(bool& negative, T& mantissa, int& exponent, std::string location) { auto r = round(); - if (r == 1 || (r == 0 && (mantissa & 1) == 1)) + if (r == Round::Up || (r == Round::Even && (mantissa & 1) == 1)) { - auto const safeToIncrement = [&maxMantissa](auto const& mantissa) { + auto const safeToIncrement = [this](auto const& mantissa) { return mantissa < maxMantissa && mantissa < kMaxRep; }; - if (cuspRoundingFixEnabled == MantissaRange::CuspRoundingFix::Enabled) + if (cuspRoundingFix != MantissaRange::CuspRoundingFix::Disabled) { // Ensure mantissa after incrementing fits within both the // min/maxMantissa range and is a valid "rep". @@ -414,14 +493,7 @@ Number::Guard::doRoundUp( safeToIncrement(mantissa), "xrpl::Number::Guard::doRoundUp", "can't recurse more than once"); - doRoundUp( - negative, - mantissa, - exponent, - minMantissa, - maxMantissa, - cuspRoundingFixEnabled, - location); + doRoundUp(negative, mantissa, exponent, location); return; } } @@ -440,30 +512,43 @@ Number::Guard::doRoundUp( } } } - bringIntoRange(negative, mantissa, exponent, minMantissa); + bringIntoRange(negative, mantissa, exponent); if (exponent > kMaxExponent) Throw(std::string(location)); } template void -Number::Guard::doRoundDown( - bool& negative, - T& mantissa, - int& exponent, - internalrep const& minMantissa) +Number::Guard::doRoundDown(bool& negative, T& mantissa, int& exponent) { auto r = round(); - if (r == 1 || (r == 0 && (mantissa & 1) == 1)) + if (cuspRoundingFix >= MantissaRange::CuspRoundingFix::Enabled330) { - --mantissa; - if (mantissa < minMantissa) + // If there was any remainder, subtract 1 from the result. This is sufficient to get the + // best rounding. + XRPL_ASSERT( + r == Round::Exact || mantissa > maxMantissa, + "xrpl::Number::Guard::doRoundDown : mantissa is expected size"); + if (r != Round::Exact) { - mantissa *= 10; - --exponent; + --mantissa; } } - bringIntoRange(negative, mantissa, exponent, minMantissa); + else + { + // Need to preserve the incorrect behavior until the fix amendment can be retired, + // because otherwise would risk an unplanned ledger fork. + if (r == Round::Up || (r == Round::Even && (mantissa & 1) == 1)) + { + --mantissa; + if (mantissa < minMantissa) + { + mantissa *= 10; + --exponent; + } + } + } + bringIntoRange(negative, mantissa, exponent); } // Modify the result to the correctly rounded value @@ -471,7 +556,7 @@ void Number::Guard::doRound(rep& drops, std::string location) const { auto r = round(); - if (r == 1 || (r == 0 && (drops & 1) == 1)) + if (r == Round::Up || (r == Round::Even && (drops & 1) == 1)) { if (drops >= kMaxRep) { @@ -530,7 +615,7 @@ doNormalize( int& exponent, MantissaRange::rep const& minMantissa, MantissaRange::rep const& maxMantissa, - MantissaRange::CuspRoundingFix cuspRoundingFixEnabled, + MantissaRange::CuspRoundingFix cuspRoundingFix, bool dropped) { static constexpr auto kMinExponent = Number::kMinExponent; @@ -553,7 +638,7 @@ doNormalize( m *= 10; --exponent; } - Guard g; + Guard g(minMantissa, maxMantissa, cuspRoundingFix); if (negative) g.setNegative(); if (dropped) @@ -598,14 +683,7 @@ doNormalize( XRPL_ASSERT_PARTS(m <= kMaxRep, "xrpl::doNormalize", "intermediate mantissa fits in int64"); mantissa = m; - g.doRoundUp( - negative, - mantissa, - exponent, - minMantissa, - maxMantissa, - cuspRoundingFixEnabled, - "Number::normalize 2"); + g.doRoundUp(negative, mantissa, exponent, "Number::normalize 2"); XRPL_ASSERT_PARTS( mantissa >= minMantissa && mantissa <= maxMantissa, "xrpl::doNormalize", @@ -620,13 +698,12 @@ Number::normalize( int& exponent, internalrep const& minMantissa, internalrep const& maxMantissa, - MantissaRange::CuspRoundingFix cuspRoundingFixEnabled) + MantissaRange::CuspRoundingFix cuspRoundingFix) { // Not used by every compiler version, and thus not necessarily // counted by coverage build // LCOV_EXCL_START - doNormalize( - negative, mantissa, exponent, minMantissa, maxMantissa, cuspRoundingFixEnabled, false); + doNormalize(negative, mantissa, exponent, minMantissa, maxMantissa, cuspRoundingFix, false); // LCOV_EXCL_STOP } @@ -638,13 +715,12 @@ Number::normalize( int& exponent, internalrep const& minMantissa, internalrep const& maxMantissa, - MantissaRange::CuspRoundingFix cuspRoundingFixEnabled) + MantissaRange::CuspRoundingFix cuspRoundingFix) { // Not used by every compiler version, and thus not necessarily // counted by coverage build // LCOV_EXCL_START - doNormalize( - negative, mantissa, exponent, minMantissa, maxMantissa, cuspRoundingFixEnabled, false); + doNormalize(negative, mantissa, exponent, minMantissa, maxMantissa, cuspRoundingFix, false); // LCOV_EXCL_STOP } @@ -656,16 +732,27 @@ Number::normalize( int& exponent, internalrep const& minMantissa, internalrep const& maxMantissa, - MantissaRange::CuspRoundingFix cuspRoundingFixEnabled) + MantissaRange::CuspRoundingFix cuspRoundingFix) { - doNormalize( - negative, mantissa, exponent, minMantissa, maxMantissa, cuspRoundingFixEnabled, false); + doNormalize(negative, mantissa, exponent, minMantissa, maxMantissa, cuspRoundingFix, false); } void Number::normalize(MantissaRange const& range) { - normalize(negative_, mantissa_, exponent_, range.min, range.max, range.cuspRoundingFixEnabled); + normalize(negative_, mantissa_, exponent_, range.min, range.max, range.cuspRoundingFix); +} + +void +Number::normalize(Guard const& guard) +{ + normalize( + negative_, + mantissa_, + exponent_, + guard.minMantissa, + guard.maxMantissa, + guard.cuspRoundingFix); } // Copy the number, but set a new exponent. Because the mantissa doesn't change, @@ -719,46 +806,102 @@ Number::operator+=(Number const& y) bool const yn = y.negative_; uint128_t ym = y.mantissa_; auto ye = y.exponent_; - Guard g; + Guard g(kRange); + + auto const& minMantissa = g.minMantissa; + auto const& maxMantissa = g.maxMantissa; + auto const cuspRoundingFix = g.cuspRoundingFix; + + // Bring the exponents of both values into agreement, so the mantissas are on the same scale + // and can be added directly together. + + auto const upperLimit = static_cast(g.minMantissa) * 1000; + // For the "adjust" lambda + // expandM / expandE: The values for which the mantissa will be expanded, and the exponent + // decreased to match. Mantissa won't be expanded beyond upperLimit. + // (37e8 == 37000e5 == 37000000e2) + // shrinkM / shrinkE: The values for which the mantissa will be shrunk, and exponent increased + // to match, if necessary. + auto const adjust = [&g, &upperLimit]( + uint128_t& expandM, int& expandE, uint128_t& shrinkM, int& shrinkE) { + // Adjust up and down until the exponents match + if (g.cuspRoundingFix == MantissaRange::CuspRoundingFix::Enabled330) + { + // For Enabled330, there are three steps. + // 1. First, shrink the mantissa of shrinkM/shrinkE while shrinkM ends in 0. + while (shrinkE < expandE && shrinkM % 10 == 0) + { + g.doDropDigit(shrinkM, shrinkE); + } + + // 2. Then expand the mantissa of expandM/expandE, with a limit for expandM a few orders + // of magnitude above the MantissaRange. This will leave a few extra digits for rounding + // later, but nothing excessive. + while (shrinkE < expandE && expandE > kMinExponent && expandM < upperLimit) + { + expandM *= 10; + --expandE; + } + } + + // 3. Finally, shrink the mantissa of shrinkM/shrinkE until the exponents match. Any removed + // digits will be put into the Guard. This is the only step for non-Enabled330 modes. + while (shrinkE < expandE) + { + g.doDropDigit(shrinkM, shrinkE); + } + }; + + // Shrink the mantissa and raise the exponent of the value with the lower exponent. Store any + // dropped digits in the Guard. if (xe < ye) { if (xn) g.setNegative(); - do - { - g.doDropDigit(xm, xe); - } while (xe < ye); + + adjust(ym, ye, xm, xe); } else if (xe > ye) { if (yn) g.setNegative(); - do - { - g.doDropDigit(ym, ye); - } while (xe > ye); - } - auto const& range = kRange.get(); - auto const& minMantissa = range.min; - auto const& maxMantissa = range.max; - auto const cuspRoundingFixEnabled = range.cuspRoundingFixEnabled; + adjust(xm, xe, ym, ye); + } + else if (g.cuspRoundingFix == MantissaRange::CuspRoundingFix::Enabled330) + { + // Both values have the same exponent. + // Set the sign of the Guard based on the sign of the Number with the smallest + // unsigned _mantissa_ + if ((xm < ym && xn) || (ym < xm && yn)) + g.setNegative(); + } if (xn == yn) { xm += ym; - if (xm > maxMantissa || xm > kMaxRep) + + if (g.cuspRoundingFix >= MantissaRange::CuspRoundingFix::Enabled330) { - g.doDropDigit(xm, xe); + // Don't do any adjustments for Enabled330. Normalize will take care of it + // Because of "adjust", the only way there can be data in the Guard is if we first grew + // the mantissa past the maxMantissa. Since we added here, it can only get bigger. + // If xm > maxMantissa, then doNormalize has all the data it needs from the last 3-4 + // digits, plus the "dropped" flag that will be passed in. + // If not, then the mantissa will only need to be padded out with 0s and won't need to + // round. + XRPL_ASSERT( + xm > maxMantissa || g.empty(), + "xrpl::Number::operator+ : rounding state expected after add"); + } + else + { + if (xm > maxMantissa || xm > kMaxRep) + { + g.doDropDigit(xm, xe); + } + g.doRoundUp(xn, xm, xe, "Number::addition overflow"); } - g.doRoundUp( - xn, - xm, - xe, - minMantissa, - maxMantissa, - cuspRoundingFixEnabled, - "Number::addition overflow"); } else { @@ -772,19 +915,67 @@ Number::operator+=(Number const& y) xe = ye; xn = yn; } - while (xm < minMantissa && xm * 10 <= kMaxRep) + if (cuspRoundingFix >= MantissaRange::CuspRoundingFix::Enabled330) { - xm *= 10; - xm -= g.pop(); - --xe; + // Because we subtracted, xm can have any number of digits from 1 up to + // upperLimit * 10, and g can be in any state. (Note that xm can't be zero, because that + // special case was tested earlier.) + + // Grow xm/xe and pull digits out of the Guard until xm reaches upperLimit, but stop if + // the Guard empties out, because no rounding will be necessary. This will ensure that + // normalize will have enough information to make an accurate rounding decision. + // (Normalize will pad a small mantissa back into range.) Note that if any digits were + // lost (xbit_), the Guard will never be empty, so xm will grow larger than upperLimit. + while (xm < upperLimit && !g.empty()) + { + xm *= 10; + xm -= g.pop(); + --xe; + } + XRPL_ASSERT( + xm > maxMantissa || g.empty(), + "xrpl::Number::operator+ : rounding state expected after subtract"); } - g.doRoundDown(xn, xm, xe, minMantissa); + else + { + // Grow xm/xe and pull digits out of the Guard until it's back in the + // minMantissa/maxMantissa range. + while (xm < minMantissa && xm * 10 <= kMaxRep) + { + xm *= 10; + xm -= g.pop(); + --xe; + } + } + // Rounding down can result in decrementing xm, based on whether there is any data left in + // the Guard (depending on cuspRoundingFix). Note that if that happens, then the Guard is + // not empty. For Enabled330, that will also result in the "dropped" flag being passed to + // doNormalize, which may result in the mantissa being incremented again. It doesn't matter + // what the dropped digits are, only that they exist. This is because subtracting one + // "overcorrects", so we know there are still trailing digits to be accounted for in the + // rounding. + // + // This works because + // 1. The rounding up will be done _after_ the mantissa is brought into range. It may not + // be in range right now, and + // 2. The "dropped" flag is only ever used as a tie-breaker, specifically when rounding + // away from zero, and the dropped digits are 0, or when rounding to nearest, and + // the dropped digits represent exactly 0.5. + g.doRoundDown(xn, xm, xe); } + doNormalize( + xn, + xm, + xe, + minMantissa, + maxMantissa, + cuspRoundingFix, + cuspRoundingFix == MantissaRange::CuspRoundingFix::Enabled330 && !g.empty()); negative_ = xn; mantissa_ = static_cast(xm); exponent_ = xe; - normalize(range); + XRPL_ASSERT(isnormal(), "xrpl::Number::operator+= : result is normal"); return *this; } @@ -818,14 +1009,11 @@ Number::operator*=(Number const& y) auto ze = xe + ye; auto zs = xs * ys; bool zn = (zs == -1); - Guard g; + Guard g(kRange); if (zn) g.setNegative(); - auto const& range = kRange.get(); - auto const& minMantissa = range.min; - auto const& maxMantissa = range.max; - auto const cuspRoundingFixEnabled = range.cuspRoundingFixEnabled; + auto const& maxMantissa = g.maxMantissa; while (zm > maxMantissa || zm > kMaxRep) { @@ -834,19 +1022,12 @@ Number::operator*=(Number const& y) xm = static_cast(zm); xe = ze; - g.doRoundUp( - zn, - xm, - xe, - minMantissa, - maxMantissa, - cuspRoundingFixEnabled, - "Number::multiplication overflow : exponent is " + std::to_string(xe)); + g.doRoundUp(zn, xm, xe, "Number::multiplication overflow : exponent is " + std::to_string(xe)); negative_ = zn; mantissa_ = xm; exponent_ = xe; - normalize(range); + normalize(g); return *this; } @@ -882,7 +1063,7 @@ Number::operator/=(Number const& y) auto const& range = kRange.get(); auto const& minMantissa = range.min; auto const& maxMantissa = range.max; - auto const cuspRoundingFixEnabled = range.cuspRoundingFixEnabled; + auto const cuspRoundingFix = range.cuspRoundingFix; // Division operates on two large integers (16-digit for small // mantissas, 19-digit for large) using integer math. If the values @@ -930,7 +1111,7 @@ Number::operator/=(Number const& y) // This is equivalent to if we had used an initial factor of 10^22, // a couple digits more than we actually need. // - // Stage 3: If there is still a remainder, and the CuspRoundingFix + // Stage 3: If there is still a remainder, and the cuspRoundingFix // is enabled, pass a flag indicating such to doNormalize. The Guard // in doNormalize will treat that flag as if non-zero digits had // been dropped from the mantissa when shrinking it into range. @@ -1014,14 +1195,14 @@ Number::operator/=(Number const& y) // rounding fix is enabled, flag if there is still // a remainder from stage 2. bool const useTrailingRemainder = - cuspRoundingFixEnabled == MantissaRange::CuspRoundingFix::Enabled; + cuspRoundingFix != MantissaRange::CuspRoundingFix::Disabled; if (useTrailingRemainder) { dropped = partialNumerator % dm != 0; } } } - doNormalize(zp, zm, ze, minMantissa, maxMantissa, cuspRoundingFixEnabled, dropped); + doNormalize(zp, zm, ze, minMantissa, maxMantissa, cuspRoundingFix, dropped); negative_ = zp; mantissa_ = static_cast(zm); exponent_ = ze; @@ -1035,7 +1216,7 @@ operator rep() const { rep drops = mantissa(); int offset = exponent(); - Guard g; + Guard g(kRange); if (drops != 0) { if (negative_) diff --git a/src/libxrpl/protocol/Rules.cpp b/src/libxrpl/protocol/Rules.cpp index e0968ea868..d71bb77f66 100644 --- a/src/libxrpl/protocol/Rules.cpp +++ b/src/libxrpl/protocol/Rules.cpp @@ -39,22 +39,30 @@ setCurrentTransactionRules(std::optional r) // Push the appropriate setting, instead of having the class pull every time // the value is needed. That could get expensive fast. - // If any new conditions with new amendments are added, those amendments must also be added to - // useRulesGuards. - bool const enableVaultNumbers = - !r || (r->enabled(featureSingleAssetVault) || r->enabled(featureLendingProtocol)); - bool const enableCuspRoundingFix = !r || r->enabled(fixCleanup3_2_0); - XRPL_ASSERT( - !r || useRulesGuards(*r) == (enableCuspRoundingFix || enableVaultNumbers), - "setCurrentTransactionRules : rule decisions match"); - // Declare the range this way to keep clang-tidy from complaining - auto const range = [enableCuspRoundingFix, enableVaultNumbers]() { - if (enableVaultNumbers) + auto const range = [&r]() { + // If any new conditions with new amendments are added to "enableLargeNumbers", those + // amendments must also be added to useRulesGuards. + bool const enableLargeNumbers = + !r || (r->enabled(featureSingleAssetVault) || r->enabled(featureLendingProtocol)); + // If enableLargeNumbers is true, then useRulesGuard must also return true. + // However, the reverse is not true. Other amendments can cause the rules guard to be used, + // even though large numbers are _not_ used. + XRPL_ASSERT( + !r || !enableLargeNumbers || useRulesGuards(*r), + "setCurrentTransactionRules : rule decisions match"); + + if (enableLargeNumbers) { - if (enableCuspRoundingFix) + static_assert( + MantissaRange::MantissaScale::Large == MantissaRange::MantissaScale::Large330); + if (!r || r->enabled(fixCleanup3_3_0)) { - return MantissaRange::MantissaScale::Large; + return MantissaRange::MantissaScale::Large330; + } + if (r->enabled(fixCleanup3_2_0)) + { + return MantissaRange::MantissaScale::Large320; } return MantissaRange::MantissaScale::LargeLegacy; } @@ -69,14 +77,14 @@ bool useRulesGuards(Rules const& rules) { // The list of amendments used here - to decide whether to create a RulesGuard - must be a - // superset of the list used to figure out which mantissa scale to use in - // setCurrentTransactionRules. Additional amendments can be added if desired. + // superset of the list used to determine "enableLargeNumbers" in setCurrentTransactionRules. + // Additional amendments can be added if desired. // // As soon as any one of these amendments is retired, this whole function can be removed, along // with createGuards, and any other callers, and the first set of guards can be created directly // at the call site, without using optional. - return rules.enabled(fixCleanup3_2_0) || rules.enabled(featureSingleAssetVault) || - rules.enabled(featureLendingProtocol); + return rules.enabled(featureSingleAssetVault) || rules.enabled(featureLendingProtocol) || + rules.enabled(fixCleanup3_2_0) || rules.enabled(fixCleanup3_3_0); } void @@ -87,7 +95,8 @@ createGuards( { if (useRulesGuards(rules)) { - // raii classes for the current ledger rules. + // raii classes for the current ledger rules. If the rules are set, the MantissaRange will + // be updated, too. rulesGuard.emplace(rules); } else diff --git a/src/libxrpl/protocol/STNumber.cpp b/src/libxrpl/protocol/STNumber.cpp index 79f7655869..bd7f67649b 100644 --- a/src/libxrpl/protocol/STNumber.cpp +++ b/src/libxrpl/protocol/STNumber.cpp @@ -88,7 +88,6 @@ STNumber::add(Serializer& s) const } else { -#if !NDEBUG // There are circumstances where an already-rounded Number is // serialized without being touched by a transactor, and thus // without an asset. We can't know if it's rounded, because it could @@ -96,11 +95,9 @@ STNumber::add(Serializer& s) const // Json. Regardless, the only time we should be serializing an // STNumber is when the scale is large. XRPL_ASSERT_PARTS( - Number::getMantissaScale() == MantissaRange::MantissaScale::LargeLegacy || - Number::getMantissaScale() == MantissaRange::MantissaScale::Large, + Number::getMantissaScale() != MantissaRange::MantissaScale::Small, "xrpl::STNumber::add", "STNumber only used with large mantissa scale"); -#endif } } diff --git a/src/test/app/Invariants_test.cpp b/src/test/app/Invariants_test.cpp index b70d099ba4..8a1f24e30b 100644 --- a/src/test/app/Invariants_test.cpp +++ b/src/test/app/Invariants_test.cpp @@ -5030,11 +5030,10 @@ class Invariants_test : public beast::unit_test::Suite std::vector values; }; - for (auto const mantissaScale : { - MantissaRange::MantissaScale::LargeLegacy, - MantissaRange::MantissaScale::Large, - }) + for (auto const mantissaScale : MantissaRange::getAllScales()) { + if (mantissaScale == MantissaRange::MantissaScale::Small) + continue; NumberMantissaScaleGuard const g{mantissaScale}; auto makeDelta = [&vaultAsset](Number const& n) -> ValidVault::DeltaInfo { diff --git a/src/tests/libxrpl/basics/Number.cpp b/src/tests/libxrpl/basics/Number.cpp index e8c4e4209f..70d6be2da5 100644 --- a/src/tests/libxrpl/basics/Number.cpp +++ b/src/tests/libxrpl/basics/Number.cpp @@ -48,6 +48,22 @@ fmt(BigInt const& value) return out; } +BigInt +toBigInt(Number const& n) +{ + BigInt v = n.mantissa(); + auto e = n.exponent(); + + for (; e > 0; --e) + v *= 10; + for (; e < 0; ++e) + { + EXPECT_EQ(v % 10, 0); + v /= 10; + } + return v; +} + template static T pow10(int n) @@ -177,28 +193,35 @@ TEST(NumberTest, add) auto const scale = Number::getMantissaScale(); - using Case = std::tuple; - auto const cSmall = std::to_array( - {{Number{1'000'000'000'000'000, -15}, - Number{6'555'555'555'555'555, -29}, - Number{1'000'000'000'000'066, -15}}, - {Number{-1'000'000'000'000'000, -15}, - Number{-6'555'555'555'555'555, -29}, - Number{-1'000'000'000'000'066, -15}}, - {Number{-1'000'000'000'000'000, -15}, - Number{6'555'555'555'555'555, -29}, - Number{-9'999'999'999'999'344, -16}}, - {Number{-6'555'555'555'555'555, -29}, - Number{1'000'000'000'000'000, -15}, - Number{9'999'999'999'999'344, -16}}, - {Number{}, Number{5}, Number{5}}, - {Number{5}, Number{}, Number{5}}, - {Number{5'555'555'555'555'555, -32768}, - Number{-5'555'555'555'555'554, -32768}, - Number{0}}, - {Number{-9'999'999'999'999'999, -31}, - Number{1'000'000'000'000'000, -15}, - Number{9'999'999'999'999'990, -16}}}); + using Case = std::tuple; + auto const cSmall = std::to_array({ + {Number{1'000'000'000'000'000, -15}, + Number{6'555'555'555'555'555, -29}, + Number{1'000'000'000'000'066, -15}, + __LINE__}, + {Number{-1'000'000'000'000'000, -15}, + Number{-6'555'555'555'555'555, -29}, + Number{-1'000'000'000'000'066, -15}, + __LINE__}, + {Number{-1'000'000'000'000'000, -15}, + Number{6'555'555'555'555'555, -29}, + Number{-9'999'999'999'999'344, -16}, + __LINE__}, + {Number{-6'555'555'555'555'555, -29}, + Number{1'000'000'000'000'000, -15}, + Number{9'999'999'999'999'344, -16}, + __LINE__}, + {Number{}, Number{5}, Number{5}, __LINE__}, + {Number{5}, Number{}, Number{5}, __LINE__}, + {Number{5'555'555'555'555'555, -32768}, + Number{-5'555'555'555'555'554, -32768}, + Number{0}, + __LINE__}, + {Number{-9'999'999'999'999'999, -31}, + Number{1'000'000'000'000'000, -15}, + Number{9'999'999'999'999'990, -16}, + __LINE__}, + }); auto const cLarge = std::to_array( // Note that items with extremely large mantissas need to be // calculated, because otherwise they overflow uint64. Items from C @@ -206,45 +229,57 @@ TEST(NumberTest, add) { {Number{1'000'000'000'000'000, -15}, Number{6'555'555'555'555'555, -29}, - Number{1'000'000'000'000'065'556, -18}}, + Number{1'000'000'000'000'065'556, -18}, + __LINE__}, {Number{-1'000'000'000'000'000, -15}, Number{-6'555'555'555'555'555, -29}, - Number{-1'000'000'000'000'065'556, -18}}, + Number{-1'000'000'000'000'065'556, -18}, + __LINE__}, {Number{-1'000'000'000'000'000, -15}, Number{6'555'555'555'555'555, -29}, - Number{true, 9'999'999'999'999'344'444ULL, -19, Number::Normalized{}}}, + Number{true, 9'999'999'999'999'344'444ULL, -19, Number::Normalized{}}, + __LINE__}, {Number{-6'555'555'555'555'555, -29}, Number{1'000'000'000'000'000, -15}, - Number{false, 9'999'999'999'999'344'444ULL, -19, Number::Normalized{}}}, - {Number{}, Number{5}, Number{5}}, - {Number{5}, Number{}, Number{5}}, + Number{false, 9'999'999'999'999'344'444ULL, -19, Number::Normalized{}}, + __LINE__}, + {Number{}, Number{5}, Number{5}, __LINE__}, + {Number{5}, Number{}, Number{5}, __LINE__}, {Number{5'555'555'555'555'555'000, -32768}, Number{-5'555'555'555'555'554'000, -32768}, - Number{0}}, + Number{0}, + __LINE__}, {Number{-9'999'999'999'999'999, -31}, Number{1'000'000'000'000'000, -15}, - Number{9'999'999'999'999'990, -16}}, + Number{9'999'999'999'999'990, -16}, + __LINE__}, // Items from cSmall expanded for the larger mantissa {Number{1'000'000'000'000'000'000, -18}, Number{6'555'555'555'555'555'555, -35}, - Number{1'000'000'000'000'000'066, -18}}, + Number{1'000'000'000'000'000'066, -18}, + __LINE__}, {Number{-1'000'000'000'000'000'000, -18}, Number{-6'555'555'555'555'555'555, -35}, - Number{-1'000'000'000'000'000'066, -18}}, + Number{-1'000'000'000'000'000'066, -18}, + __LINE__}, {Number{-1'000'000'000'000'000'000, -18}, Number{6'555'555'555'555'555'555, -35}, - Number{true, 9'999'999'999'999'999'344ULL, -19, Number::Normalized{}}}, + Number{true, 9'999'999'999'999'999'344ULL, -19, Number::Normalized{}}, + __LINE__}, {Number{-6'555'555'555'555'555'555, -35}, Number{1'000'000'000'000'000'000, -18}, - Number{false, 9'999'999'999'999'999'344ULL, -19, Number::Normalized{}}}, - {Number{}, Number{5}, Number{5}}, + Number{false, 9'999'999'999'999'999'344ULL, -19, Number::Normalized{}}, + __LINE__}, + {Number{}, Number{5}, Number{5}, __LINE__}, {Number{5'555'555'555'555'555'555, -32768}, Number{-5'555'555'555'555'555'554, -32768}, - Number{0}}, + Number{0}, + __LINE__}, {Number{true, 9'999'999'999'999'999'999ULL, -37, Number::Normalized{}}, Number{1'000'000'000'000'000'000, -18}, - Number{false, 9'999'999'999'999'999'990ULL, -19, Number::Normalized{}}}, - {Number{Number::kMaxRep - 1}, Number{1, 0}, Number{Number::kMaxRep}}, + Number{false, 9'999'999'999'999'999'990ULL, -19, Number::Normalized{}}, + __LINE__}, + {Number{Number::kMaxRep - 1}, Number{1, 0}, Number{Number::kMaxRep}, __LINE__}, // Test extremes { // Each Number operand rounds up, so the actual mantissa is @@ -252,6 +287,7 @@ TEST(NumberTest, add) Number{false, 9'999'999'999'999'999'999ULL, 0, Number::Normalized{}}, Number{false, 9'999'999'999'999'999'999ULL, 0, Number::Normalized{}}, Number{2, 19}, + __LINE__, }, { // Does not round. Mantissas are going to be > maxRep, so if @@ -262,21 +298,25 @@ TEST(NumberTest, add) Number{false, 9'999'999'999'999'999'990ULL, 0, Number::Normalized{}}, Number{false, 9'999'999'999'999'999'990ULL, 0, Number::Normalized{}}, Number{false, 1'999'999'999'999'999'998ULL, 1, Number::Normalized{}}, + __LINE__, }, }); auto const cLargeLegacy = std::to_array({ - {Number{Number::kMaxRep}, Number{6, -1}, Number{Number::kMaxRep / 10, 1}}, + {Number{Number::kMaxRep}, Number{6, -1}, Number{Number::kMaxRep / 10, 1}, __LINE__}, }); auto const cLargeCorrected = std::to_array({ - {Number{Number::kMaxRep}, Number{6, -1}, Number{(Number::kMaxRep / 10) + 1, 1}}, + {Number{Number::kMaxRep}, + Number{6, -1}, + Number{(Number::kMaxRep / 10) + 1, 1}, + __LINE__}, }); auto test = [](auto const& c) { - for (auto const& [x, y, z] : c) + for (auto const& [x, y, z, line] : c) { auto const result = x + y; std::stringstream ss; ss << x << " + " << y << " = " << result << ". Expected: " << z; - EXPECT_EQ(result, z) << ss.str(); + EXPECT_EQ(result, z) << ss.str() << " Line: " << line; } }; if (scale == MantissaRange::MantissaScale::Small) @@ -319,21 +359,28 @@ TEST(NumberTest, sub) auto const scale = Number::getMantissaScale(); - using Case = std::tuple; + using Case = std::tuple; auto const cSmall = std::to_array( {{Number{1'000'000'000'000'000, -15}, Number{6'555'555'555'555'555, -29}, - Number{9'999'999'999'999'344, -16}}, + Number{9'999'999'999'999'344, -16}, + __LINE__}, {Number{6'555'555'555'555'555, -29}, Number{1'000'000'000'000'000, -15}, - Number{-9'999'999'999'999'344, -16}}, - {Number{1'000'000'000'000'000, -15}, Number{1'000'000'000'000'000, -15}, Number{0}}, + Number{-9'999'999'999'999'344, -16}, + __LINE__}, + {Number{1'000'000'000'000'000, -15}, + Number{1'000'000'000'000'000, -15}, + Number{0}, + __LINE__}, {Number{1'000'000'000'000'000, -15}, Number{1'000'000'000'000'001, -15}, - Number{-1'000'000'000'000'000, -30}}, + Number{-1'000'000'000'000'000, -30}, + __LINE__}, {Number{1'000'000'000'000'001, -15}, Number{1'000'000'000'000'000, -15}, - Number{1'000'000'000'000'000, -30}}}); + Number{1'000'000'000'000'000, -30}, + __LINE__}}); auto const cLarge = std::to_array( // Note that items with extremely large mantissas need to be // calculated, because otherwise they overflow uint64. Items from C @@ -341,49 +388,63 @@ TEST(NumberTest, sub) { {Number{1'000'000'000'000'000, -15}, Number{6'555'555'555'555'555, -29}, - Number{false, 9'999'999'999'999'344'444ULL, -19, Number::Normalized{}}}, + Number{false, 9'999'999'999'999'344'444ULL, -19, Number::Normalized{}}, + __LINE__}, {Number{6'555'555'555'555'555, -29}, Number{1'000'000'000'000'000, -15}, - Number{true, 9'999'999'999'999'344'444ULL, -19, Number::Normalized{}}}, - {Number{1'000'000'000'000'000, -15}, Number{1'000'000'000'000'000, -15}, Number{0}}, + Number{true, 9'999'999'999'999'344'444ULL, -19, Number::Normalized{}}, + __LINE__}, + {Number{1'000'000'000'000'000, -15}, + Number{1'000'000'000'000'000, -15}, + Number{0}, + __LINE__}, {Number{1'000'000'000'000'000, -15}, Number{1'000'000'000'000'001, -15}, - Number{-1'000'000'000'000'000, -30}}, + Number{-1'000'000'000'000'000, -30}, + __LINE__}, {Number{1'000'000'000'000'001, -15}, Number{1'000'000'000'000'000, -15}, - Number{1'000'000'000'000'000, -30}}, + Number{1'000'000'000'000'000, -30}, + __LINE__}, // Items from cSmall expanded for the larger mantissa {Number{1'000'000'000'000'000'000, -18}, Number{6'555'555'555'555'555'555, -32}, - Number{false, 9'999'999'999'999'344'444ULL, -19, Number::Normalized{}}}, + Number{false, 9'999'999'999'999'344'444ULL, -19, Number::Normalized{}}, + __LINE__}, {Number{6'555'555'555'555'555'555, -32}, Number{1'000'000'000'000'000'000, -18}, - Number{true, 9'999'999'999'999'344'444ULL, -19, Number::Normalized{}}}, + Number{true, 9'999'999'999'999'344'444ULL, -19, Number::Normalized{}}, + __LINE__}, {Number{1'000'000'000'000'000'000, -18}, Number{1'000'000'000'000'000'000, -18}, - Number{0}}, + Number{0}, + __LINE__}, {Number{1'000'000'000'000'000'000, -18}, Number{1'000'000'000'000'000'001, -18}, - Number{-1'000'000'000'000'000'000, -36}}, + Number{-1'000'000'000'000'000'000, -36}, + __LINE__}, {Number{1'000'000'000'000'000'001, -18}, Number{1'000'000'000'000'000'000, -18}, - Number{1'000'000'000'000'000'000, -36}}, - {Number{Number::kMaxRep}, Number{6, -1}, Number{Number::kMaxRep - 1}}, + Number{1'000'000'000'000'000'000, -36}, + __LINE__}, + {Number{Number::kMaxRep}, Number{6, -1}, Number{Number::kMaxRep - 1}, __LINE__}, {Number{false, Number::kMaxRep + 1, 0, Number::Normalized{}}, Number{1, 0}, - Number{(Number::kMaxRep / 10) + 1, 1}}, + Number{(Number::kMaxRep / 10) + 1, 1}, + __LINE__}, {Number{false, Number::kMaxRep + 1, 0, Number::Normalized{}}, Number{3, 0}, - Number{Number::kMaxRep}}, - {power(2, 63), Number{3, 0}, Number{Number::kMaxRep}}, + Number{Number::kMaxRep}, + __LINE__}, + {power(2, 63), Number{3, 0}, Number{Number::kMaxRep}, __LINE__}, }); auto test = [](auto const& c) { - for (auto const& [x, y, z] : c) + for (auto const& [x, y, z, line] : c) { auto const result = x - y; std::stringstream ss; ss << x << " - " << y << " = " << result << ". Expected: " << z; - EXPECT_EQ(result, z) << ss.str(); + EXPECT_EQ(result, z) << ss.str() << " Line: " << line; } }; if (scale == MantissaRange::MantissaScale::Small) @@ -1365,8 +1426,7 @@ TEST(NumberTest, to_string) "9223372036854775e3"); } break; - case MantissaRange::MantissaScale::LargeLegacy: - case MantissaRange::MantissaScale::Large: + default: // Test the edges // ((exponent < -(28)) || (exponent > -(8))))) test(Number::min(), "1e-32750"); @@ -1405,8 +1465,6 @@ TEST(NumberTest, to_string) -(Number{std::numeric_limits::max(), 0} + 1), "-9223372036854775810"); break; - default: - EXPECT_TRUE(false); } } } @@ -1787,9 +1845,7 @@ TEST(NumberTest, upward_rounding_produces_value_not_below_exact_at_k_max_rep_cus BigInt const exactProduct = BigInt(kAValue) * BigInt(kBValue); // What Number actually stored. - BigInt storedValue = BigInt(product.mantissa()); - for (int i = 0; i < product.exponent(); ++i) - storedValue *= 10; + BigInt const storedValue = toBigInt(product); BigInt const signedDifference = storedValue - exactProduct; @@ -1809,7 +1865,8 @@ TEST(NumberTest, upward_rounding_produces_value_not_below_exact_at_k_max_rep_cus switch (scale) { - case MantissaRange::MantissaScale::Large: + case MantissaRange::MantissaScale::Large320: + case MantissaRange::MantissaScale::Large330: EXPECT_TRUE(signedDifference >= 0) << message(); EXPECT_TRUE(signedDifference < pow10(product.exponent())) << message(); EXPECT_EQ(product.mantissa(), (std::numeric_limits::max() / 10) + 1); @@ -1897,7 +1954,8 @@ TEST(NumberTest, upward_division_returns_value_not_below_exact_on_large_scale) // Upward invariant: stored >= exact. Bug: stored < exact. switch (scale) { - case MantissaRange::MantissaScale::Large: + case MantissaRange::MantissaScale::Large320: + case MantissaRange::MantissaScale::Large330: EXPECT_TRUE(stored >= exact) << message(); EXPECT_TRUE(diff < pow10(quotient.exponent())) << message(); break; @@ -1951,10 +2009,11 @@ TEST(NumberTest, downward_division_returns_value_not_above_exact_on_large_scale) return os.str(); }; - // Downward invariant: stored <= exact. Bug: stored > exact. + // invariant: stored <= exact. Bug: stored > exact. switch (scale) { - case MantissaRange::MantissaScale::Large: + case MantissaRange::MantissaScale::Large320: + case MantissaRange::MantissaScale::Large330: EXPECT_TRUE(stored <= exact) << message(); EXPECT_TRUE(diff > -pow10(quotient.exponent())) << message(); break; @@ -2018,10 +2077,11 @@ TEST(NumberTest, to_nearest_division_uses_dropped_digits_on_large_scale) return os.str(); }; - // ToNearest should account for dropped digits beyond the visible "5". + // invariant: stored >= exact. Bug: stored < exact. switch (scale) { - case MantissaRange::MantissaScale::Large: + case MantissaRange::MantissaScale::Large320: + case MantissaRange::MantissaScale::Large330: EXPECT_TRUE(stored >= exact) << message(); EXPECT_TRUE(diff < pow10(quotient.exponent())) << message(); break; @@ -2039,4 +2099,365 @@ TEST(NumberTest, to_nearest_division_uses_dropped_digits_on_large_scale) } } +TEST(NumberTest, subtraction_rounding) +{ + for (auto const mantissaScale : MantissaRange::getAllScales()) + { + NumberMantissaScaleGuard const mg{mantissaScale}; + NumberRoundModeGuard const rg{Number::RoundingMode::ToNearest}; + + auto const scale = Number::getMantissaScale(); + + auto const exp = Number::mantissaLog(); + // SubCase is + // * offset: offset from exp + // * extraB: whether to include 1e"exp" in "b" + // * aString: expected string value for "a" + // * bString: expected string value for "b" + // There aren't too many valid combinations for test cases here. If extraB is true, + // offset can really only be 2, because any larger and the mantissa can't be represented + // without loss. Offset can't be less than 2, or there's no error. + using SubCase = std::tuple; + auto const c = std::to_array({ + {2, + true, + scale == MantissaRange::MantissaScale::Small ? "100000000000000000" + : "100000000000000000000", + scale == MantissaRange::MantissaScale::Small ? "-1000000000000001" + : "-1000000000000000001"}, + {2, + false, + scale == MantissaRange::MantissaScale::Small ? "100000000000000000" + : "100000000000000000000", + "-1"}, + {30, + false, + scale == MantissaRange::MantissaScale::Small + ? "1000000000000000000000000000000000000000000000" + : "1000000000000000000000000000000000000000000000000", + "-1"}, + }); + + for (auto const& [offset, extraB, aString, bString] : c) + { + Number const a{1LL, exp + offset}; + Number const b{-((extraB ? Number{1, exp} : kNumZero) + 1)}; + + auto const bigA = toBigInt(a); + auto const bigB = toBigInt(b); + + EXPECT_EQ(bigA, BigInt{aString}); + EXPECT_EQ(bigB, BigInt{bString}); + + auto construct = [&a, &b](Number::RoundingMode r) { + NumberRoundModeGuard const roundGuard{r}; + auto const sum = a + b; + BigInt const stored = toBigInt(sum); + return std::make_pair(r, std::make_pair(stored, sum)); + }; + + BigInt const exact = bigA + bigB; + + auto const sums = [&]() { + std::map> r; + r.emplace(construct(Number::RoundingMode::TowardsZero)); + r.emplace(construct(Number::RoundingMode::Upward)); + r.emplace(construct(Number::RoundingMode::Downward)); + r.emplace(construct(Number::RoundingMode::ToNearest)); + return r; + }(); + + auto const message = [&](auto const& r, auto const& sum) { + std::ostringstream os; + os << "\n a = " << a << " (" << fmt(bigA) + << ")\n b = " << b << " (" << fmt(bigB) + << ")\n exact a + b = " << fmt(exact) << "\n"; + + auto const diff = sum.first - exact; + auto const rLabel = to_string(r); + os << std::string(15 - rLabel.length(), ' ') << rLabel << " = " << fmt(sum.first) + << "\n difference = " << fmt(diff) << "\n"; + + return os.str(); + }; + + auto const expectedExponent = + offset - (scale == MantissaRange::MantissaScale::Small && extraB ? 1 : 0); + auto const epsilon = pow10(expectedExponent); + for (auto const& [r, sum] : sums) + { + auto diff = sum.first - exact; + switch (scale) + { + case MantissaRange::MantissaScale::Small: + case MantissaRange::MantissaScale::LargeLegacy: + case MantissaRange::MantissaScale::Large320: { + // Without the fix, all the results but one round up + if (r == Number::RoundingMode::Downward) + { + // Downward works because the Guard sign is negative, and Downward + // returns Up instead of Down if negative and there's a remainder, + // whereas TowardsZero always returns Down. + EXPECT_LT(sum.first, exact) << message(r, sum); + EXPECT_EQ(diff, -(epsilon - 1)) << message(r, sum); + } + else + { + EXPECT_GT(sum.first, exact) << message(r, sum); + EXPECT_EQ(diff, 1) << message(r, sum); + } + break; + } + default: { + EXPECT_LE(sum.second.exponent(), expectedExponent) << message(r, sum); + switch (r) + { + case Number::RoundingMode::Upward: + case Number::RoundingMode::ToNearest: + EXPECT_GT(sum.first, exact) << message(r, sum); + EXPECT_EQ(diff, 1) << message(r, sum); + break; + default: + EXPECT_LT(sum.first, exact) << message(r, sum); + EXPECT_EQ(diff, -(epsilon - 1)) << message(r, sum); + } + } + } + } + } + } +} + +TEST(NumberTest, number_add_directed_sign_wrong) +{ + for (auto const mantissaScale : MantissaRange::getAllScales()) + { + NumberMantissaScaleGuard const mg{mantissaScale}; + NumberRoundModeGuard const rg{Number::RoundingMode::ToNearest}; + + auto const scale = Number::getMantissaScale(); + { + // Two negative numbers with the same exponent + Number const a{-6, Number::mantissaLog()}; + Number const b{a - 3}; + EXPECT_TRUE(a.exponent() == b.exponent() && abs(b) > abs(a)); + + BigInt const exact = toBigInt(a) + toBigInt(b); + if (scale == MantissaRange::MantissaScale::Small) + { + EXPECT_EQ(exact, BigInt{"-12000000000000003"}); + } + else + { + EXPECT_EQ(exact, BigInt{"-12000000000000000003"}); + } + + Number down, up; + { + NumberRoundModeGuard const g{Number::RoundingMode::Downward}; + down = a + b; + } + { + NumberRoundModeGuard const g{Number::RoundingMode::Upward}; + up = a + b; + } + + auto const valueDown = toBigInt(down); + auto const valueUp = toBigInt(up); + auto message = [&] { + std::ostringstream log; + log << " exact = " << fmt(exact) << "\n downward = " << fmt(valueDown) + << " (correct rounding: <= exact)" + << "\n upward = " << fmt(valueUp) << " (correct rounding: >= exact)\n\n"; + return log.str(); + }; + + if (scale == MantissaRange::MantissaScale::Large330) + { + EXPECT_LE(valueDown, exact) << message(); // Downward should round away from zero + EXPECT_GE(valueUp, exact) << message(); // Upward should round toward 0 + } + else + { + EXPECT_GT(valueDown, exact) + << message(); // Downward rounded toward zero (too high) + EXPECT_LT(valueUp, exact) << message(); // Upward rounded toward -inf (too low) + } + } + + { + // Positive control: the same magnitudes with a positive result round + Number const pa{6, Number::mantissaLog()}; + Number const pb{pa + 3}; + EXPECT_TRUE(pa.exponent() == pb.exponent() && abs(pb) > abs(pa)); + BigInt const pexact = toBigInt(pa) + toBigInt(pb); // 12'000'000'000'000'000'003 + + Number pdown, pup; + { + NumberRoundModeGuard const g{Number::RoundingMode::Downward}; + pdown = pa + pb; + } + { + NumberRoundModeGuard const g{Number::RoundingMode::Upward}; + pup = pa + pb; + } + auto const valuePDown = toBigInt(pdown); + auto const valuePUp = toBigInt(pup); + auto message = [&] { + std::ostringstream log; + log << " exact = " << fmt(pexact) << "\n downward = " << fmt(valuePDown) + << " (correct rounding: <= exact)" + << "\n upward = " << fmt(valuePUp) + << " (correct rounding: >= exact)\n\n"; + return log.str(); + }; + + EXPECT_LE(valuePDown, pexact) << message(); // correct for positive results + EXPECT_GE(valuePUp, pexact) << message(); + } + + { + // Mixed sign numbers with the same exponent: negative second value + Number const a{1, Number::mantissaLog()}; + Number const b{Number{-9, Number::mantissaLog()} - 3}; + EXPECT_TRUE(a.exponent() == b.exponent() && abs(b) > abs(a)); + + BigInt const exact = toBigInt(a) + toBigInt(b); + if (scale == MantissaRange::MantissaScale::Small) + { + EXPECT_EQ(exact, BigInt{"-8000000000000003"}); + } + else + { + EXPECT_EQ(exact, BigInt{"-8000000000000000003"}); + } + + Number down, up; + { + NumberRoundModeGuard const g{Number::RoundingMode::Downward}; + down = a + b; + } + { + NumberRoundModeGuard const g{Number::RoundingMode::Upward}; + up = a + b; + } + + auto const valueDown = toBigInt(down); + auto const valueUp = toBigInt(up); + auto message = [&] { + std::ostringstream log; + log << " exact = " << fmt(exact) << "\n downward = " << fmt(valueDown) + << " (correct rounding: <= exact)" + << "\n upward = " << fmt(valueUp) << " (correct rounding: >= exact)\n\n"; + return log.str(); + }; + + EXPECT_LE(valueDown, exact) << message(); // Downward should round away from zero + EXPECT_GE(valueUp, exact) << message(); // Upward should round toward 0 + } + + { + // Mixed sign numbers with the same exponent: negative first value + Number const a{-1, Number::mantissaLog()}; + Number const b{Number{9, Number::mantissaLog()} + 3}; + EXPECT_TRUE(a.exponent() == b.exponent() && abs(b) > abs(a)); + + BigInt const exact = toBigInt(a) + toBigInt(b); + if (scale == MantissaRange::MantissaScale::Small) + { + EXPECT_EQ(exact, BigInt{"8000000000000003"}); + } + else + { + EXPECT_EQ(exact, BigInt{"8000000000000000003"}); + } + + Number down, up; + { + NumberRoundModeGuard const g{Number::RoundingMode::Downward}; + down = a + b; + } + { + NumberRoundModeGuard const g{Number::RoundingMode::Upward}; + up = a + b; + } + + auto const valueDown = toBigInt(down); + auto const valueUp = toBigInt(up); + auto message = [&] { + std::ostringstream log; + log << " exact = " << fmt(exact) << "\n downward = " << fmt(valueDown) + << " (correct rounding: <= exact)" + << "\n upward = " << fmt(valueUp) << " (correct rounding: >= exact)\n\n"; + return log.str(); + }; + + EXPECT_LE(valueDown, exact) << message(); // Downward should round away from zero + EXPECT_GE(valueUp, exact) << message(); // Upward should round toward 0 + } + } +} + +TEST(NumberTest, number_add_to_nearest_picks_farther) +{ + for (auto const mantissaScale : MantissaRange::getAllScales()) + { + NumberMantissaScaleGuard const mg{mantissaScale}; + NumberRoundModeGuard const rg{Number::RoundingMode::ToNearest}; + + auto const scale = Number::getMantissaScale(); + + // Case is + using Case = std::pair; + + auto const c = std::to_array({ + {Number{5'175'909'259'972'499'745LL, 22}, -1'074'951'375'311'646'003}, + {Number{1}, -1'074'956'551'220'905'975}, + {Number{1, 10}, -1'074'956'551'220'905'975}, + {Number{1, 20}, -1'074'956'551'220'905'975}, + {Number{1, 27}, -1'074'956'551'220'905'975}, + {Number{1, 28}, -1'074'956'551'220'905'974}, + {Number{1, 31}, -1'074'956'551'220'904'975}, + }); + + for (auto const& [y, expectedQ] : c) + { + Number const x{-1'074'956'551'220'905'975LL, 28}; + Number const res = x + y; + + BigInt const exact = toBigInt(x) + toBigInt(y); + BigInt const vres = toBigInt(res); + + BigInt ulp = 1; + for (int i = 0; i < res.exponent(); ++i) + ulp *= 10; + + BigInt const q = (exact - ulp / 2) / ulp; + Number const normalizedExact{static_cast(q), res.exponent()}; + BigInt const norm = toBigInt(normalizedExact); + + auto message = [&](auto const& comp) { + std::ostringstream log; + log << fmt(q) + " != " + fmt(comp) << "\n" + << " x = " << x << "\n y = " << y + << "\n exact = " << fmt(exact) + << "\n result (x + y) = " << fmt(vres) + << "\n normalize(exact) = " << fmt(norm) << "\n\n"; + return log.str(); + }; + + if (scale == MantissaRange::MantissaScale::Small) + { + auto const comp = toBigInt(Number{expectedQ, -3}); + EXPECT_EQ(q, comp) << message(comp); + } + else + { + EXPECT_EQ(q, expectedQ) << message(BigInt(expectedQ)); + } + EXPECT_EQ(normalizedExact, res); + } + } +} + } // namespace xrpl From 73b6852a122854140336e6e6bc30a3a4b41aa5fd Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Mon, 13 Jul 2026 11:40:40 +0100 Subject: [PATCH 3/5] style: Unify style for all Doxygen comments (#7776) --- .../codegen/templates/LedgerEntry.h.mako | 4 +- .../codegen/templates/Transaction.h.mako | 4 +- include/xrpl/basics/Archive.h | 15 +- include/xrpl/basics/Blob.h | 7 +- include/xrpl/basics/Buffer.h | 81 +- include/xrpl/basics/CompressionAlgorithms.h | 6 +- include/xrpl/basics/CountedObject.h | 28 +- include/xrpl/basics/DecayingSample.h | 32 +- include/xrpl/basics/IntrusivePointer.h | 302 +++--- include/xrpl/basics/IntrusiveRefCounts.h | 210 ++-- include/xrpl/basics/LocalValue.h | 8 +- include/xrpl/basics/Log.h | 114 ++- include/xrpl/basics/MathUtilities.h | 5 +- include/xrpl/basics/Number.h | 42 +- include/xrpl/basics/RangeSet.h | 123 +-- include/xrpl/basics/Resolver.h | 21 +- include/xrpl/basics/SharedWeakCachePointer.h | 75 +- include/xrpl/basics/SlabAllocator.h | 98 +- include/xrpl/basics/Slice.h | 80 +- include/xrpl/basics/StringUtilities.h | 30 +- include/xrpl/basics/TaggedCache.h | 176 ++-- include/xrpl/basics/ToString.h | 9 +- include/xrpl/basics/UptimeClock.h | 13 +- include/xrpl/basics/base_uint.h | 62 +- include/xrpl/basics/chrono.h | 36 +- include/xrpl/basics/contract.h | 29 +- include/xrpl/basics/hardened_hash.h | 54 +- include/xrpl/basics/make_SSLContext.h | 8 +- include/xrpl/basics/mulDiv.h | 20 +- include/xrpl/basics/random.h | 68 +- include/xrpl/basics/scope.h | 70 +- include/xrpl/basics/spinlock.h | 130 +-- include/xrpl/basics/tagged_integer.h | 23 +- include/xrpl/beast/asio/io_latency_probe.h | 33 +- include/xrpl/beast/clock/abstract_clock.h | 72 +- .../xrpl/beast/clock/basic_seconds_clock.h | 19 +- include/xrpl/beast/clock/manual_clock.h | 35 +- .../beast/container/aged_container_utility.h | 4 +- .../container/detail/aged_ordered_container.h | 37 +- .../detail/aged_unordered_container.h | 39 +- include/xrpl/beast/core/CurrentThreadName.h | 39 +- include/xrpl/beast/core/LexicalCast.h | 38 +- include/xrpl/beast/core/List.h | 428 ++++---- include/xrpl/beast/core/LockFreeStack.h | 84 +- include/xrpl/beast/core/SemanticVersion.h | 35 +- include/xrpl/beast/hash/hash_append.h | 74 +- include/xrpl/beast/insight/Collector.h | 74 +- include/xrpl/beast/insight/Counter.h | 39 +- include/xrpl/beast/insight/Event.h | 41 +- include/xrpl/beast/insight/Gauge.h | 52 +- include/xrpl/beast/insight/Group.h | 8 +- include/xrpl/beast/insight/Groups.h | 12 +- include/xrpl/beast/insight/Hook.h | 22 +- include/xrpl/beast/insight/Meter.h | 37 +- include/xrpl/beast/insight/NullCollector.h | 4 +- include/xrpl/beast/insight/StatsDCollector.h | 20 +- include/xrpl/beast/net/IPAddress.h | 24 +- include/xrpl/beast/net/IPAddressConversion.h | 22 +- include/xrpl/beast/net/IPAddressV4.h | 15 +- include/xrpl/beast/net/IPAddressV6.h | 8 +- include/xrpl/beast/net/IPEndpoint.h | 85 +- include/xrpl/beast/rfc2616.h | 69 +- include/xrpl/beast/test/yield_to.h | 44 +- include/xrpl/beast/unit_test/amount.h | 4 +- .../beast/unit_test/detail/const_container.h | 21 +- include/xrpl/beast/unit_test/global_suites.h | 8 +- include/xrpl/beast/unit_test/match.h | 42 +- include/xrpl/beast/unit_test/recorder.h | 8 +- include/xrpl/beast/unit_test/reporter.h | 7 +- include/xrpl/beast/unit_test/results.h | 80 +- include/xrpl/beast/unit_test/runner.h | 115 ++- include/xrpl/beast/unit_test/suite.h | 225 +++-- include/xrpl/beast/unit_test/suite_info.h | 20 +- include/xrpl/beast/unit_test/suite_list.h | 13 +- include/xrpl/beast/unit_test/thread.h | 4 +- include/xrpl/beast/utility/Journal.h | 136 ++- include/xrpl/beast/utility/PropertyStream.h | 88 +- include/xrpl/beast/utility/WrappedSink.h | 4 +- include/xrpl/beast/utility/Zero.h | 37 +- include/xrpl/beast/utility/maybe_const.h | 8 +- include/xrpl/beast/utility/temp_dir.h | 32 +- include/xrpl/beast/xor_shift_engine.h | 17 +- include/xrpl/conditions/Condition.h | 55 +- include/xrpl/conditions/Fulfillment.h | 137 +-- .../xrpl/conditions/detail/PreimageSha256.h | 32 +- include/xrpl/config/BasicConfig.h | 144 +-- include/xrpl/core/ClosureCounter.h | 51 +- include/xrpl/core/Coro.ipp | 6 +- include/xrpl/core/HashRouter.h | 90 +- include/xrpl/core/Job.h | 17 +- include/xrpl/core/JobQueue.h | 157 +-- include/xrpl/core/JobTypeInfo.h | 17 +- include/xrpl/core/NetworkIDService.h | 30 +- include/xrpl/core/PeerReservationTable.h | 2 +- include/xrpl/core/ServiceRegistry.h | 26 +- include/xrpl/core/detail/Workers.h | 107 +- include/xrpl/core/detail/semaphore.h | 22 +- include/xrpl/crypto/RFC1751.h | 15 +- include/xrpl/crypto/csprng.h | 46 +- include/xrpl/crypto/secure_erase.h | 29 +- include/xrpl/json/JsonPropertyStream.h | 4 +- include/xrpl/json/Output.h | 18 +- include/xrpl/json/Writer.h | 184 ++-- include/xrpl/json/json_reader.h | 94 +- include/xrpl/json/json_value.h | 255 +++-- include/xrpl/json/json_writer.h | 99 +- include/xrpl/json/to_string.h | 8 +- include/xrpl/ledger/AcceptedLedgerTx.h | 22 +- include/xrpl/ledger/AmendmentTable.h | 24 +- include/xrpl/ledger/ApplyView.h | 377 +++---- include/xrpl/ledger/ApplyViewImpl.h | 47 +- include/xrpl/ledger/CachedView.h | 18 +- include/xrpl/ledger/CanonicalTXSet.h | 14 +- include/xrpl/ledger/Dir.h | 25 +- include/xrpl/ledger/Ledger.h | 132 +-- include/xrpl/ledger/LedgerTiming.h | 108 +- include/xrpl/ledger/OpenView.h | 141 +-- include/xrpl/ledger/OrderBookDB.h | 119 +-- include/xrpl/ledger/PaymentSandbox.h | 59 +- include/xrpl/ledger/PendingSaves.h | 73 +- include/xrpl/ledger/RawView.h | 99 +- include/xrpl/ledger/ReadView.h | 152 +-- include/xrpl/ledger/Sandbox.h | 13 +- include/xrpl/ledger/View.h | 124 +-- include/xrpl/ledger/helpers/AMMHelpers.h | 104 +- .../xrpl/ledger/helpers/AccountRootHelpers.h | 336 ++++--- .../xrpl/ledger/helpers/DirectoryHelpers.h | 99 +- include/xrpl/ledger/helpers/LendingHelpers.h | 24 +- include/xrpl/ledger/helpers/MPTokenHelpers.h | 115 ++- include/xrpl/ledger/helpers/NFTokenHelpers.h | 60 +- include/xrpl/ledger/helpers/OfferHelpers.h | 25 +- .../ledger/helpers/PaymentChannelHelpers.h | 43 +- .../xrpl/ledger/helpers/RippleStateHelpers.h | 55 +- include/xrpl/ledger/helpers/SponsorHelpers.h | 170 ++-- include/xrpl/ledger/helpers/TokenHelpers.h | 43 +- include/xrpl/ledger/helpers/VaultHelpers.h | 132 +-- include/xrpl/net/HTTPClient.h | 16 +- include/xrpl/net/RegisterSSLCerts.h | 15 +- include/xrpl/nodestore/Backend.h | 143 +-- include/xrpl/nodestore/Database.h | 197 ++-- include/xrpl/nodestore/DatabaseRotating.h | 15 +- include/xrpl/nodestore/DummyScheduler.h | 4 +- include/xrpl/nodestore/Factory.h | 44 +- include/xrpl/nodestore/Manager.h | 86 +- include/xrpl/nodestore/NodeObject.h | 58 +- include/xrpl/nodestore/Scheduler.h | 50 +- include/xrpl/nodestore/Task.h | 11 +- include/xrpl/nodestore/Types.h | 8 +- include/xrpl/nodestore/detail/BatchWriter.h | 49 +- include/xrpl/nodestore/detail/DecodedBlob.h | 33 +- include/xrpl/nodestore/detail/EncodedBlob.h | 65 +- include/xrpl/protocol/AMMCore.h | 24 +- include/xrpl/protocol/AccountID.h | 69 +- include/xrpl/protocol/Asset.h | 6 +- include/xrpl/protocol/Book.h | 17 +- include/xrpl/protocol/BuildInfo.h | 109 ++- include/xrpl/protocol/ConfidentialTransfer.h | 12 +- include/xrpl/protocol/ErrorCodes.h | 41 +- include/xrpl/protocol/Feature.h | 30 +- include/xrpl/protocol/Fees.h | 34 +- include/xrpl/protocol/HashPrefix.h | 77 +- include/xrpl/protocol/IOUAmount.h | 42 +- include/xrpl/protocol/Indexes.h | 193 ++-- include/xrpl/protocol/InnerObjectFormats.h | 10 +- include/xrpl/protocol/Issue.h | 23 +- include/xrpl/protocol/Keylet.h | 21 +- include/xrpl/protocol/KnownFormats.h | 82 +- include/xrpl/protocol/LedgerFormats.h | 147 +-- include/xrpl/protocol/LedgerHeader.h | 16 +- include/xrpl/protocol/LedgerShortcut.h | 12 +- include/xrpl/protocol/MPTAmount.h | 34 +- include/xrpl/protocol/MPTIssue.h | 3 +- .../xrpl/protocol/NFTSyntheticSerializer.h | 5 +- include/xrpl/protocol/NFTokenID.h | 12 +- include/xrpl/protocol/NFTokenOfferID.h | 10 +- include/xrpl/protocol/Protocol.h | 347 ++++--- include/xrpl/protocol/PublicKey.h | 138 +-- include/xrpl/protocol/Quality.h | 105 +- include/xrpl/protocol/QualityFunction.h | 12 +- include/xrpl/protocol/Rate.h | 21 +- include/xrpl/protocol/Rules.h | 37 +- include/xrpl/protocol/SField.h | 29 +- include/xrpl/protocol/SOTemplate.h | 43 +- include/xrpl/protocol/STAmount.h | 21 +- include/xrpl/protocol/STBase.h | 52 +- include/xrpl/protocol/STExchange.h | 24 +- include/xrpl/protocol/STLedgerEntry.h | 22 +- include/xrpl/protocol/STObject.h | 172 ++-- include/xrpl/protocol/STParsedJSON.h | 41 +- include/xrpl/protocol/STTakesAsset.h | 7 +- include/xrpl/protocol/STTx.h | 58 +- include/xrpl/protocol/STValidation.h | 61 +- include/xrpl/protocol/STVector256.h | 8 +- include/xrpl/protocol/SecretKey.h | 72 +- include/xrpl/protocol/Seed.h | 65 +- include/xrpl/protocol/SeqProxy.h | 59 +- include/xrpl/protocol/Sign.h | 69 +- include/xrpl/protocol/SystemParameters.h | 40 +- include/xrpl/protocol/TxFlags.h | 47 +- include/xrpl/protocol/TxFormats.h | 84 +- include/xrpl/protocol/TxMeta.h | 4 +- include/xrpl/protocol/UintTypes.h | 70 +- include/xrpl/protocol/Units.h | 68 +- include/xrpl/protocol/XRPAmount.h | 25 +- include/xrpl/protocol/detail/STVar.h | 3 +- include/xrpl/protocol/digest.h | 103 +- include/xrpl/protocol/serialize.h | 8 +- include/xrpl/protocol/tokens.h | 23 +- .../xrpl/protocol_autogen/LedgerEntryBase.h | 4 +- .../xrpl/protocol_autogen/TransactionBase.h | 4 +- .../protocol_autogen/ledger_entries/AMM.h | 4 +- .../ledger_entries/AccountRoot.h | 4 +- .../ledger_entries/Amendments.h | 4 +- .../protocol_autogen/ledger_entries/Bridge.h | 4 +- .../protocol_autogen/ledger_entries/Check.h | 4 +- .../ledger_entries/Credential.h | 4 +- .../protocol_autogen/ledger_entries/DID.h | 4 +- .../ledger_entries/Delegate.h | 4 +- .../ledger_entries/DepositPreauth.h | 4 +- .../ledger_entries/DirectoryNode.h | 4 +- .../protocol_autogen/ledger_entries/Escrow.h | 4 +- .../ledger_entries/FeeSettings.h | 4 +- .../ledger_entries/LedgerHashes.h | 4 +- .../protocol_autogen/ledger_entries/Loan.h | 4 +- .../ledger_entries/LoanBroker.h | 4 +- .../protocol_autogen/ledger_entries/MPToken.h | 4 +- .../ledger_entries/MPTokenIssuance.h | 4 +- .../ledger_entries/NFTokenOffer.h | 4 +- .../ledger_entries/NFTokenPage.h | 4 +- .../ledger_entries/NegativeUNL.h | 4 +- .../protocol_autogen/ledger_entries/Offer.h | 4 +- .../protocol_autogen/ledger_entries/Oracle.h | 4 +- .../ledger_entries/PayChannel.h | 4 +- .../ledger_entries/PermissionedDomain.h | 4 +- .../ledger_entries/RippleState.h | 4 +- .../ledger_entries/SignerList.h | 4 +- .../ledger_entries/Sponsorship.h | 4 +- .../protocol_autogen/ledger_entries/Ticket.h | 4 +- .../protocol_autogen/ledger_entries/Vault.h | 4 +- .../ledger_entries/XChainOwnedClaimID.h | 4 +- .../XChainOwnedCreateAccountClaimID.h | 4 +- .../protocol_autogen/transactions/AMMBid.h | 4 +- .../transactions/AMMClawback.h | 4 +- .../protocol_autogen/transactions/AMMCreate.h | 4 +- .../protocol_autogen/transactions/AMMDelete.h | 4 +- .../transactions/AMMDeposit.h | 4 +- .../protocol_autogen/transactions/AMMVote.h | 4 +- .../transactions/AMMWithdraw.h | 4 +- .../transactions/AccountDelete.h | 4 +- .../transactions/AccountSet.h | 4 +- .../protocol_autogen/transactions/Batch.h | 4 +- .../transactions/CheckCancel.h | 4 +- .../protocol_autogen/transactions/CheckCash.h | 4 +- .../transactions/CheckCreate.h | 4 +- .../protocol_autogen/transactions/Clawback.h | 4 +- .../transactions/ConfidentialMPTClawback.h | 4 +- .../transactions/ConfidentialMPTConvert.h | 4 +- .../transactions/ConfidentialMPTConvertBack.h | 4 +- .../transactions/ConfidentialMPTMergeInbox.h | 4 +- .../transactions/ConfidentialMPTSend.h | 4 +- .../transactions/CredentialAccept.h | 4 +- .../transactions/CredentialCreate.h | 4 +- .../transactions/CredentialDelete.h | 4 +- .../protocol_autogen/transactions/DIDDelete.h | 4 +- .../protocol_autogen/transactions/DIDSet.h | 4 +- .../transactions/DelegateSet.h | 4 +- .../transactions/DepositPreauth.h | 4 +- .../transactions/EnableAmendment.h | 4 +- .../transactions/EscrowCancel.h | 4 +- .../transactions/EscrowCreate.h | 4 +- .../transactions/EscrowFinish.h | 4 +- .../transactions/LedgerStateFix.h | 4 +- .../transactions/LoanBrokerCoverClawback.h | 4 +- .../transactions/LoanBrokerCoverDeposit.h | 4 +- .../transactions/LoanBrokerCoverWithdraw.h | 4 +- .../transactions/LoanBrokerDelete.h | 4 +- .../transactions/LoanBrokerSet.h | 4 +- .../transactions/LoanDelete.h | 4 +- .../transactions/LoanManage.h | 4 +- .../protocol_autogen/transactions/LoanPay.h | 4 +- .../protocol_autogen/transactions/LoanSet.h | 4 +- .../transactions/MPTokenAuthorize.h | 4 +- .../transactions/MPTokenIssuanceCreate.h | 4 +- .../transactions/MPTokenIssuanceDestroy.h | 4 +- .../transactions/MPTokenIssuanceSet.h | 4 +- .../transactions/NFTokenAcceptOffer.h | 4 +- .../transactions/NFTokenBurn.h | 4 +- .../transactions/NFTokenCancelOffer.h | 4 +- .../transactions/NFTokenCreateOffer.h | 4 +- .../transactions/NFTokenMint.h | 4 +- .../transactions/NFTokenModify.h | 4 +- .../transactions/OfferCancel.h | 4 +- .../transactions/OfferCreate.h | 4 +- .../transactions/OracleDelete.h | 4 +- .../protocol_autogen/transactions/OracleSet.h | 4 +- .../protocol_autogen/transactions/Payment.h | 4 +- .../transactions/PaymentChannelClaim.h | 4 +- .../transactions/PaymentChannelCreate.h | 4 +- .../transactions/PaymentChannelFund.h | 4 +- .../transactions/PermissionedDomainDelete.h | 4 +- .../transactions/PermissionedDomainSet.h | 4 +- .../protocol_autogen/transactions/SetFee.h | 4 +- .../transactions/SetRegularKey.h | 4 +- .../transactions/SignerListSet.h | 4 +- .../transactions/SponsorshipSet.h | 4 +- .../transactions/SponsorshipTransfer.h | 4 +- .../transactions/TicketCreate.h | 4 +- .../protocol_autogen/transactions/TrustSet.h | 4 +- .../protocol_autogen/transactions/UNLModify.h | 4 +- .../transactions/VaultClawback.h | 4 +- .../transactions/VaultCreate.h | 4 +- .../transactions/VaultDelete.h | 4 +- .../transactions/VaultDeposit.h | 4 +- .../protocol_autogen/transactions/VaultSet.h | 4 +- .../transactions/VaultWithdraw.h | 4 +- .../transactions/XChainAccountCreateCommit.h | 4 +- .../XChainAddAccountCreateAttestation.h | 4 +- .../transactions/XChainAddClaimAttestation.h | 4 +- .../transactions/XChainClaim.h | 4 +- .../transactions/XChainCommit.h | 4 +- .../transactions/XChainCreateBridge.h | 4 +- .../transactions/XChainCreateClaimID.h | 4 +- .../transactions/XChainModifyBridge.h | 4 +- include/xrpl/rdb/RelationalDatabase.h | 6 +- include/xrpl/rdb/SociDB.h | 64 +- include/xrpl/resource/Charge.h | 24 +- include/xrpl/resource/Consumer.h | 47 +- include/xrpl/resource/Disposition.h | 16 +- include/xrpl/resource/Fees.h | 4 +- include/xrpl/resource/Gossip.h | 8 +- include/xrpl/resource/ResourceManager.h | 33 +- include/xrpl/resource/detail/Entry.h | 4 +- include/xrpl/resource/detail/Import.h | 4 +- include/xrpl/resource/detail/Logic.h | 4 +- include/xrpl/resource/detail/Tuning.h | 4 +- include/xrpl/server/Handoff.h | 4 +- include/xrpl/server/InfoSub.h | 59 +- include/xrpl/server/LoadFeeTrack.h | 19 +- include/xrpl/server/Manifest.h | 353 ++++--- include/xrpl/server/NetworkOPs.h | 82 +- include/xrpl/server/Port.h | 4 +- include/xrpl/server/Server.h | 4 +- include/xrpl/server/Session.h | 79 +- include/xrpl/server/SimpleWriter.h | 4 +- include/xrpl/server/WSSession.h | 48 +- include/xrpl/server/Wallet.h | 23 +- include/xrpl/server/Writer.h | 28 +- include/xrpl/server/detail/BaseHTTPPeer.h | 4 +- include/xrpl/server/detail/BaseWSPeer.h | 12 +- include/xrpl/server/detail/Door.h | 17 +- include/xrpl/server/detail/ServerImpl.h | 50 +- include/xrpl/server/detail/io_list.h | 149 +-- include/xrpl/shamap/Family.h | 14 +- include/xrpl/shamap/FullBelowCache.h | 76 +- include/xrpl/shamap/SHAMap.h | 228 +++-- .../xrpl/shamap/SHAMapAccountStateLeafNode.h | 4 +- include/xrpl/shamap/SHAMapInnerNode.h | 81 +- include/xrpl/shamap/SHAMapLeafNode.h | 11 +- include/xrpl/shamap/SHAMapNodeID.h | 15 +- include/xrpl/shamap/SHAMapSyncFilter.h | 4 +- include/xrpl/shamap/SHAMapTreeNode.h | 103 +- include/xrpl/shamap/SHAMapTxLeafNode.h | 4 +- .../xrpl/shamap/SHAMapTxPlusMetaLeafNode.h | 4 +- include/xrpl/shamap/detail/TaggedPointer.h | 254 ++--- include/xrpl/tx/ApplyContext.h | 35 +- include/xrpl/tx/Transactor.h | 170 ++-- include/xrpl/tx/apply.h | 186 ++-- include/xrpl/tx/applySteps.h | 383 +++++--- include/xrpl/tx/invariants/InvariantCheck.h | 3 +- include/xrpl/tx/invariants/MPTInvariant.h | 18 +- include/xrpl/tx/invariants/VaultInvariant.h | 16 +- include/xrpl/tx/paths/AMMLiquidity.h | 15 +- include/xrpl/tx/paths/AMMOffer.h | 15 +- include/xrpl/tx/paths/BookTip.h | 22 +- include/xrpl/tx/paths/Flow.h | 38 +- include/xrpl/tx/paths/Offer.h | 41 +- include/xrpl/tx/paths/OfferStream.h | 59 +- include/xrpl/tx/paths/RippleCalc.h | 11 +- include/xrpl/tx/paths/detail/FlatSets.h | 11 +- include/xrpl/tx/paths/detail/Steps.h | 415 ++++---- include/xrpl/tx/paths/detail/StrandFlow.h | 83 +- .../tx/transactors/account/SignerListSet.h | 6 +- include/xrpl/tx/transactors/dex/AMMBid.h | 3 +- include/xrpl/tx/transactors/dex/AMMClawback.h | 3 +- include/xrpl/tx/transactors/dex/AMMContext.h | 6 +- include/xrpl/tx/transactors/dex/AMMCreate.h | 53 +- include/xrpl/tx/transactors/dex/AMMDelete.h | 3 +- include/xrpl/tx/transactors/dex/AMMDeposit.h | 24 +- include/xrpl/tx/transactors/dex/AMMVote.h | 3 +- include/xrpl/tx/transactors/dex/AMMWithdraw.h | 37 +- include/xrpl/tx/transactors/dex/OfferCreate.h | 20 +- .../xrpl/tx/transactors/lending/LoanManage.h | 9 +- .../xrpl/tx/transactors/oracle/OracleDelete.h | 14 +- .../xrpl/tx/transactors/oracle/OracleSet.h | 14 +- .../PermissionedDomainDelete.h | 4 +- .../PermissionedDomainSet.h | 4 +- .../xrpl/tx/transactors/system/TicketCreate.h | 12 +- src/libxrpl/basics/Number.cpp | 68 +- src/libxrpl/basics/ResolverAsio.cpp | 18 +- src/libxrpl/basics/base64.cpp | 56 +- src/libxrpl/basics/make_SSLContext.cpp | 83 +- src/libxrpl/core/detail/LoadMonitor.cpp | 9 +- src/libxrpl/crypto/RFC1751.cpp | 20 +- src/libxrpl/json/Writer.cpp | 14 +- src/libxrpl/json/json_value.cpp | 7 +- src/libxrpl/ledger/helpers/NFTokenHelpers.cpp | 12 +- src/libxrpl/nodestore/backend/NullFactory.cpp | 4 +- .../nodestore/backend/RocksDBFactory.cpp | 4 +- src/libxrpl/protocol/AccountID.cpp | 4 +- src/libxrpl/protocol/Feature.cpp | 52 +- src/libxrpl/protocol/Indexes.cpp | 35 +- src/libxrpl/protocol/PublicKey.cpp | 25 +- src/libxrpl/protocol/SecretKey.cpp | 39 +- src/libxrpl/protocol/tokens.cpp | 19 +- src/libxrpl/rdb/SociDB.cpp | 17 +- src/libxrpl/shamap/SHAMap.cpp | 23 +- src/libxrpl/shamap/SHAMapSync.cpp | 15 +- src/libxrpl/tx/Transactor.cpp | 24 +- src/libxrpl/tx/paths/BookStep.cpp | 15 +- .../tx/transactors/bridge/XChainBridge.cpp | 196 ++-- src/libxrpl/tx/transactors/dex/AMMDeposit.cpp | 15 +- .../tx/transactors/dex/AMMWithdraw.cpp | 18 +- .../PermissionedDomainDelete.cpp | 4 +- .../PermissionedDomainSet.cpp | 4 +- src/test/app/AMMCalc_test.cpp | 3 +- src/test/app/AMMExtendedMPT_test.cpp | 6 +- src/test/app/AMMExtended_test.cpp | 12 +- src/test/app/CheckMPT_test.cpp | 3 +- src/test/app/Invariants_test.cpp | 3 +- src/test/app/LedgerHistory_test.cpp | 14 +- src/test/app/Loan_test.cpp | 30 +- src/test/app/Ticket_test.cpp | 20 +- src/test/beast/define_print.cpp | 4 +- .../DistributedValidatorsSim_test.cpp | 3 +- src/test/consensus/LedgerTrie_test.cpp | 92 +- src/test/core/Config_test.cpp | 4 +- src/test/csf/BasicNetwork.h | 225 ++--- src/test/csf/CollectorRef.h | 99 +- src/test/csf/Digraph.h | 158 +-- src/test/csf/Histogram.h | 58 +- src/test/csf/Peer.h | 238 +++-- src/test/csf/PeerGroup.h | 166 ++-- src/test/csf/Proposal.h | 7 +- src/test/csf/Scheduler.h | 188 ++-- src/test/csf/Sim.h | 88 +- src/test/csf/TrustGraph.h | 76 +- src/test/csf/Tx.h | 31 +- src/test/csf/Validation.h | 16 +- src/test/csf/collectors.h | 104 +- src/test/csf/events.h | 73 +- src/test/csf/ledgers.h | 169 ++-- src/test/csf/random.h | 78 +- src/test/csf/submitters.h | 39 +- src/test/csf/timers.h | 3 +- src/test/jtx/AMM.h | 12 +- src/test/jtx/AMMTest.h | 3 +- src/test/jtx/AbstractClient.h | 27 +- src/test/jtx/Account.h | 62 +- src/test/jtx/CheckMessageLogs.h | 16 +- src/test/jtx/Env.h | 508 +++++----- src/test/jtx/Env_ss.h | 9 +- src/test/jtx/JSONRPCClient.h | 4 +- src/test/jtx/JTx.h | 25 +- src/test/jtx/Oracle.h | 3 +- src/test/jtx/PathSet.h | 9 +- src/test/jtx/TestHelpers.h | 30 +- src/test/jtx/WSClient.h | 12 +- src/test/jtx/acctdelete.h | 4 +- src/test/jtx/amount.h | 167 ++-- src/test/jtx/balance.h | 17 +- src/test/jtx/batch.h | 16 +- src/test/jtx/check.h | 24 +- src/test/jtx/delivermin.h | 4 +- src/test/jtx/deposit.h | 12 +- src/test/jtx/did.h | 16 +- src/test/jtx/directory.h | 32 +- src/test/jtx/domain.h | 4 +- src/test/jtx/envconfig.h | 174 ++-- src/test/jtx/escrow.h | 12 +- src/test/jtx/fee.h | 4 +- src/test/jtx/flags.h | 16 +- src/test/jtx/impl/dids.cpp | 4 +- src/test/jtx/impl/directory.cpp | 4 +- src/test/jtx/impl/escrow.cpp | 4 +- src/test/jtx/jtx_json.h | 4 +- src/test/jtx/ledgerStateFix.h | 12 +- src/test/jtx/memo.h | 11 +- src/test/jtx/mpt.h | 84 +- src/test/jtx/multisign.h | 25 +- src/test/jtx/noop.h | 4 +- src/test/jtx/offer.h | 8 +- src/test/jtx/owners.h | 34 +- src/test/jtx/paths.h | 13 +- src/test/jtx/pay.h | 4 +- src/test/jtx/prop.h | 4 +- src/test/jtx/quality.h | 16 +- src/test/jtx/rate.h | 4 +- src/test/jtx/regkey.h | 8 +- src/test/jtx/require.h | 17 +- src/test/jtx/rpc.h | 15 +- src/test/jtx/sendmax.h | 4 +- src/test/jtx/seq.h | 4 +- src/test/jtx/sig.h | 19 +- src/test/jtx/tag.h | 8 +- src/test/jtx/tags.h | 4 +- src/test/jtx/ter.h | 7 +- src/test/jtx/ticket.h | 16 +- src/test/jtx/token.h | 84 +- src/test/jtx/trust.h | 8 +- src/test/jtx/txflags.h | 4 +- src/test/jtx/utility.h | 41 +- src/test/jtx/vault.h | 4 +- src/test/nodestore/TestBase.h | 19 +- src/test/overlay/reduce_relay_test.cpp | 83 +- src/test/rpc/LedgerEntry_test.cpp | 16 +- src/test/rpc/LedgerRPC_test.cpp | 10 +- src/test/unit_test/FileDirGuard.h | 8 +- src/test/unit_test/multi_runner.h | 6 +- src/test/unit_test/utils.h | 8 +- src/tests/libxrpl/helpers/Account.h | 20 +- src/tests/libxrpl/helpers/TestFamily.h | 15 +- .../libxrpl/helpers/TestServiceRegistry.h | 25 +- src/tests/libxrpl/helpers/TxTest.h | 16 +- .../app/consensus/RCLCensorshipDetector.h | 49 +- src/xrpld/app/consensus/RCLConsensus.cpp | 4 +- src/xrpld/app/consensus/RCLConsensus.h | 363 ++++--- src/xrpld/app/consensus/RCLCxLedger.h | 82 +- src/xrpld/app/consensus/RCLCxPeerPos.h | 83 +- src/xrpld/app/consensus/RCLCxTx.h | 152 +-- src/xrpld/app/consensus/RCLValidations.h | 147 ++- .../app/ledger/AbstractFetchPackContainer.h | 20 +- src/xrpld/app/ledger/AcceptedLedger.h | 17 +- src/xrpld/app/ledger/BuildLedger.h | 50 +- src/xrpld/app/ledger/InboundLedger.h | 12 +- src/xrpld/app/ledger/InboundLedgers.h | 17 +- src/xrpld/app/ledger/InboundTransactions.h | 15 +- src/xrpld/app/ledger/LedgerCleaner.h | 25 +- src/xrpld/app/ledger/LedgerHistory.cpp | 3 +- src/xrpld/app/ledger/LedgerHistory.h | 78 +- src/xrpld/app/ledger/LedgerHolder.h | 13 +- src/xrpld/app/ledger/LedgerMaster.h | 57 +- src/xrpld/app/ledger/LedgerPersistence.h | 91 +- src/xrpld/app/ledger/LedgerReplay.h | 9 +- src/xrpld/app/ledger/LedgerReplayTask.h | 12 +- src/xrpld/app/ledger/LedgerReplayer.h | 8 +- src/xrpld/app/ledger/LedgerToJson.h | 13 +- src/xrpld/app/ledger/OpenLedger.h | 183 ++-- src/xrpld/app/ledger/OrderBookDBImpl.h | 17 +- src/xrpld/app/ledger/detail/BuildLedger.cpp | 19 +- src/xrpld/app/ledger/detail/InboundLedger.cpp | 59 +- .../app/ledger/detail/InboundLedgers.cpp | 20 +- .../app/ledger/detail/InboundTransactions.cpp | 3 +- src/xrpld/app/ledger/detail/LedgerCleaner.cpp | 30 +- src/xrpld/app/ledger/detail/LedgerMaster.cpp | 70 +- src/xrpld/app/ledger/detail/TimeoutCounter.h | 99 +- src/xrpld/app/main/Application.h | 14 +- src/xrpld/app/main/CollectorManager.h | 4 +- src/xrpld/app/main/LoadManager.h | 60 +- src/xrpld/app/main/NodeIdentity.h | 9 +- src/xrpld/app/main/NodeStoreScheduler.h | 4 +- src/xrpld/app/misc/DeliverMax.h | 12 +- src/xrpld/app/misc/FeeVote.h | 35 +- src/xrpld/app/misc/NegativeUNLVote.h | 4 +- src/xrpld/app/misc/NetworkOPs.cpp | 14 +- src/xrpld/app/misc/SHAMapStore.h | 56 +- src/xrpld/app/misc/SHAMapStoreImp.h | 13 +- src/xrpld/app/misc/Transaction.h | 40 +- src/xrpld/app/misc/TxQ.h | 921 ++++++++++-------- src/xrpld/app/misc/ValidatorKeys.h | 7 +- src/xrpld/app/misc/ValidatorList.h | 784 ++++++++------- src/xrpld/app/misc/ValidatorSite.h | 199 ++-- src/xrpld/app/misc/detail/AmendmentTable.cpp | 95 +- src/xrpld/app/misc/setup_HashRouter.h | 4 +- src/xrpld/consensus/Consensus.h | 683 ++++++------- src/xrpld/consensus/ConsensusParms.h | 130 ++- src/xrpld/consensus/ConsensusProposal.h | 170 ++-- src/xrpld/consensus/ConsensusTypes.h | 185 ++-- src/xrpld/consensus/DisputedTx.h | 129 ++- src/xrpld/consensus/LedgerTrie.h | 389 ++++---- src/xrpld/consensus/Validations.h | 675 +++++++------ src/xrpld/core/Config.h | 110 ++- src/xrpld/core/NetworkIDServiceImpl.h | 13 +- src/xrpld/core/TimeKeeper.h | 54 +- src/xrpld/core/detail/Config.cpp | 16 +- src/xrpld/overlay/Cluster.h | 56 +- src/xrpld/overlay/Compression.h | 6 +- src/xrpld/overlay/Message.h | 27 +- src/xrpld/overlay/Overlay.h | 108 +- src/xrpld/overlay/Peer.h | 35 +- src/xrpld/overlay/PeerSet.h | 29 +- src/xrpld/overlay/Slot.h | 124 ++- src/xrpld/overlay/Squelch.h | 19 +- src/xrpld/overlay/detail/ConnectAttempt.h | 4 +- src/xrpld/overlay/detail/Handshake.cpp | 29 +- src/xrpld/overlay/detail/Handshake.h | 189 ++-- src/xrpld/overlay/detail/Message.cpp | 71 +- src/xrpld/overlay/detail/OverlayImpl.cpp | 20 +- src/xrpld/overlay/detail/OverlayImpl.h | 133 +-- src/xrpld/overlay/detail/PeerImp.cpp | 8 +- src/xrpld/overlay/detail/PeerImp.h | 163 ++-- src/xrpld/overlay/detail/PeerSet.cpp | 8 +- src/xrpld/overlay/detail/ProtocolMessage.h | 74 +- src/xrpld/overlay/detail/ProtocolVersion.cpp | 11 +- src/xrpld/overlay/detail/ProtocolVersion.h | 54 +- src/xrpld/overlay/detail/TrafficCount.h | 48 +- src/xrpld/overlay/detail/Tuning.h | 203 ++-- src/xrpld/overlay/detail/TxMetrics.h | 65 +- src/xrpld/overlay/detail/ZeroCopyStream.h | 20 +- src/xrpld/overlay/make_Overlay.h | 4 +- src/xrpld/overlay/predicates.h | 32 +- src/xrpld/peerfinder/PeerfinderManager.h | 230 +++-- src/xrpld/peerfinder/Slot.h | 43 +- src/xrpld/peerfinder/detail/Bootcache.h | 71 +- src/xrpld/peerfinder/detail/Checker.h | 43 +- src/xrpld/peerfinder/detail/Counts.h | 140 ++- src/xrpld/peerfinder/detail/Fixed.h | 16 +- src/xrpld/peerfinder/detail/Handouts.h | 33 +- src/xrpld/peerfinder/detail/Livecache.h | 62 +- src/xrpld/peerfinder/detail/Logic.h | 42 +- src/xrpld/peerfinder/detail/SlotImp.h | 13 +- src/xrpld/peerfinder/detail/Source.h | 21 +- src/xrpld/peerfinder/detail/SourceStrings.h | 4 +- src/xrpld/peerfinder/detail/Store.h | 4 +- src/xrpld/peerfinder/detail/StoreSqdb.h | 4 +- src/xrpld/peerfinder/detail/Tuning.h | 45 +- src/xrpld/peerfinder/detail/iosformat.h | 20 +- src/xrpld/peerfinder/make_Manager.h | 4 +- src/xrpld/perflog/detail/PerfLogImp.h | 4 +- src/xrpld/rpc/Context.h | 4 +- src/xrpld/rpc/DeliveredAmount.h | 13 +- src/xrpld/rpc/MPTokenIssuanceID.h | 11 +- src/xrpld/rpc/RPCCall.h | 7 +- src/xrpld/rpc/RPCHandler.h | 4 +- src/xrpld/rpc/RPCSub.h | 4 +- src/xrpld/rpc/Role.h | 20 +- src/xrpld/rpc/Status.h | 52 +- src/xrpld/rpc/detail/AssetCache.h | 25 +- src/xrpld/rpc/detail/Handler.cpp | 4 +- src/xrpld/rpc/detail/Handler.h | 8 +- src/xrpld/rpc/detail/PathRequest.h | 7 +- src/xrpld/rpc/detail/PathRequestManager.cpp | 7 +- src/xrpld/rpc/detail/PathRequestManager.h | 11 +- src/xrpld/rpc/detail/Pathfinder.h | 27 +- src/xrpld/rpc/detail/RPCHandler.cpp | 153 ++- src/xrpld/rpc/detail/RPCHelpers.h | 3 +- src/xrpld/rpc/detail/TransactionSign.cpp | 16 +- src/xrpld/rpc/detail/TransactionSign.h | 71 +- src/xrpld/rpc/detail/TrustLine.h | 56 +- src/xrpld/rpc/detail/Tuning.h | 64 +- .../rpc/handlers/account/AccountNFTs.cpp | 21 +- .../rpc/handlers/account/AccountObjects.cpp | 21 +- .../handlers/orderbook/GetAggregatePrice.cpp | 3 +- src/xrpld/rpc/json_body.h | 4 +- 653 files changed, 18022 insertions(+), 12993 deletions(-) diff --git a/cmake/scripts/codegen/templates/LedgerEntry.h.mako b/cmake/scripts/codegen/templates/LedgerEntry.h.mako index 63f5f39ef9..c799903b21 100644 --- a/cmake/scripts/codegen/templates/LedgerEntry.h.mako +++ b/cmake/scripts/codegen/templates/LedgerEntry.h.mako @@ -177,7 +177,9 @@ ${field['typeData']['setter_type']} ${field['paramName']}${',' if i < len(requir object_ = *sle; } - /** @brief Ledger entry-specific field setters */ + /** + * @brief Ledger entry-specific field setters + */ % for field in fields: /** diff --git a/cmake/scripts/codegen/templates/Transaction.h.mako b/cmake/scripts/codegen/templates/Transaction.h.mako index d3b303d9d6..49e2e4a5cd 100644 --- a/cmake/scripts/codegen/templates/Transaction.h.mako +++ b/cmake/scripts/codegen/templates/Transaction.h.mako @@ -185,7 +185,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ % for field in fields: /** diff --git a/include/xrpl/basics/Archive.h b/include/xrpl/basics/Archive.h index 58e12bbb71..66d6a019af 100644 --- a/include/xrpl/basics/Archive.h +++ b/include/xrpl/basics/Archive.h @@ -4,13 +4,14 @@ namespace xrpl { -/** Extract a tar archive compressed with lz4 - - @param src the path of the archive to be extracted - @param dst the directory to extract to - - @throws runtime_error -*/ +/** + * Extract a tar archive compressed with lz4 + * + * @param src the path of the archive to be extracted + * @param dst the directory to extract to + * + * @throws runtime_error + */ void extractTarLz4(boost::filesystem::path const& src, boost::filesystem::path const& dst); diff --git a/include/xrpl/basics/Blob.h b/include/xrpl/basics/Blob.h index ee0d6cf3b5..bfb8e5a697 100644 --- a/include/xrpl/basics/Blob.h +++ b/include/xrpl/basics/Blob.h @@ -4,9 +4,10 @@ namespace xrpl { -/** Storage for linear binary data. - Blocks of binary data appear often in various idioms and structures. -*/ +/** + * Storage for linear binary data. + * Blocks of binary data appear often in various idioms and structures. + */ using Blob = std::vector; } // namespace xrpl diff --git a/include/xrpl/basics/Buffer.h b/include/xrpl/basics/Buffer.h index c0ae8ef56e..05af6c409a 100644 --- a/include/xrpl/basics/Buffer.h +++ b/include/xrpl/basics/Buffer.h @@ -10,9 +10,10 @@ namespace xrpl { -/** Like std::vector but better. - Meets the requirements of BufferFactory. -*/ +/** + * Like std::vector but better. + * Meets the requirements of BufferFactory. + */ class Buffer { private: @@ -24,30 +25,37 @@ public: Buffer() = default; - /** Create an uninitialized buffer with the given size. */ + /** + * Create an uninitialized buffer with the given size. + */ explicit Buffer(std::size_t size) : p_((size != 0u) ? new std::uint8_t[size] : nullptr), size_(size) { } - /** Create a buffer as a copy of existing memory. - - @param data a pointer to the existing memory. If - size is non-zero, it must not be null. - @param size size of the existing memory block. - */ + /** + * Create a buffer as a copy of existing memory. + * + * @param data a pointer to the existing memory. If + * size is non-zero, it must not be null. + * @param size size of the existing memory block. + */ Buffer(void const* data, std::size_t size) : Buffer(size) { if (size != 0u) std::memcpy(p_.get(), data, size); } - /** Copy-construct */ + /** + * Copy-construct + */ Buffer(Buffer const& other) : Buffer(other.p_.get(), other.size_) { } - /** Copy assign */ + /** + * Copy assign + */ Buffer& operator=(Buffer const& other) { @@ -59,17 +67,19 @@ public: return *this; } - /** Move-construct. - The other buffer is reset. - */ + /** + * Move-construct. + * The other buffer is reset. + */ Buffer(Buffer&& other) noexcept : p_(std::move(other.p_)), size_(other.size_) { other.size_ = 0; } - /** Move-assign. - The other buffer is reset. - */ + /** + * Move-assign. + * The other buffer is reset. + */ Buffer& operator=(Buffer&& other) noexcept { @@ -82,12 +92,16 @@ public: return *this; } - /** Construct from a slice */ + /** + * Construct from a slice + */ explicit Buffer(Slice s) : Buffer(s.data(), s.size()) { } - /** Assign from slice */ + /** + * Assign from slice + */ Buffer& operator=(Slice s) { @@ -101,7 +115,9 @@ public: return *this; } - /** Returns the number of bytes in the buffer. */ + /** + * Returns the number of bytes in the buffer. + */ [[nodiscard]] std::size_t size() const noexcept { @@ -121,10 +137,11 @@ public: return Slice{p_.get(), size_}; } - /** Return a pointer to beginning of the storage. - @note The return type is guaranteed to be a pointer - to a single byte, to facilitate pointer arithmetic. - */ + /** + * Return a pointer to beginning of the storage. + * @note The return type is guaranteed to be a pointer + * to a single byte, to facilitate pointer arithmetic. + */ /** @{ */ [[nodiscard]] std::uint8_t const* data() const noexcept @@ -139,9 +156,10 @@ public: } /** @} */ - /** Reset the buffer. - All memory is deallocated. The resulting size is 0. - */ + /** + * Reset the buffer. + * All memory is deallocated. The resulting size is 0. + */ void clear() noexcept { @@ -149,9 +167,10 @@ public: size_ = 0; } - /** Reallocate the storage. - Existing data, if any, is discarded. - */ + /** + * Reallocate the storage. + * Existing data, if any, is discarded. + */ std::uint8_t* alloc(std::size_t n) { diff --git a/include/xrpl/basics/CompressionAlgorithms.h b/include/xrpl/basics/CompressionAlgorithms.h index a5ec8645b6..316acb14ac 100644 --- a/include/xrpl/basics/CompressionAlgorithms.h +++ b/include/xrpl/basics/CompressionAlgorithms.h @@ -12,7 +12,8 @@ namespace xrpl::compression_algorithms { -/** LZ4 block compression. +/** + * LZ4 block compression. * @tparam BufferFactory Callable object or lambda. * Takes the requested buffer size and returns allocated buffer pointer. * @param in Data to compress @@ -80,7 +81,8 @@ lz4Decompress( return decompressedSize; } -/** LZ4 block decompression. +/** + * LZ4 block decompression. * @tparam InputStream ZeroCopyInputStream * @param in Input source stream * @param inSize Size of compressed data diff --git a/include/xrpl/basics/CountedObject.h b/include/xrpl/basics/CountedObject.h index 275894673e..bb7b0d8877 100644 --- a/include/xrpl/basics/CountedObject.h +++ b/include/xrpl/basics/CountedObject.h @@ -9,7 +9,9 @@ namespace xrpl { -/** Manages all counted object types. */ +/** + * Manages all counted object types. + */ class CountedObjects { public: @@ -23,10 +25,11 @@ public: getCounts(int minimumThreshold) const; public: - /** Implementation for @ref CountedObject. - - @internal - */ + /** + * Implementation for @ref CountedObject. + * + * @internal + */ class Counter { public: @@ -94,13 +97,14 @@ private: //------------------------------------------------------------------------------ -/** Tracks the number of instances of an object. - - Derived classes have their instances counted automatically. This is used - for reporting purposes. - - @ingroup basics -*/ +/** + * Tracks the number of instances of an object. + * + * Derived classes have their instances counted automatically. This is used + * for reporting purposes. + * + * @ingroup basics + */ template class CountedObject { diff --git a/include/xrpl/basics/DecayingSample.h b/include/xrpl/basics/DecayingSample.h index 86a8baa62e..1b05770734 100644 --- a/include/xrpl/basics/DecayingSample.h +++ b/include/xrpl/basics/DecayingSample.h @@ -6,9 +6,10 @@ namespace xrpl { -/** Sampling function using exponential decay to provide a continuous value. - @tparam The number of seconds in the decay window. -*/ +/** + * Sampling function using exponential decay to provide a continuous value. + * @tparam The number of seconds in the decay window. + */ template class DecayingSample { @@ -19,15 +20,16 @@ public: DecayingSample() = delete; /** - @param now Start time of DecayingSample. - */ + * @param now Start time of DecayingSample. + */ explicit DecayingSample(time_point now) : value_(value_type()), when_(now) { } - /** Add a new sample. - The value is first aged according to the specified time. - */ + /** + * Add a new sample. + * The value is first aged according to the specified time. + */ value_type add(value_type value, time_point now) { @@ -36,9 +38,10 @@ public: return value_ / Window; } - /** Retrieve the current value in normalized units. - The samples are first aged according to the specified time. - */ + /** + * Retrieve the current value in normalized units. + * The samples are first aged according to the specified time. + */ value_type value(time_point now) { @@ -87,9 +90,10 @@ private: //------------------------------------------------------------------------------ -/** Sampling function using exponential decay to provide a continuous value. - @tparam HalfLife The half life of a sample, in seconds. -*/ +/** + * Sampling function using exponential decay to provide a continuous value. + * @tparam HalfLife The half life of a sample, in seconds. + */ template class DecayWindow { diff --git a/include/xrpl/basics/IntrusivePointer.h b/include/xrpl/basics/IntrusivePointer.h index c23d6afb85..59853ad4d0 100644 --- a/include/xrpl/basics/IntrusivePointer.h +++ b/include/xrpl/basics/IntrusivePointer.h @@ -10,33 +10,37 @@ namespace xrpl { //------------------------------------------------------------------------------ -/** Tag to create an intrusive pointer from another intrusive pointer by using a - static cast. This is useful to create an intrusive pointer to a derived - class from an intrusive pointer to a base class. -*/ +/** + * Tag to create an intrusive pointer from another intrusive pointer by using a + * static cast. This is useful to create an intrusive pointer to a derived + * class from an intrusive pointer to a base class. + */ struct StaticCastTagSharedIntrusive { }; -/** Tag to create an intrusive pointer from another intrusive pointer by using a - dynamic cast. This is useful to create an intrusive pointer to a derived - class from an intrusive pointer to a base class. If the cast fails an empty - (null) intrusive pointer is created. -*/ +/** + * Tag to create an intrusive pointer from another intrusive pointer by using a + * dynamic cast. This is useful to create an intrusive pointer to a derived + * class from an intrusive pointer to a base class. If the cast fails an empty + * (null) intrusive pointer is created. + */ struct DynamicCastTagSharedIntrusive { }; -/** When creating or adopting a raw pointer, controls whether the strong count - is incremented or not. Use this tag to increment the strong count. -*/ +/** + * When creating or adopting a raw pointer, controls whether the strong count + * is incremented or not. Use this tag to increment the strong count. + */ struct SharedIntrusiveAdoptIncrementStrongTag { }; -/** When creating or adopting a raw pointer, controls whether the strong count - is incremented or not. Use this tag to leave the strong count unchanged. -*/ +/** + * When creating or adopting a raw pointer, controls whether the strong count + * is incremented or not. Use this tag to leave the strong count unchanged. + */ struct SharedIntrusiveAdoptNoIncrementTag { }; @@ -50,20 +54,21 @@ concept CAdoptTag = std::is_same_v || //------------------------------------------------------------------------------ -/** A shared intrusive pointer class that supports weak pointers. - - This is meant to be used for SHAMapInnerNodes, but may be useful for other - cases. Since the reference counts are stored on the pointee, the pointee is - not destroyed until both the strong _and_ weak pointer counts go to zero. - When the strong pointer count goes to zero, the "partialDestructor" is - called. This can be used to destroy as much of the object as possible while - still retaining the reference counts. For example, for SHAMapInnerNodes the - children may be reset in that function. Note that std::shared_pointer WILL - run the destructor when the strong count reaches zero, but may not free the - memory used by the object until the weak count reaches zero. In xrpld, we - typically allocate shared pointers with the `make_shared` function. When - that is used, the memory is not reclaimed until the weak count reaches zero. -*/ +/** + * A shared intrusive pointer class that supports weak pointers. + * + * This is meant to be used for SHAMapInnerNodes, but may be useful for other + * cases. Since the reference counts are stored on the pointee, the pointee is + * not destroyed until both the strong _and_ weak pointer counts go to zero. + * When the strong pointer count goes to zero, the "partialDestructor" is + * called. This can be used to destroy as much of the object as possible while + * still retaining the reference counts. For example, for SHAMapInnerNodes the + * children may be reset in that function. Note that std::shared_pointer WILL + * run the destructor when the strong count reaches zero, but may not free the + * memory used by the object until the weak count reaches zero. In xrpld, we + * typically allocate shared pointers with the `make_shared` function. When + * that is used, the memory is not reclaimed until the weak count reaches zero. + */ template class SharedIntrusive { @@ -111,8 +116,9 @@ public: operator=( SharedIntrusive&& rhs); // NOLINT(cppcoreguidelines-rvalue-reference-param-not-moved) - /** Adopt the raw pointer. The strong reference may or may not be - incremented, depending on the TAdoptTag + /** + * Adopt the raw pointer. The strong reference may or may not be + * incremented, depending on the TAdoptTag */ template void @@ -120,27 +126,31 @@ public: ~SharedIntrusive(); - /** Create a new SharedIntrusive by statically casting the pointer - controlled by the rhs param. - */ + /** + * Create a new SharedIntrusive by statically casting the pointer + * controlled by the rhs param. + */ template SharedIntrusive(StaticCastTagSharedIntrusive, SharedIntrusive const& rhs); - /** Create a new SharedIntrusive by statically casting the pointer - controlled by the rhs param. - */ + /** + * Create a new SharedIntrusive by statically casting the pointer + * controlled by the rhs param. + */ template SharedIntrusive(StaticCastTagSharedIntrusive, SharedIntrusive&& rhs); - /** Create a new SharedIntrusive by dynamically casting the pointer - controlled by the rhs param. - */ + /** + * Create a new SharedIntrusive by dynamically casting the pointer + * controlled by the rhs param. + */ template SharedIntrusive(DynamicCastTagSharedIntrusive, SharedIntrusive const& rhs); - /** Create a new SharedIntrusive by dynamically casting the pointer - controlled by the rhs param. - */ + /** + * Create a new SharedIntrusive by dynamically casting the pointer + * controlled by the rhs param. + */ template SharedIntrusive(DynamicCastTagSharedIntrusive, SharedIntrusive&& rhs); @@ -153,17 +163,22 @@ public: explicit operator bool() const noexcept; - /** Set the pointer to null, decrement the strong count, and run the - appropriate release action. - */ + /** + * Set the pointer to null, decrement the strong count, and run the + * appropriate release action. + */ void reset(); - /** Get the raw pointer */ + /** + * Get the raw pointer + */ [[nodiscard]] T* get() const; - /** Return the strong count */ + /** + * Return the strong count + */ [[nodiscard]] std::size_t useCount() const; @@ -181,43 +196,51 @@ public: friend class WeakIntrusive; private: - /** Return the raw pointer held by this object. */ + /** + * Return the raw pointer held by this object. + */ [[nodiscard]] T* unsafeGetRawPtr() const; - /** Exchange the current raw pointer held by this object with the given - pointer. Decrement the strong count of the raw pointer previously held - by this object and run the appropriate release action. + /** + * Exchange the current raw pointer held by this object with the given + * pointer. Decrement the strong count of the raw pointer previously held + * by this object and run the appropriate release action. */ void unsafeReleaseAndStore(T* next); - /** Set the raw pointer directly. This is wrapped in a function so the class - can support both atomic and non-atomic pointers in a future patch. + /** + * Set the raw pointer directly. This is wrapped in a function so the class + * can support both atomic and non-atomic pointers in a future patch. */ void unsafeSetRawPtr(T* p); - /** Exchange the raw pointer directly. - This sets the raw pointer to the given value and returns the previous - value. This is wrapped in a function so the class can support both - atomic and non-atomic pointers in a future patch. + /** + * Exchange the raw pointer directly. + * This sets the raw pointer to the given value and returns the previous + * value. This is wrapped in a function so the class can support both + * atomic and non-atomic pointers in a future patch. */ T* unsafeExchange(T* p); - /** pointer to the type with an intrusive count */ + /** + * pointer to the type with an intrusive count + */ T* ptr_{nullptr}; }; //------------------------------------------------------------------------------ -/** A weak intrusive pointer class for the SharedIntrusive pointer class. - -Note that this weak pointer class asks differently from normal weak pointer -classes. When the strong pointer count goes to zero, the "partialDestructor" -is called. See the comment on SharedIntrusive for a fuller explanation. -*/ +/** + * A weak intrusive pointer class for the SharedIntrusive pointer class. + * + * Note that this weak pointer class asks differently from normal weak pointer + * classes. When the strong pointer count goes to zero, the "partialDestructor" + * is called. See the comment on SharedIntrusive for a fuller explanation. + */ template class WeakIntrusive { @@ -247,54 +270,62 @@ public: WeakIntrusive& operator=(SharedIntrusive const& rhs); - /** Adopt the raw pointer and increment the weak count. */ + /** + * Adopt the raw pointer and increment the weak count. + */ void adopt(T* ptr); ~WeakIntrusive(); - /** Get a strong pointer from the weak pointer, if possible. This will - only return a seated pointer if the strong count on the raw pointer - is non-zero before locking. + /** + * Get a strong pointer from the weak pointer, if possible. This will + * only return a seated pointer if the strong count on the raw pointer + * is non-zero before locking. */ SharedIntrusive lock() const; - /** Return true if the strong count is zero. */ + /** + * Return true if the strong count is zero. + */ [[nodiscard]] bool expired() const; - /** Set the pointer to null and decrement the weak count. - - Note: This may run the destructor if the strong count is zero. - */ + /** + * Set the pointer to null and decrement the weak count. + * + * Note: This may run the destructor if the strong count is zero. + */ void reset(); private: T* ptr_ = nullptr; - /** Decrement the weak count. This does _not_ set the raw pointer to - null. - - Note: This may run the destructor if the strong count is zero. - */ + /** + * Decrement the weak count. This does _not_ set the raw pointer to + * null. + * + * Note: This may run the destructor if the strong count is zero. + */ void unsafeReleaseNoStore(); }; //------------------------------------------------------------------------------ -/** A combination of a strong and a weak intrusive pointer stored in the - space of a single pointer. - - This class is similar to a `std::variant` - with some optimizations. In particular, it uses a low-order bit to - determine if the raw pointer represents a strong pointer or a weak - pointer. It can also be quickly switched between its strong pointer and - weak pointer representations. This class is useful for storing intrusive - pointers in tagged caches. - */ +/** + * A combination of a strong and a weak intrusive pointer stored in the + * space of a single pointer. + * + * This class is similar to a `std::variant` + * with some optimizations. In particular, it uses a low-order bit to + * determine if the raw pointer represents a strong pointer or a weak + * pointer. It can also be quickly switched between its strong pointer and + * weak pointer representations. This class is useful for storing intrusive + * pointers in tagged caches. + */ template class SharedWeakUnion @@ -336,69 +367,83 @@ public: ~SharedWeakUnion(); - /** Return a strong pointer if this is already a strong pointer (i.e. - don't lock the weak pointer. Use the `lock` method if that's what's - needed) + /** + * Return a strong pointer if this is already a strong pointer (i.e. + * don't lock the weak pointer. Use the `lock` method if that's what's + * needed) */ [[nodiscard]] SharedIntrusive getStrong() const; - /** Return true if this is a strong pointer and the strong pointer is - seated. + /** + * Return true if this is a strong pointer and the strong pointer is + * seated. */ explicit operator bool() const noexcept; - /** Set the pointer to null, decrement the appropriate ref count, and - run the appropriate release action. + /** + * Set the pointer to null, decrement the appropriate ref count, and + * run the appropriate release action. */ void reset(); - /** If this is a strong pointer, return the raw pointer. Otherwise - return null. + /** + * If this is a strong pointer, return the raw pointer. Otherwise + * return null. */ [[nodiscard]] T* get() const; - /** If this is a strong pointer, return the strong count. Otherwise + /** + * If this is a strong pointer, return the strong count. Otherwise * return 0 */ [[nodiscard]] std::size_t useCount() const; - /** Return true if there is a non-zero strong count. */ + /** + * Return true if there is a non-zero strong count. + */ [[nodiscard]] bool expired() const; - /** If this is a strong pointer, return the strong pointer. Otherwise - attempt to lock the weak pointer. + /** + * If this is a strong pointer, return the strong pointer. Otherwise + * attempt to lock the weak pointer. */ [[nodiscard]] SharedIntrusive lock() const; - /** Return true is this represents a strong pointer. */ + /** + * Return true is this represents a strong pointer. + */ [[nodiscard]] bool isStrong() const; - /** Return true is this represents a weak pointer. */ + /** + * Return true is this represents a weak pointer. + */ [[nodiscard]] bool isWeak() const; - /** If this is a weak pointer, attempt to convert it to a strong - pointer. - - @return true if successfully converted to a strong pointer (or was - already a strong pointer). Otherwise false. - */ + /** + * If this is a weak pointer, attempt to convert it to a strong + * pointer. + * + * @return true if successfully converted to a strong pointer (or was + * already a strong pointer). Otherwise false. + */ bool convertToStrong(); - /** If this is a strong pointer, attempt to convert it to a weak - pointer. - - @return false if the pointer is null. Otherwise return true. - */ + /** + * If this is a strong pointer, attempt to convert it to a weak + * pointer. + * + * @return false if the pointer is null. Otherwise return true. + */ bool convertToWeak(); @@ -411,23 +456,27 @@ private: static constexpr std::uintptr_t kPtrMask = ~kTagMask; private: - /** Return the raw pointer held by this object. + /** + * Return the raw pointer held by this object. */ [[nodiscard]] T* unsafeGetRawPtr() const; enum class RefStrength { Strong, Weak }; - /** Set the raw pointer and tag bit directly. + /** + * Set the raw pointer and tag bit directly. */ void unsafeSetRawPtr(T* p, RefStrength rs); - /** Set the raw pointer and tag bit to all zeros (strong null pointer). + /** + * Set the raw pointer and tag bit to all zeros (strong null pointer). */ void unsafeSetRawPtr(std::nullptr_t); - /** Decrement the appropriate ref count, and run the appropriate release - action. Note: this does _not_ set the raw pointer to null. + /** + * Decrement the appropriate ref count, and run the appropriate release + * action. Note: this does _not_ set the raw pointer to null. */ void unsafeReleaseNoStore(); @@ -435,12 +484,13 @@ private: //------------------------------------------------------------------------------ -/** Create a shared intrusive pointer. - - Note: unlike std::shared_ptr, where there is an advantage of allocating - the pointer and control block together, there is no benefit for intrusive - pointers. -*/ +/** + * Create a shared intrusive pointer. + * + * Note: unlike std::shared_ptr, where there is an advantage of allocating + * the pointer and control block together, there is no benefit for intrusive + * pointers. + */ template SharedIntrusive makeSharedIntrusive(Args&&... args) diff --git a/include/xrpl/basics/IntrusiveRefCounts.h b/include/xrpl/basics/IntrusiveRefCounts.h index 5eb1422541..caa06ed786 100644 --- a/include/xrpl/basics/IntrusiveRefCounts.h +++ b/include/xrpl/basics/IntrusiveRefCounts.h @@ -8,35 +8,38 @@ namespace xrpl { -/** Action to perform when releasing a strong pointer. - - noop: Do nothing. For example, a `noop` action will occur when a count is - decremented to a non-zero value. - - partialDestroy: Run the `partialDestructor`. This action will happen when a - strong count is decremented to zero and the weak count is non-zero. - - destroy: Run the destructor. This action will occur when either the strong - count or weak count is decremented and the other count is also zero. +/** + * Action to perform when releasing a strong pointer. + * + * noop: Do nothing. For example, a `noop` action will occur when a count is + * decremented to a non-zero value. + * + * partialDestroy: Run the `partialDestructor`. This action will happen when a + * strong count is decremented to zero and the weak count is non-zero. + * + * destroy: Run the destructor. This action will occur when either the strong + * count or weak count is decremented and the other count is also zero. */ enum class ReleaseStrongRefAction { NoOp, PartialDestroy, Destroy }; -/** Action to perform when releasing a weak pointer. - - noop: Do nothing. For example, a `noop` action will occur when a count is - decremented to a non-zero value. - - destroy: Run the destructor. This action will occur when either the strong - count or weak count is decremented and the other count is also zero. +/** + * Action to perform when releasing a weak pointer. + * + * noop: Do nothing. For example, a `noop` action will occur when a count is + * decremented to a non-zero value. + * + * destroy: Run the destructor. This action will occur when either the strong + * count or weak count is decremented and the other count is also zero. */ enum class ReleaseWeakRefAction { NoOp, Destroy }; -/** Implement the strong count, weak count, and bit flags for an intrusive - pointer. - - A class can satisfy the requirements of an xrpl::IntrusivePointer by - inheriting from this class. - */ +/** + * Implement the strong count, weak count, and bit flags for an intrusive + * pointer. + * + * A class can satisfy the requirements of an xrpl::IntrusivePointer by + * inheriting from this class. + */ struct IntrusiveRefCounts { virtual ~IntrusiveRefCounts() noexcept; @@ -105,109 +108,123 @@ private: static constexpr size_t kFieldTypeBits = sizeof(FieldType) * 8; static constexpr FieldType kOne = 1; - /** `refCounts` consists of four fields that are treated atomically: - - 1. Strong count. This is a count of the number of shared pointers that - hold a reference to this object. When the strong counts goes to zero, - if the weak count is zero, the destructor is run. If the weak count is - non-zero when the strong count goes to zero then the partialDestructor - is run. - - 2. Weak count. This is a count of the number of weak pointer that hold - a reference to this object. When the weak count goes to zero and the - strong count is also zero, then the destructor is run. - - 3. Partial destroy started bit. This bit is set if the - `partialDestructor` function has been started (or is about to be - started). This is used to prevent the destructor from running - concurrently with the partial destructor. This can easily happen when - the last strong pointer release its reference in one thread and starts - the partialDestructor, while in another thread the last weak pointer - goes out of scope and starts the destructor while the partialDestructor - is still running. Both a start and finished bit is needed to handle a - corner-case where the last strong pointer goes out of scope, then then - last `weakPointer` goes out of scope, but this happens before the - `partialDestructor` bit is set. It would be possible to use a single - bit if it could also be set atomically when the strong count goes to - zero and the weak count is non-zero, but that would add complexity (and - likely slow down common cases as well). - - 4. Partial destroy finished bit. This bit is set when the - `partialDestructor` has finished running. See (3) above for more - information. - - */ + /** + * `refCounts` consists of four fields that are treated atomically: + * + * 1. Strong count. This is a count of the number of shared pointers that + * hold a reference to this object. When the strong counts goes to zero, + * if the weak count is zero, the destructor is run. If the weak count is + * non-zero when the strong count goes to zero then the partialDestructor + * is run. + * + * 2. Weak count. This is a count of the number of weak pointer that hold + * a reference to this object. When the weak count goes to zero and the + * strong count is also zero, then the destructor is run. + * + * 3. Partial destroy started bit. This bit is set if the + * `partialDestructor` function has been started (or is about to be + * started). This is used to prevent the destructor from running + * concurrently with the partial destructor. This can easily happen when + * the last strong pointer release its reference in one thread and starts + * the partialDestructor, while in another thread the last weak pointer + * goes out of scope and starts the destructor while the partialDestructor + * is still running. Both a start and finished bit is needed to handle a + * corner-case where the last strong pointer goes out of scope, then then + * last `weakPointer` goes out of scope, but this happens before the + * `partialDestructor` bit is set. It would be possible to use a single + * bit if it could also be set atomically when the strong count goes to + * zero and the weak count is non-zero, but that would add complexity (and + * likely slow down common cases as well). + * + * 4. Partial destroy finished bit. This bit is set when the + * `partialDestructor` has finished running. See (3) above for more + * information. + */ mutable std::atomic refCounts_{kStrongDelta}; - /** Amount to change the strong count when adding or releasing a reference - - Note: The strong count is stored in the low `StrongCountNumBits` bits - of refCounts - */ + /** + * Amount to change the strong count when adding or releasing a reference + * + * Note: The strong count is stored in the low `StrongCountNumBits` bits + * of refCounts + */ static constexpr FieldType kStrongDelta = 1; - /** Amount to change the weak count when adding or releasing a reference - - Note: The weak count is stored in the high `WeakCountNumBits` bits of - refCounts - */ + /** + * Amount to change the weak count when adding or releasing a reference + * + * Note: The weak count is stored in the high `WeakCountNumBits` bits of + * refCounts + */ static constexpr FieldType kWeakDelta = (kOne << kStrongCountNumBits); - /** Flag that is set when the partialDestroy function has started running - (or is about to start running). - - See description of the `refCounts` field for a fuller description of - this field. - */ + /** + * Flag that is set when the partialDestroy function has started running + * (or is about to start running). + * + * See description of the `refCounts` field for a fuller description of + * this field. + */ static constexpr FieldType kPartialDestroyStartedMask = (kOne << (kFieldTypeBits - 1)); - /** Flag that is set when the partialDestroy function has finished running - - See description of the `refCounts` field for a fuller description of - this field. - */ + /** + * Flag that is set when the partialDestroy function has finished running + * + * See description of the `refCounts` field for a fuller description of + * this field. + */ static constexpr FieldType kPartialDestroyFinishedMask = (kOne << (kFieldTypeBits - 2)); - /** Mask that will zero out all the `count` bits and leave the tag bits - unchanged. - */ + /** + * Mask that will zero out all the `count` bits and leave the tag bits + * unchanged. + */ static constexpr FieldType kTagMask = kPartialDestroyStartedMask | kPartialDestroyFinishedMask; - /** Mask that will zero out the `tag` bits and leave the count bits - unchanged. - */ + /** + * Mask that will zero out the `tag` bits and leave the count bits + * unchanged. + */ static constexpr FieldType kValueMask = ~kTagMask; - /** Mask that will zero out everything except the strong count. + /** + * Mask that will zero out everything except the strong count. */ static constexpr FieldType kStrongMask = ((kOne << kStrongCountNumBits) - 1) & kValueMask; - /** Mask that will zero out everything except the weak count. + /** + * Mask that will zero out everything except the weak count. */ static constexpr FieldType kWeakMask = (((kOne << kWeakCountNumBits) - 1) << kStrongCountNumBits) & kValueMask; - /** Unpack the count and tag fields from the packed atomic integer form. */ + /** + * Unpack the count and tag fields from the packed atomic integer form. + */ struct RefCountPair { CountType strong; CountType weak; - /** The `partialDestroyStartedBit` is set to on when the partial - destroy function is started. It is not a boolean; it is a uint32 - with all bits zero with the possible exception of the - `partialDestroyStartedMask` bit. This is done so it can be directly - masked into the `combinedValue`. + /** + * The `partialDestroyStartedBit` is set to on when the partial + * destroy function is started. It is not a boolean; it is a uint32 + * with all bits zero with the possible exception of the + * `partialDestroyStartedMask` bit. This is done so it can be directly + * masked into the `combinedValue`. */ FieldType partialDestroyStartedBit{0}; - /** The `partialDestroyFinishedBit` is set to on when the partial - destroy function has finished. + /** + * The `partialDestroyFinishedBit` is set to on when the partial + * destroy function has finished. */ FieldType partialDestroyFinishedBit{0}; RefCountPair(FieldType v) noexcept; RefCountPair(CountType s, CountType w) noexcept; - /** Convert back to the packed integer form. */ + /** + * Convert back to the packed integer form. + */ [[nodiscard]] FieldType combinedValue() const noexcept; @@ -215,9 +232,10 @@ private: static_cast((kOne << kStrongCountNumBits) - 1); static constexpr CountType kMaxWeakValue = static_cast((kOne << kWeakCountNumBits) - 1); - /** Put an extra margin to detect when running up against limits. - This is only used in debug code, and is useful if we reduce the - number of bits in the strong and weak counts (to 16 and 14 bits). + /** + * Put an extra margin to detect when running up against limits. + * This is only used in debug code, and is useful if we reduce the + * number of bits in the strong and weak counts (to 16 and 14 bits). */ static constexpr CountType kCheckStrongMaxValue = kMaxStrongValue - 32; static constexpr CountType kCheckWeakMaxValue = kMaxWeakValue - 32; diff --git a/include/xrpl/basics/LocalValue.h b/include/xrpl/basics/LocalValue.h index 1c2a657a18..c5e544a343 100644 --- a/include/xrpl/basics/LocalValue.h +++ b/include/xrpl/basics/LocalValue.h @@ -70,11 +70,15 @@ public: { } - /** Stores instance of T specific to the calling coroutine or thread. */ + /** + * Stores instance of T specific to the calling coroutine or thread. + */ T& operator*(); - /** Stores instance of T specific to the calling coroutine or thread. */ + /** + * Stores instance of T specific to the calling coroutine or thread. + */ T* operator->() { diff --git a/include/xrpl/basics/Log.h b/include/xrpl/basics/Log.h index 4e3437fe71..945dc1b4ec 100644 --- a/include/xrpl/basics/Log.h +++ b/include/xrpl/basics/Log.h @@ -16,7 +16,9 @@ namespace xrpl { -/** Manages partitions for logging. */ +/** + * Manages partitions for logging. + */ class Logs { private: @@ -40,69 +42,81 @@ private: writeAlways(beast::Severity level, std::string const& text) override; }; - /** Manages a system file containing logged output. - The system file remains open during program execution. Interfaces - are provided for interoperating with standard log management - tools like logrotate(8): - http://linuxcommand.org/man_pages/logrotate8.html - @note None of the listed interfaces are thread-safe. - */ + /** + * Manages a system file containing logged output. + * The system file remains open during program execution. Interfaces + * are provided for interoperating with standard log management + * tools like logrotate(8): + * http://linuxcommand.org/man_pages/logrotate8.html + * @note None of the listed interfaces are thread-safe. + */ class File { public: - /** Construct with no associated system file. - A system file may be associated later with @ref open. - @see open - */ + /** + * Construct with no associated system file. + * A system file may be associated later with @ref open. + * @see open + */ File(); - /** Destroy the object. - If a system file is associated, it will be flushed and closed. - */ + /** + * Destroy the object. + * If a system file is associated, it will be flushed and closed. + */ ~File() = default; - /** Determine if a system file is associated with the log. - @return `true` if a system file is associated and opened for - writing. - */ + /** + * Determine if a system file is associated with the log. + * @return `true` if a system file is associated and opened for + * writing. + */ [[nodiscard]] bool isOpen() const noexcept; - /** Associate a system file with the log. - If the file does not exist an attempt is made to create it - and open it for writing. If the file already exists an attempt is - made to open it for appending. - If a system file is already associated with the log, it is closed - first. - @return `true` if the file was opened. - */ + /** + * Associate a system file with the log. + * If the file does not exist an attempt is made to create it + * and open it for writing. If the file already exists an attempt is + * made to open it for appending. + * If a system file is already associated with the log, it is closed + * first. + * @return `true` if the file was opened. + */ bool open(boost::filesystem::path const& path); - /** Close and re-open the system file associated with the log - This assists in interoperating with external log management tools. - @return `true` if the file was opened. - */ + /** + * Close and re-open the system file associated with the log + * This assists in interoperating with external log management tools. + * @return `true` if the file was opened. + */ bool closeAndReopen(); - /** Close the system file if it is open. */ + /** + * Close the system file if it is open. + */ void close(); - /** write to the log file. - Does nothing if there is no associated system file. - */ + /** + * write to the log file. + * Does nothing if there is no associated system file. + */ void write(char const* text); - /** write to the log file and append an end of line marker. - Does nothing if there is no associated system file. - */ + /** + * write to the log file and append an end of line marker. + * Does nothing if there is no associated system file. + */ void writeln(char const* text); - /** Write to the log file using std::string. */ + /** + * Write to the log file using std::string. + */ /** @{ */ void write(std::string const& str) @@ -223,19 +237,21 @@ private: //------------------------------------------------------------------------------ // Debug logging: -/** Set the sink for the debug journal. - - @param sink unique_ptr to new debug Sink. - @return unique_ptr to the previous Sink. nullptr if there was no Sink. -*/ +/** + * Set the sink for the debug journal. + * + * @param sink unique_ptr to new debug Sink. + * @return unique_ptr to the previous Sink. nullptr if there was no Sink. + */ std::unique_ptr setDebugLogSink(std::unique_ptr sink); -/** Returns a debug journal. - The journal may drain to a null sink, so its output - may never be seen. Never use it for critical - information. -*/ +/** + * Returns a debug journal. + * The journal may drain to a null sink, so its output + * may never be seen. Never use it for critical + * information. + */ beast::Journal debugLog(); diff --git a/include/xrpl/basics/MathUtilities.h b/include/xrpl/basics/MathUtilities.h index 4552b335e1..78f5c76988 100644 --- a/include/xrpl/basics/MathUtilities.h +++ b/include/xrpl/basics/MathUtilities.h @@ -6,7 +6,8 @@ namespace xrpl { -/** Calculate one number divided by another number in percentage. +/** + * Calculate one number divided by another number in percentage. * The result is rounded up to the next integer, and capped in the range [0,100] * E.g. calculatePercent(1, 100) = 1 because 1/100 = 0.010000 * calculatePercent(1, 99) = 2 because 1/99 = 0.010101 @@ -19,7 +20,7 @@ namespace xrpl { * @return the percentage, in [0, 100] * * @note total cannot be zero. - * */ + */ constexpr std::size_t calculatePercent(std::size_t count, std::size_t total) { diff --git a/include/xrpl/basics/Number.h b/include/xrpl/basics/Number.h index 28c7b1dbda..0026e7f006 100644 --- a/include/xrpl/basics/Number.h +++ b/include/xrpl/basics/Number.h @@ -47,7 +47,8 @@ isPowerOfTen(T value) namespace detail { -/** Builds a table of the powers of 10 +/** + * Builds a table of the powers of 10 * * This function is marked consteval, so it can only be run in * a constexpr context. This assures that it is and can only be run at @@ -92,7 +93,8 @@ static_assert(kPowerOfTen[10] == 10'000'000'000); static_assert( isPowerOfTen(kPowerOfTen.back()) && *logTen(kPowerOfTen.back()) == detail::kUint64Digits - 1); -/** MantissaRange defines a range for the mantissa of a normalized Number. +/** + * MantissaRange defines a range for the mantissa of a normalized Number. * * The mantissa is in the range [min, max], where * * min is a power of 10, and @@ -247,7 +249,8 @@ private: template concept Integral64 = std::is_same_v || std::is_same_v; -/** Number is a floating point type that can represent a wide range of values. +/** + * Number is a floating point type that can represent a wide range of values. * * It can represent all values that can be represented by an STAmount - * regardless of asset type - XRPAmount, MPTAmount, and IOUAmount, with at least @@ -343,7 +346,6 @@ concept Integral64 = std::is_same_v || std::is_same_v(m); } -/** Returns the exponent of the external view of the Number. +/** + * Returns the exponent of the external view of the Number. * * Please see the "---- External Interface ----" section of the class * documentation for an explanation of why the internal value may be modified. @@ -948,10 +958,10 @@ public: operator=(NumberRoundModeGuard const&) = delete; }; -/** Sets the new scale and restores the old scale when it leaves scope. +/** + * Sets the new scale and restores the old scale when it leaves scope. * * If you think you need to use this class outside of unit tests, no you don't. - * */ class NumberMantissaScaleGuard { diff --git a/include/xrpl/basics/RangeSet.h b/include/xrpl/basics/RangeSet.h index 2ed543b376..3de882979e 100644 --- a/include/xrpl/basics/RangeSet.h +++ b/include/xrpl/basics/RangeSet.h @@ -13,23 +13,25 @@ namespace xrpl { -/** A closed interval over the domain T. - - For an instance ClosedInterval c, this represents the closed interval - (c.first(), c.last()). A single element interval has c.first() == c.last(). - - This is simply a type-alias for boost interval container library interval - set, so users should consult that documentation for available supporting - member and free functions. -*/ +/** + * A closed interval over the domain T. + * + * For an instance ClosedInterval c, this represents the closed interval + * (c.first(), c.last()). A single element interval has c.first() == c.last(). + * + * This is simply a type-alias for boost interval container library interval + * set, so users should consult that documentation for available supporting + * member and free functions. + */ template using ClosedInterval = boost::icl::closed_interval; -/** Create a closed range interval - - Helper function to create a closed range interval without having to qualify - the template argument. -*/ +/** + * Create a closed range interval + * + * Helper function to create a closed range interval without having to qualify + * the template argument. + */ template ClosedInterval range(T low, T high) @@ -37,28 +39,30 @@ range(T low, T high) return ClosedInterval(low, high); } -/** A set of closed intervals over the domain T. - - Represents a set of values of the domain T using the minimum number - of disjoint ClosedInterval. This is useful to represent ranges of - T where a few instances are missing, e.g. the set 1-5,8-9,11-14. - - This is simply a type-alias for boost interval container library interval - set, so users should consult that documentation for available supporting - member and free functions. -*/ +/** + * A set of closed intervals over the domain T. + * + * Represents a set of values of the domain T using the minimum number + * of disjoint ClosedInterval. This is useful to represent ranges of + * T where a few instances are missing, e.g. the set 1-5,8-9,11-14. + * + * This is simply a type-alias for boost interval container library interval + * set, so users should consult that documentation for available supporting + * member and free functions. + */ template using RangeSet = boost::icl::interval_set>; -/** Convert a ClosedInterval to a styled string - - The styled string is - "c.first()-c.last()" if c.first() != c.last() - "c.first()" if c.first() == c.last() - - @param ci The closed interval to convert - @return The style string -*/ +/** + * Convert a ClosedInterval to a styled string + * + * The styled string is + * "c.first()-c.last()" if c.first() != c.last() + * "c.first()" if c.first() == c.last() + * + * @param ci The closed interval to convert + * @return The style string + */ template std::string to_string(ClosedInterval const& ci) @@ -68,14 +72,15 @@ to_string(ClosedInterval const& ci) return std::to_string(ci.first()) + "-" + std::to_string(ci.last()); } -/** Convert the given RangeSet to a styled string. - - The styled string representation is the set of disjoint intervals joined - by commas. The string "empty" is returned if the set is empty. - - @param rs The rangeset to convert - @return The styled string -*/ +/** + * Convert the given RangeSet to a styled string. + * + * The styled string representation is the set of disjoint intervals joined + * by commas. The string "empty" is returned if the set is empty. + * + * @param rs The rangeset to convert + * @return The styled string + */ template std::string to_string(RangeSet const& rs) @@ -91,15 +96,16 @@ to_string(RangeSet const& rs) return s; } -/** Convert the given styled string to a RangeSet. - - The styled string representation is the set - of disjoint intervals joined by commas. - - @param rs The set to be populated - @param s The styled string to convert - @return True on successfully converting styled string -*/ +/** + * Convert the given styled string to a RangeSet. + * + * The styled string representation is the set + * of disjoint intervals joined by commas. + * + * @param rs The set to be populated + * @param s The styled string to convert + * @return True on successfully converting styled string + */ template [[nodiscard]] bool fromString(RangeSet& rs, std::string const& s) @@ -161,14 +167,15 @@ fromString(RangeSet& rs, std::string const& s) return result; } -/** Find the largest value not in the set that is less than a given value. - - @param rs The set of interest - @param t The value that must be larger than the result - @param minVal (Default is 0) The smallest allowed value - @return The largest v such that minV <= v < t and !contains(rs, v) or - std::nullopt if no such v exists. -*/ +/** + * Find the largest value not in the set that is less than a given value. + * + * @param rs The set of interest + * @param t The value that must be larger than the result + * @param minVal (Default is 0) The smallest allowed value + * @return The largest v such that minV <= v < t and !contains(rs, v) or + * std::nullopt if no such v exists. + */ template std::optional prevMissing(RangeSet const& rs, T t, T minVal = 0) diff --git a/include/xrpl/basics/Resolver.h b/include/xrpl/basics/Resolver.h index d48958b76d..239eb9630e 100644 --- a/include/xrpl/basics/Resolver.h +++ b/include/xrpl/basics/Resolver.h @@ -15,22 +15,29 @@ public: virtual ~Resolver() = 0; - /** Issue an asynchronous stop request. */ + /** + * Issue an asynchronous stop request. + */ virtual void stopAsync() = 0; - /** Issue a synchronous stop request. */ + /** + * Issue a synchronous stop request. + */ virtual void stop() = 0; - /** Issue a synchronous start request. */ + /** + * Issue a synchronous start request. + */ virtual void start() = 0; - /** resolve all hostnames on the list - @param names the names to be resolved - @param handler the handler to call - */ + /** + * resolve all hostnames on the list + * @param names the names to be resolved + * @param handler the handler to call + */ /** @{ */ template void diff --git a/include/xrpl/basics/SharedWeakCachePointer.h b/include/xrpl/basics/SharedWeakCachePointer.h index a143647a1e..1b78af2fae 100644 --- a/include/xrpl/basics/SharedWeakCachePointer.h +++ b/include/xrpl/basics/SharedWeakCachePointer.h @@ -7,13 +7,14 @@ namespace xrpl { -/** A combination of a std::shared_ptr and a std::weak_pointer. - - -This class is a wrapper to a `std::variant` -This class is useful for storing intrusive pointers in tagged caches using less -memory than storing both pointers directly. -*/ +/** + * A combination of a std::shared_ptr and a std::weak_pointer. + * + * + * This class is a wrapper to a `std::variant` + * This class is useful for storing intrusive pointers in tagged caches using less + * memory than storing both pointers directly. + */ template class SharedWeakCachePointer @@ -48,65 +49,79 @@ public: ~SharedWeakCachePointer(); - /** Return a strong pointer if this is already a strong pointer (i.e. don't - lock the weak pointer. Use the `lock` method if that's what's needed) + /** + * Return a strong pointer if this is already a strong pointer (i.e. don't + * lock the weak pointer. Use the `lock` method if that's what's needed) */ [[nodiscard]] std::shared_ptr const& getStrong() const; - /** Return true if this is a strong pointer and the strong pointer is - seated. + /** + * Return true if this is a strong pointer and the strong pointer is + * seated. */ explicit operator bool() const noexcept; - /** Set the pointer to null, decrement the appropriate ref count, and run - the appropriate release action. + /** + * Set the pointer to null, decrement the appropriate ref count, and run + * the appropriate release action. */ void reset(); - /** If this is a strong pointer, return the raw pointer. Otherwise return - null. + /** + * If this is a strong pointer, return the raw pointer. Otherwise return + * null. */ [[nodiscard]] T* get() const; - /** If this is a strong pointer, return the strong count. Otherwise return 0 + /** + * If this is a strong pointer, return the strong count. Otherwise return 0 */ [[nodiscard]] std::size_t useCount() const; - /** Return true if there is a non-zero strong count. */ + /** + * Return true if there is a non-zero strong count. + */ [[nodiscard]] bool expired() const; - /** If this is a strong pointer, return the strong pointer. Otherwise - attempt to lock the weak pointer. + /** + * If this is a strong pointer, return the strong pointer. Otherwise + * attempt to lock the weak pointer. */ [[nodiscard]] std::shared_ptr lock() const; - /** Return true is this represents a strong pointer. */ + /** + * Return true is this represents a strong pointer. + */ [[nodiscard]] bool isStrong() const; - /** Return true is this represents a weak pointer. */ + /** + * Return true is this represents a weak pointer. + */ [[nodiscard]] bool isWeak() const; - /** If this is a weak pointer, attempt to convert it to a strong pointer. - - @return true if successfully converted to a strong pointer (or was - already a strong pointer). Otherwise false. - */ + /** + * If this is a weak pointer, attempt to convert it to a strong pointer. + * + * @return true if successfully converted to a strong pointer (or was + * already a strong pointer). Otherwise false. + */ bool convertToStrong(); - /** If this is a strong pointer, attempt to convert it to a weak pointer. - - @return false if the pointer is null. Otherwise return true. - */ + /** + * If this is a strong pointer, attempt to convert it to a weak pointer. + * + * @return false if the pointer is null. Otherwise return true. + */ bool convertToWeak(); diff --git a/include/xrpl/basics/SlabAllocator.h b/include/xrpl/basics/SlabAllocator.h index 8e741991f6..7b6e88e8bc 100644 --- a/include/xrpl/basics/SlabAllocator.h +++ b/include/xrpl/basics/SlabAllocator.h @@ -33,7 +33,9 @@ class SlabAllocator static_assert(alignof(Type) == 8 || alignof(Type) == 4); - /** A block of memory that is owned by a slab allocator */ + /** + * A block of memory that is owned by a slab allocator + */ struct SlabBlock { // A mutex to protect the freelist for this block: @@ -80,7 +82,9 @@ class SlabAllocator SlabBlock& operator=(SlabBlock&& other) = delete; - /** Determines whether the given pointer belongs to this allocator */ + /** + * Determines whether the given pointer belongs to this allocator + */ bool own(std::uint8_t const* pIn) const noexcept { @@ -107,14 +111,15 @@ class SlabAllocator return ret; } - /** Return an item to this allocator's freelist. - - @param ptr The pointer to the chunk of memory being deallocated. - - @note This is a dangerous, private interface; the item being - returned should belong to this allocator. Debug builds - will check and assert if this is not the case. Release - builds will not. + /** + * Return an item to this allocator's freelist. + * + * @param ptr The pointer to the chunk of memory being deallocated. + * + * @note This is a dangerous, private interface; the item being + * returned should belong to this allocator. Debug builds + * will check and assert if this is not the case. Release + * builds will not. */ void deallocate(std::uint8_t* ptr) noexcept @@ -145,13 +150,14 @@ private: std::size_t const slabSize_; public: - /** Constructs a slab allocator able to allocate objects of a fixed size - - @param count the number of items the slab allocator can allocate; note - that a count of 0 is valid and means that the allocator - is, effectively, disabled. This can be very useful in some - contexts (e.g. when minimal memory usage is needed) and - allows for graceful failure. + /** + * Constructs a slab allocator able to allocate objects of a fixed size + * + * @param count the number of items the slab allocator can allocate; note + * that a count of 0 is valid and means that the allocator + * is, effectively, disabled. This can be very useful in some + * contexts (e.g. when minimal memory usage is needed) and + * allows for graceful failure. */ constexpr explicit SlabAllocator( std::size_t extra, @@ -179,17 +185,20 @@ public: // shutdown process up could make this possible. ~SlabAllocator() = default; - /** Returns the size of the memory block this allocator returns. */ + /** + * Returns the size of the memory block this allocator returns. + */ [[nodiscard]] constexpr std::size_t size() const noexcept { return itemSize_; } - /** Returns a suitably aligned pointer, if one is available. - - @return a pointer to a block of memory from the allocator, or - nullptr if the allocator can't satisfy this request. + /** + * Returns a suitably aligned pointer, if one is available. + * + * @return a pointer to a block of memory from the allocator, or + * nullptr if the allocator can't satisfy this request. */ std::uint8_t* allocate() noexcept @@ -250,12 +259,13 @@ public: return slab->allocate(); } - /** Returns the memory block to the allocator. - - @param ptr A pointer to a memory block. - @param size If non-zero, a hint as to the size of the block. - @return true if this memory block belonged to the allocator and has - been released; false otherwise. + /** + * Returns the memory block to the allocator. + * + * @param ptr A pointer to a memory block. + * @param size If non-zero, a hint as to the size of the block. + * @return true if this memory block belonged to the allocator and has + * been released; false otherwise. */ bool deallocate(std::uint8_t* ptr) noexcept @@ -278,7 +288,9 @@ public: } }; -/** A collection of slab allocators of various sizes for a given type. */ +/** + * A collection of slab allocators of various sizes for a given type. + */ template class SlabAllocatorSet { @@ -345,13 +357,14 @@ public: ~SlabAllocatorSet() = default; - /** Returns a suitably aligned pointer, if one is available. - - @param extra The number of extra bytes, above and beyond the size of - the object, that should be returned by the allocator. - - @return a pointer to a block of memory, or nullptr if the allocator - can't satisfy this request. + /** + * Returns a suitably aligned pointer, if one is available. + * + * @param extra The number of extra bytes, above and beyond the size of + * the object, that should be returned by the allocator. + * + * @return a pointer to a block of memory, or nullptr if the allocator + * can't satisfy this request. */ std::uint8_t* allocate(std::size_t extra) noexcept @@ -368,12 +381,13 @@ public: return nullptr; } - /** Returns the memory block to the allocator. - - @param ptr A pointer to a memory block. - - @return true if this memory block belonged to one of the allocators - in this set and has been released; false otherwise. + /** + * Returns the memory block to the allocator. + * + * @param ptr A pointer to a memory block. + * + * @return true if this memory block belonged to one of the allocators + * in this set and has been released; false otherwise. */ bool deallocate(std::uint8_t* ptr) noexcept diff --git a/include/xrpl/basics/Slice.h b/include/xrpl/basics/Slice.h index f87ca063b8..36e7615c3a 100644 --- a/include/xrpl/basics/Slice.h +++ b/include/xrpl/basics/Slice.h @@ -16,12 +16,13 @@ namespace xrpl { -/** An immutable linear range of bytes. - - A fully constructed Slice is guaranteed to be in a valid state. - A Slice is lightweight and copyable, it retains no ownership - of the underlying memory. -*/ +/** + * An immutable linear range of bytes. + * + * A fully constructed Slice is guaranteed to be in a valid state. + * A Slice is lightweight and copyable, it retains no ownership + * of the underlying memory. + */ class Slice { private: @@ -32,30 +33,37 @@ public: using value_type = std::uint8_t; using const_iterator = value_type const*; - /** Default constructed Slice has length 0. */ + /** + * Default constructed Slice has length 0. + */ Slice() noexcept = default; Slice(Slice const&) noexcept = default; Slice& operator=(Slice const&) noexcept = default; - /** Create a slice pointing to existing memory. */ + /** + * Create a slice pointing to existing memory. + */ Slice(void const* data, std::size_t size) noexcept : data_(reinterpret_cast(data)), size_(size) { } - /** Return `true` if the byte range is empty. */ + /** + * Return `true` if the byte range is empty. + */ [[nodiscard]] bool empty() const noexcept { return size_ == 0; } - /** Returns the number of bytes in the storage. - - This may be zero for an empty range. - */ + /** + * Returns the number of bytes in the storage. + * + * This may be zero for an empty range. + */ /** @{ */ [[nodiscard]] std::size_t size() const noexcept @@ -70,17 +78,20 @@ public: } /** @} */ - /** Return a pointer to beginning of the storage. - @note The return type is guaranteed to be a pointer - to a single byte, to facilitate pointer arithmetic. - */ + /** + * Return a pointer to beginning of the storage. + * @note The return type is guaranteed to be a pointer + * to a single byte, to facilitate pointer arithmetic. + */ [[nodiscard]] std::uint8_t const* data() const noexcept { return data_; } - /** Access raw bytes. */ + /** + * Access raw bytes. + */ std::uint8_t operator[](std::size_t i) const noexcept { @@ -88,7 +99,9 @@ public: return data_[i]; } - /** Advance the buffer. */ + /** + * Advance the buffer. + */ /** @{ */ Slice& operator+=(std::size_t n) @@ -108,7 +121,9 @@ public: } /** @} */ - /** Shrinks the slice by moving its start forward by n characters. */ + /** + * Shrinks the slice by moving its start forward by n characters. + */ void removePrefix(std::size_t n) { @@ -116,7 +131,9 @@ public: size_ -= n; } - /** Shrinks the slice by moving its end backward by n characters. */ + /** + * Shrinks the slice by moving its end backward by n characters. + */ void removeSuffix(std::size_t n) { @@ -147,16 +164,17 @@ public: return data_ + size_; } - /** Return a "sub slice" of given length starting at the given position - - Note that the subslice encompasses the range [pos, pos + rCount), - where rCount is the smaller of count and size() - pos. - - @param pos position of the first character - @count requested length - - @returns The requested subslice, if the request is valid. - @throws std::out_of_range if pos > size() + /** + * Return a "sub slice" of given length starting at the given position + * + * Note that the subslice encompasses the range [pos, pos + rCount), + * where rCount is the smaller of count and size() - pos. + * + * @param pos position of the first character + * @count requested length + * + * @return The requested subslice, if the request is valid. + * @throws std::out_of_range if pos > size() */ [[nodiscard]] Slice substr(std::size_t pos, std::size_t count = std::numeric_limits::max()) const diff --git a/include/xrpl/basics/StringUtilities.h b/include/xrpl/basics/StringUtilities.h index 97df43d68f..2b360d2fda 100644 --- a/include/xrpl/basics/StringUtilities.h +++ b/include/xrpl/basics/StringUtilities.h @@ -17,15 +17,16 @@ namespace xrpl { -/** Format arbitrary binary data as an SQLite "blob literal". - - In SQLite, blob literals must be encoded when used in a query. Per - https://sqlite.org/lang_expr.html#literal_values_constants_ they are - encoded as string literals containing hexadecimal data and preceded - by a single 'X' character. - - @param blob An arbitrary blob of binary data - @return The input, encoded as a blob literal. +/** + * Format arbitrary binary data as an SQLite "blob literal". + * + * In SQLite, blob literals must be encoded when used in a query. Per + * https://sqlite.org/lang_expr.html#literal_values_constants_ they are + * encoded as string literals containing hexadecimal data and preceded + * by a single 'X' character. + * + * @param blob An arbitrary blob of binary data + * @return The input, encoded as a blob literal. */ std::string sqlBlobLiteral(Blob const& blob); @@ -130,11 +131,12 @@ trimWhitespace(std::string str); std::optional toUInt64(std::string const& s); -/** Determines if the given string looks like a TOML-file hosting domain. - - Do not use this function to determine if a particular string is a valid - domain, as this function may reject domains that are otherwise valid and - doesn't check whether the TLD is valid. +/** + * Determines if the given string looks like a TOML-file hosting domain. + * + * Do not use this function to determine if a particular string is a valid + * domain, as this function may reject domains that are otherwise valid and + * doesn't check whether the TLD is valid. */ bool isProperlyFormedTomlDomain(std::string_view domain); diff --git a/include/xrpl/basics/TaggedCache.h b/include/xrpl/basics/TaggedCache.h index 16c87cf833..7bb2cb552b 100644 --- a/include/xrpl/basics/TaggedCache.h +++ b/include/xrpl/basics/TaggedCache.h @@ -41,18 +41,19 @@ struct ReplaceDynamically; } // namespace detail -/** Map/cache combination. - This class implements a cache and a map. The cache keeps objects alive - in the map. The map allows multiple code paths that reference objects - with the same tag to get the same actual object. - - So long as data is in the cache, it will stay in memory. - If it stays in memory even after it is ejected from the cache, - the map will track it. - - @note Callers must not modify data objects that are stored in the cache - unless they hold their own lock over all cache operations. -*/ +/** + * Map/cache combination. + * This class implements a cache and a map. The cache keeps objects alive + * in the map. The map allows multiple code paths that reference objects + * with the same tag to get the same actual object. + * + * So long as data is in the cache, it will stay in memory. + * If it stays in memory even after it is ejected from the cache, + * the map will track it. + * + * @note Callers must not modify data objects that are stored in the cache + * unless they hold their own lock over all cache operations. + */ template < class Key, class T, @@ -82,11 +83,15 @@ public: beast::insight::Collector::ptr const& collector = beast::insight::NullCollector::make()); public: - /** Return the clock associated with the cache. */ + /** + * Return the clock associated with the cache. + */ clock_type& clock(); - /** Returns the number of items in the container. */ + /** + * Returns the number of items in the container. + */ std::size_t size() const; @@ -105,9 +110,10 @@ public: void reset(); - /** Refresh the last access time on a key if present. - @return `true` If the key was found. - */ + /** + * Refresh the last access time on a key if present. + * @return `true` If the key was found. + */ template bool touchIfExists(KeyComparable const& key); @@ -130,14 +136,15 @@ private: SharedPointerType const&, SharedPointerType&>; - /** Shared implementation of the canonicalize family. - - `policy` selects how a collision is resolved when `key` already exists: - detail::ReplaceCached, detail::ReplaceClient or - detail::ReplaceDynamically. For ReplaceDynamically `replaceCallback` is - invoked with the existing strong pointer and returns whether to replace - the cached value with `data`; for the tag policies it is unused. - */ + /** + * Shared implementation of the canonicalize family. + * + * `policy` selects how a collision is resolved when `key` already exists: + * detail::ReplaceCached, detail::ReplaceClient or + * detail::ReplaceDynamically. For ReplaceDynamically `replaceCallback` is + * invoked with the existing strong pointer and returns whether to replace + * the cached value with `data`; for the tag policies it is unused. + */ template bool canonicalizeImpl( @@ -147,69 +154,73 @@ private: Callback&& replaceCallback = nullptr); public: - /** Replace aliased objects with originals. - - Due to concurrency it is possible for two separate objects with - the same content and referring to the same unique "thing" to exist. - This routine eliminates the duplicate and performs a replacement - on the callers shared pointer if needed. - - `replaceCallback` is a callable taking the existing strong pointer and - returning whether to replace the cached value with `data` (true) or to - keep the cached value and write it back into `data` (false). Because the - write-back case mutates `data`, `data` must be writable. - - @param key The key corresponding to the object - @param data A shared pointer to the data corresponding to the object. - @param replaceCallback A callable (existing strong pointer -> bool). - - @return `true` if an existing live entry was found and used; `false` if a new entry was - inserted or an expired tracked entry was re-cached. - **/ + /** + * Replace aliased objects with originals. + * + * Due to concurrency it is possible for two separate objects with + * the same content and referring to the same unique "thing" to exist. + * This routine eliminates the duplicate and performs a replacement + * on the callers shared pointer if needed. + * + * `replaceCallback` is a callable taking the existing strong pointer and + * returning whether to replace the cached value with `data` (true) or to + * keep the cached value and write it back into `data` (false). Because the + * write-back case mutates `data`, `data` must be writable. + * + * @param key The key corresponding to the object + * @param data A shared pointer to the data corresponding to the object. + * @param replaceCallback A callable (existing strong pointer -> bool). + * + * @return `true` if an existing live entry was found and used; `false` if a new entry was + * inserted or an expired tracked entry was re-cached. + */ template bool canonicalize(key_type const& key, SharedPointerType& data, Callback&& replaceCallback); - /** Insert/update the canonical entry for `key`, always replacing the - cached value with `data`. - - If an entry already exists for `key`, the cached value is unconditionally - replaced with `data`; otherwise `data` is inserted. `data` is never - written back, so it may be const. - - @param key The key corresponding to the object. - @param data A shared pointer to the data corresponding to the object. - - @return `true` if an existing live entry was found and used; `false` if a new entry was - inserted or an expired tracked entry was re-cached. - **/ + /** + * Insert/update the canonical entry for `key`, always replacing the + * cached value with `data`. + * + * If an entry already exists for `key`, the cached value is unconditionally + * replaced with `data`; otherwise `data` is inserted. `data` is never + * written back, so it may be const. + * + * @param key The key corresponding to the object. + * @param data A shared pointer to the data corresponding to the object. + * + * @return `true` if an existing live entry was found and used; `false` if a new entry was + * inserted or an expired tracked entry was re-cached. + */ bool canonicalizeReplaceCache(key_type const& key, SharedPointerType const& data); - /** Insert the canonical entry for `key`, keeping any existing cached value. - - If an entry already exists for `key`, the cached value is kept and - written back into `data` so the caller ends up with the canonical - object; otherwise `data` is inserted. Because `data` may be overwritten - it must be writable. - - @param key The key corresponding to the object. - @param data A shared pointer to the data corresponding to the object; - updated to the canonical value when one already exists. - - @return `true` if an existing live entry was found and used; `false` if a new entry was - inserted or an expired tracked entry was re-cached. - **/ + /** + * Insert the canonical entry for `key`, keeping any existing cached value. + * + * If an entry already exists for `key`, the cached value is kept and + * written back into `data` so the caller ends up with the canonical + * object; otherwise `data` is inserted. Because `data` may be overwritten + * it must be writable. + * + * @param key The key corresponding to the object. + * @param data A shared pointer to the data corresponding to the object; + * updated to the canonical value when one already exists. + * + * @return `true` if an existing live entry was found and used; `false` if a new entry was + * inserted or an expired tracked entry was re-cached. + */ bool canonicalizeReplaceClient(key_type const& key, SharedPointerType& data); SharedPointerType fetch(key_type const& key); - /** Insert the element into the container. - If the key already exists, nothing happens. - @return `true` If the element was inserted - */ + /** + * Insert the element into the container. + * If the key already exists, nothing happens. + * @return `true` If the element was inserted + */ template auto insert(key_type const& key, T const& value) -> ReturnType @@ -235,15 +246,18 @@ public: getKeys() const; // CachedSLEs functions. - /** Returns the fraction of cache hits. */ + /** + * Returns the fraction of cache hits. + */ double rate() const; - /** Fetch an item from the cache. - If the digest was not found, Handler - will be called with this signature: - SLE::const_pointer(void) - */ + /** + * Fetch an item from the cache. + * If the digest was not found, Handler + * will be called with this signature: + * SLE::const_pointer(void) + */ template SharedPointerType fetch(key_type const& digest, Handler const& h); diff --git a/include/xrpl/basics/ToString.h b/include/xrpl/basics/ToString.h index e9f8f43633..a54db8a8ce 100644 --- a/include/xrpl/basics/ToString.h +++ b/include/xrpl/basics/ToString.h @@ -5,10 +5,11 @@ namespace xrpl { -/** to_string() generalizes std::to_string to handle bools, chars, and strings. - - It's also possible to provide implementation of to_string for a class - which needs a string implementation. +/** + * to_string() generalizes std::to_string to handle bools, chars, and strings. + * + * It's also possible to provide implementation of to_string for a class + * which needs a string implementation. */ template diff --git a/include/xrpl/basics/UptimeClock.h b/include/xrpl/basics/UptimeClock.h index 502aae7c25..b375de4497 100644 --- a/include/xrpl/basics/UptimeClock.h +++ b/include/xrpl/basics/UptimeClock.h @@ -7,12 +7,13 @@ namespace xrpl { -/** Tracks program uptime to seconds precision. - - The timer caches the current time as a performance optimization. - This allows clients to query the current time thousands of times - per second. -*/ +/** + * Tracks program uptime to seconds precision. + * + * The timer caches the current time as a performance optimization. + * This allows clients to query the current time thousands of times + * per second. + */ class UptimeClock { diff --git a/include/xrpl/basics/base_uint.h b/include/xrpl/basics/base_uint.h index f75100f862..96cfa343e3 100644 --- a/include/xrpl/basics/base_uint.h +++ b/include/xrpl/basics/base_uint.h @@ -63,18 +63,19 @@ struct AlwaysFalseT : std::bool_constant } // namespace detail -/** Integers of any length that is a multiple of 32-bits - - @note This class stores its values internally in big-endian - form and that internal representation is part of the - binary protocol of the XRP Ledger and cannot be changed - arbitrarily without causing breakage. - - @tparam Bits The number of bits this integer should have; must - be at least 64 and a multiple of 32. - @tparam Tag An arbitrary type that functions as a tag and allows - the instantiation of "distinct" types that the same - number of bits. +/** + * Integers of any length that is a multiple of 32-bits + * + * @note This class stores its values internally in big-endian + * form and that internal representation is part of the + * binary protocol of the XRP Ledger and cannot be changed + * arbitrarily without causing breakage. + * + * @tparam Bits The number of bits this integer should have; must + * be at least 64 and a multiple of 32. + * @tparam Tag An arbitrary type that functions as a tag and allows + * the instantiation of "distinct" types that the same + * number of bits. */ template class BaseUInt @@ -154,21 +155,23 @@ public: return data() + kBytes; } - /** Value hashing function. - The seed prevents crafted inputs from causing degenerate parent - containers. - */ + /** + * Value hashing function. + * The seed prevents crafted inputs from causing degenerate parent + * containers. + */ using hasher = HardenedHash<>; //-------------------------------------------------------------------------- private: - /** Construct from a raw pointer. - The buffer pointed to by `data` must be at least Bits/8 bytes. - - @note the structure is used to disambiguate this from the std::uint64_t - constructor: something like base_uint(0) is ambiguous. - */ + /** + * Construct from a raw pointer. + * The buffer pointed to by `data` must be at least Bits/8 bytes. + * + * @note the structure is used to disambiguate this from the std::uint64_t + * constructor: something like base_uint(0) is ambiguous. + */ // NIKB TODO Remove the need for this constructor. struct VoidHelper { @@ -503,13 +506,14 @@ public: h(a.data_.data(), sizeof(a.data_)); } - /** Parse a hex string into a base_uint - - The input must be precisely `2 * bytes` hexadecimal characters - long, with one exception: the value '0'. - - @param sv A null-terminated string of hexadecimal characters - @return true if the input was parsed properly; false otherwise. + /** + * Parse a hex string into a base_uint + * + * The input must be precisely `2 * bytes` hexadecimal characters + * long, with one exception: the value '0'. + * + * @param sv A null-terminated string of hexadecimal characters + * @return true if the input was parsed properly; false otherwise. */ [[nodiscard]] constexpr bool parseHex(std::string_view sv) diff --git a/include/xrpl/basics/chrono.h b/include/xrpl/basics/chrono.h index 61246fc699..b855318524 100644 --- a/include/xrpl/basics/chrono.h +++ b/include/xrpl/basics/chrono.h @@ -21,15 +21,16 @@ using days = using weeks = std::chrono::duration>>; -/** Clock for measuring the network time. - - The epoch is January 1, 2000 - - epoch_offset - = date(2000-01-01) - date(1970-0-01) - = days(10957) - = seconds(946684800) -*/ +/** + * Clock for measuring the network time. + * + * The epoch is January 1, 2000 + * + * epoch_offset + * = date(2000-01-01) - date(1970-0-01) + * = days(10957) + * = seconds(946684800) + */ static constexpr std::chrono::seconds kEpochOffset = date::sys_days{date::year{2000} / 1 / 1} - date::sys_days{date::year{1970} / 1 / 1}; @@ -81,16 +82,21 @@ toStringIso(NetClock::time_point tp) return toStringIso(date::sys_time{tp.time_since_epoch() + kEpochOffset}); } -/** A clock for measuring elapsed time. - - The epoch is unspecified. -*/ +/** + * A clock for measuring elapsed time. + * + * The epoch is unspecified. + */ using Stopwatch = beast::AbstractClock; -/** A manual Stopwatch for unit tests. */ +/** + * A manual Stopwatch for unit tests. + */ using TestStopwatch = beast::ManualClock; -/** Returns an instance of a wall clock. */ +/** + * Returns an instance of a wall clock. + */ inline Stopwatch& stopwatch() { diff --git a/include/xrpl/basics/contract.h b/include/xrpl/basics/contract.h index 0e90687de3..6588cb5d1a 100644 --- a/include/xrpl/basics/contract.h +++ b/include/xrpl/basics/contract.h @@ -15,20 +15,23 @@ namespace xrpl { preconditions, postconditions, and invariants. */ -/** Generates and logs a call stack */ +/** + * Generates and logs a call stack + */ void logThrow(std::string const& title); -/** Rethrow the exception currently being handled. - - When called from within a catch block, it will pass - control to the next matching exception handler, if any. - Otherwise, std::terminate will be called. - - ASAN can't handle sudden jumps in control flow very well. This - function is marked as XRPL_NO_SANITIZE_ADDRESS to prevent it from - triggering false positives, since it throws. -*/ +/** + * Rethrow the exception currently being handled. + * + * When called from within a catch block, it will pass + * control to the next matching exception handler, if any. + * Otherwise, std::terminate will be called. + * + * ASAN can't handle sudden jumps in control flow very well. This + * function is marked as XRPL_NO_SANITIZE_ADDRESS to prevent it from + * triggering false positives, since it throws. + */ [[noreturn]] XRPL_NO_SANITIZE_ADDRESS inline void rethrow() { @@ -56,7 +59,9 @@ Throw(Args&&... args) throw std::move(e); } -/** Called when faulty logic causes a broken invariant. */ +/** + * Called when faulty logic causes a broken invariant. + */ [[noreturn]] void logicError(std::string const& how) noexcept; diff --git a/include/xrpl/basics/hardened_hash.h b/include/xrpl/basics/hardened_hash.h index 5a855736b3..6b8277a560 100644 --- a/include/xrpl/basics/hardened_hash.h +++ b/include/xrpl/basics/hardened_hash.h @@ -39,33 +39,33 @@ makeSeedPair() noexcept /** * Seed functor once per construction - - A std compatible hash adapter that resists adversarial inputs. - For this to work, T must implement in its own namespace: - - @code - - template - void - hash_append (Hasher& h, T const& t) noexcept - { - // hash_append each base and member that should - // participate in forming the hash - using beast::hash_append; - hash_append (h, static_cast(t)); - hash_append (h, static_cast(t)); - // ... - hash_append (h, t.member1); - hash_append (h, t.member2); - // ... - } - - @endcode - - Do not use any version of Murmur or CityHash for the Hasher - template parameter (the hashing algorithm). For details - see https://131002.net/siphash/#at -*/ + * + * A std compatible hash adapter that resists adversarial inputs. + * For this to work, T must implement in its own namespace: + * + * @code + * + * template + * void + * hash_append (Hasher& h, T const& t) noexcept + * { + * // hash_append each base and member that should + * // participate in forming the hash + * using beast::hash_append; + * hash_append (h, static_cast(t)); + * hash_append (h, static_cast(t)); + * // ... + * hash_append (h, t.member1); + * hash_append (h, t.member2); + * // ... + * } + * + * @endcode + * + * Do not use any version of Murmur or CityHash for the Hasher + * template parameter (the hashing algorithm). For details + * see https://131002.net/siphash/#at + */ template class HardenedHash diff --git a/include/xrpl/basics/make_SSLContext.h b/include/xrpl/basics/make_SSLContext.h index 45ac637c36..c8ada176f9 100644 --- a/include/xrpl/basics/make_SSLContext.h +++ b/include/xrpl/basics/make_SSLContext.h @@ -7,11 +7,15 @@ namespace xrpl { -/** Create a self-signed SSL context that allows anonymous Diffie Hellman. */ +/** + * Create a self-signed SSL context that allows anonymous Diffie Hellman. + */ std::shared_ptr makeSslContext(std::string const& cipherList); -/** Create an authenticated SSL context using the specified files. */ +/** + * Create an authenticated SSL context using the specified files. + */ std::shared_ptr makeSslContextAuthed( std::string const& keyFile, diff --git a/include/xrpl/basics/mulDiv.h b/include/xrpl/basics/mulDiv.h index 9076da62f2..38fa57294b 100644 --- a/include/xrpl/basics/mulDiv.h +++ b/include/xrpl/basics/mulDiv.h @@ -7,16 +7,16 @@ namespace xrpl { constexpr auto kMuldivMax = std::numeric_limits::max(); -/** Return value*mul/div accurately. - Computes the result of the multiplication and division in - a single step, avoiding overflow and retaining precision. - Throws: - None - Returns: - `std::optional`: - `std::nullopt` if the calculation overflows. Otherwise, `value * mul - / div`. -*/ +/** + * Return value*mul/div accurately. + * + * Computes the result of the multiplication and division in + * a single step, avoiding overflow and retaining precision. + * + * @throws None + * @return `std::nullopt` if the calculation overflows. Otherwise, + * `value * mul / div`. + */ std::optional mulDiv(std::uint64_t value, std::uint64_t mul, std::uint64_t div); diff --git a/include/xrpl/basics/random.h b/include/xrpl/basics/random.h index c544e7d0c8..7aeb7d6145 100644 --- a/include/xrpl/basics/random.h +++ b/include/xrpl/basics/random.h @@ -33,16 +33,17 @@ template using is_engine = std::is_invocable_r; } // namespace detail -/** Return the default random engine. - - This engine is guaranteed to be deterministic, but by - default will be randomly seeded. It is NOT cryptographically - secure and MUST NOT be used to generate randomness that - will be used for keys, secure cookies, IVs, padding, etc. - - Each thread gets its own instance of the engine which - will be randomly seeded. -*/ +/** + * Return the default random engine. + * + * This engine is guaranteed to be deterministic, but by + * default will be randomly seeded. It is NOT cryptographically + * secure and MUST NOT be used to generate randomness that + * will be used for keys, secure cookies, IVs, padding, etc. + * + * Each thread gets its own instance of the engine which + * will be randomly seeded. + */ inline beast::xor_shift_engine& defaultPrng() { @@ -70,25 +71,26 @@ defaultPrng() return kEngine; } -/** Return a uniformly distributed random integer. - - @param min The smallest value to return. If not specified - the value defaults to 0. - @param max The largest value to return. If not specified - the value defaults to the largest value that - can be represented. - - The randomness is generated by the specified engine (or - the default engine if one is not specified). The result - is cryptographically secure only when the engine passed - into the function is cryptographically secure. - - @note The range is always a closed interval, so calling - rand_int(-5, 15) can return any integer in the - closed interval [-5, 15]; similarly, calling - rand_int(7) can return any integer in the closed - interval [0, 7]. -*/ +/** + * Return a uniformly distributed random integer. + * + * @param min The smallest value to return. If not specified + * the value defaults to 0. + * @param max The largest value to return. If not specified + * the value defaults to the largest value that + * can be represented. + * + * The randomness is generated by the specified engine (or + * the default engine if one is not specified). The result + * is cryptographically secure only when the engine passed + * into the function is cryptographically secure. + * + * @note The range is always a closed interval, so calling + * rand_int(-5, 15) can return any integer in the + * closed interval [-5, 15]; similarly, calling + * rand_int(7) can return any integer in the closed + * interval [0, 7]. + */ /** @{ */ template Integral @@ -144,7 +146,9 @@ randInt() } /** @} */ -/** Return a random byte */ +/** + * Return a random byte + */ /** @{ */ template Byte @@ -166,7 +170,9 @@ randByte() } /** @} */ -/** Return a random boolean value */ +/** + * Return a random boolean value + */ /** @{ */ template inline bool diff --git a/include/xrpl/basics/scope.h b/include/xrpl/basics/scope.h index e63bb69eb5..5821e1dacc 100644 --- a/include/xrpl/basics/scope.h +++ b/include/xrpl/basics/scope.h @@ -156,41 +156,41 @@ template ScopeSuccess(EF) -> ScopeSuccess; /** - Automatically unlocks and re-locks a unique_lock object. - - This is the reverse of a std::unique_lock object - instead of locking the - mutex for the lifetime of this object, it unlocks it. - - Make sure you don't try to unlock mutexes that aren't actually locked! - - This is essentially a less-versatile boost::reverse_lock. - - e.g. @code - - std::mutex mut; - - for (;;) - { - std::unique_lock myScopedLock{mut}; - // mut is now locked - - ... do some stuff with it locked .. - - while (xyz) - { - ... do some stuff with it locked .. - - scope_unlock unlocker{myScopedLock}; - - // mut is now unlocked for the remainder of this block, - // and re-locked at the end. - - ...do some stuff with it unlocked ... - } // mut gets locked here. - - } // mut gets unlocked here - @endcode -*/ + * Automatically unlocks and re-locks a unique_lock object. + * + * This is the reverse of a std::unique_lock object - instead of locking the + * mutex for the lifetime of this object, it unlocks it. + * + * Make sure you don't try to unlock mutexes that aren't actually locked! + * + * This is essentially a less-versatile boost::reverse_lock. + * + * e.g. @code + * + * std::mutex mut; + * + * for (;;) + * { + * std::unique_lock myScopedLock{mut}; + * // mut is now locked + * + * ... do some stuff with it locked .. + * + * while (xyz) + * { + * ... do some stuff with it locked .. + * + * scope_unlock unlocker{myScopedLock}; + * + * // mut is now unlocked for the remainder of this block, + * // and re-locked at the end. + * + * ...do some stuff with it unlocked ... + * } // mut gets locked here. + * + * } // mut gets unlocked here + * @endcode + */ template class ScopeUnlock diff --git a/include/xrpl/basics/spinlock.h b/include/xrpl/basics/spinlock.h index 2cc00efdef..87611f20ba 100644 --- a/include/xrpl/basics/spinlock.h +++ b/include/xrpl/basics/spinlock.h @@ -15,15 +15,16 @@ namespace xrpl { namespace detail { -/** Inform the processor that we are in a tight spin-wait loop. - - Spinlocks caught in tight loops can result in the processor's pipeline - filling up with comparison operations, resulting in a misprediction at - the time the lock is finally acquired, necessitating pipeline flushing - which is ridiculously expensive and results in very high latency. - - This function instructs the processor to "pause" for some architecture - specific amount of time, to prevent this. +/** + * Inform the processor that we are in a tight spin-wait loop. + * + * Spinlocks caught in tight loops can result in the processor's pipeline + * filling up with comparison operations, resulting in a misprediction at + * the time the lock is finally acquired, necessitating pipeline flushing + * which is ridiculously expensive and results in very high latency. + * + * This function instructs the processor to "pause" for some architecture + * specific amount of time, to prevent this. */ inline void spinPause() noexcept @@ -38,37 +39,39 @@ spinPause() noexcept } // namespace detail /** @{ */ -/** Classes to handle arrays of spinlocks packed into a single atomic integer: - - Packed spinlocks allow for tremendously space-efficient lock-sharding - but they come at a cost. - - First, the implementation is necessarily low-level and uses advanced - features like memory ordering and highly platform-specific tricks to - maximize performance. This imposes a significant and ongoing cost to - developers. - - Second, and perhaps most important, is that the packing of multiple - locks into a single integer which, albeit space-efficient, also has - performance implications stemming from data dependencies, increased - cache-coherency traffic between processors and heavier loads on the - processor's load/store units. - - To be sure, these locks can have advantages but they are definitely - not general purpose locks and should not be thought of or used that - way. The use cases for them are likely few and far between; without - a compelling reason to use them, backed by profiling data, it might - be best to use one of the standard locking primitives instead. Note - that in most common platforms, `std::mutex` is so heavily optimized - that it can, usually, outperform spinlocks. - - @tparam T An unsigned integral type (e.g. std::uint16_t) +/** + * Classes to handle arrays of spinlocks packed into a single atomic integer: + * + * Packed spinlocks allow for tremendously space-efficient lock-sharding + * but they come at a cost. + * + * First, the implementation is necessarily low-level and uses advanced + * features like memory ordering and highly platform-specific tricks to + * maximize performance. This imposes a significant and ongoing cost to + * developers. + * + * Second, and perhaps most important, is that the packing of multiple + * locks into a single integer which, albeit space-efficient, also has + * performance implications stemming from data dependencies, increased + * cache-coherency traffic between processors and heavier loads on the + * processor's load/store units. + * + * To be sure, these locks can have advantages but they are definitely + * not general purpose locks and should not be thought of or used that + * way. The use cases for them are likely few and far between; without + * a compelling reason to use them, backed by profiling data, it might + * be best to use one of the standard locking primitives instead. Note + * that in most common platforms, `std::mutex` is so heavily optimized + * that it can, usually, outperform spinlocks. + * + * @tparam T An unsigned integral type (e.g. std::uint16_t) */ -/** A class that grabs a single packed spinlock from an atomic integer. - - This class meets the requirements of Lockable: - https://en.cppreference.com/w/cpp/named_req/Lockable +/** + * A class that grabs a single packed spinlock from an atomic integer. + * + * This class meets the requirements of Lockable: + * https://en.cppreference.com/w/cpp/named_req/Lockable */ template class PackedSpinlock @@ -91,13 +94,14 @@ public: PackedSpinlock& operator=(PackedSpinlock const&) = delete; - /** A single spinlock packed inside the specified atomic - - @param lock The atomic integer inside which the spinlock is packed. - @param index The index of the spinlock this object acquires. - - @note For performance reasons, you should strive to have `lock` be - on a cacheline by itself. + /** + * A single spinlock packed inside the specified atomic + * + * @param lock The atomic integer inside which the spinlock is packed. + * @param index The index of the spinlock this object acquires. + * + * @note For performance reasons, you should strive to have `lock` be + * on a cacheline by itself. */ PackedSpinlock(std::atomic& lock, int index) : bits_(lock), mask_(static_cast(1) << index) { @@ -133,17 +137,18 @@ public: } }; -/** A spinlock implemented on top of an atomic integer. - - @note Using `packed_spinlock` and `spinlock` against the same underlying - atomic integer can result in `spinlock` not being able to actually - acquire the lock during periods of high contention, because of how - the two locks operate: `spinlock` will spin trying to grab all the - bits at once, whereas any given `packed_spinlock` will only try to - grab one bit at a time. Caveat emptor. - - This class meets the requirements of Lockable: - https://en.cppreference.com/w/cpp/named_req/Lockable +/** + * A spinlock implemented on top of an atomic integer. + * + * @note Using `packed_spinlock` and `spinlock` against the same underlying + * atomic integer can result in `spinlock` not being able to actually + * acquire the lock during periods of high contention, because of how + * the two locks operate: `spinlock` will spin trying to grab all the + * bits at once, whereas any given `packed_spinlock` will only try to + * grab one bit at a time. Caveat emptor. + * + * This class meets the requirements of Lockable: + * https://en.cppreference.com/w/cpp/named_req/Lockable */ template class Spinlock @@ -159,12 +164,13 @@ public: Spinlock& operator=(Spinlock const&) = delete; - /** Grabs the - - @param lock The atomic integer to spin against. - - @note For performance reasons, you should strive to have `lock` be - on a cacheline by itself. + /** + * Grabs the + * + * @param lock The atomic integer to spin against. + * + * @note For performance reasons, you should strive to have `lock` be + * on a cacheline by itself. */ Spinlock(std::atomic& lock) : lock_(lock) { diff --git a/include/xrpl/basics/tagged_integer.h b/include/xrpl/basics/tagged_integer.h index 5a088db863..2edb314a16 100644 --- a/include/xrpl/basics/tagged_integer.h +++ b/include/xrpl/basics/tagged_integer.h @@ -12,17 +12,18 @@ namespace xrpl { -/** A type-safe wrap around standard integral types - - The tag is used to implement type safety, catching mismatched types at - compile time. Multiple instantiations wrapping the same underlying integral - type are distinct types (distinguished by tag) and will not interoperate. A - tagged_integer supports all the usual assignment, arithmetic, comparison and - shifting operations defined for the underlying type - - The tag is not meant as a unit, which would require restricting the set of - allowed arithmetic operations. -*/ +/** + * A type-safe wrap around standard integral types + * + * The tag is used to implement type safety, catching mismatched types at + * compile time. Multiple instantiations wrapping the same underlying integral + * type are distinct types (distinguished by tag) and will not interoperate. A + * tagged_integer supports all the usual assignment, arithmetic, comparison and + * shifting operations defined for the underlying type + * + * The tag is not meant as a unit, which would require restricting the set of + * allowed arithmetic operations. + */ template class TaggedInteger : boost::totally_ordered< TaggedInteger, diff --git a/include/xrpl/beast/asio/io_latency_probe.h b/include/xrpl/beast/asio/io_latency_probe.h index f67ff4a692..d87bdafe45 100644 --- a/include/xrpl/beast/asio/io_latency_probe.h +++ b/include/xrpl/beast/asio/io_latency_probe.h @@ -14,7 +14,9 @@ namespace beast { -/** Measures handler latency on an io_context queue. */ +/** + * Measures handler latency on an io_context queue. + */ template class IOLatencyProbe { @@ -42,7 +44,9 @@ public: cancel(lock, true); } - /** Return the io_context associated with the latency probe. */ + /** + * Return the io_context associated with the latency probe. + */ /** @{ */ boost::asio::io_context& getIoContext() @@ -57,9 +61,10 @@ public: } /** @} */ - /** Cancel all pending i/o. - Any handlers which have already been queued will still be called. - */ + /** + * Cancel all pending i/o. + * Any handlers which have already been queued will still be called. + */ /** @{ */ void cancel() @@ -76,10 +81,11 @@ public: } /** @} */ - /** Measure one sample of i/o latency. - Handler will be called with this signature: - void Handler (Duration d); - */ + /** + * Measure one sample of i/o latency. + * Handler will be called with this signature: + * void Handler (Duration d); + */ template void sampleOne(Handler&& handler) @@ -91,10 +97,11 @@ public: ios_, SampleOp(std::forward(handler), Clock::now(), false, this)); } - /** Initiate continuous i/o latency sampling. - Handler will be called with this signature: - void Handler (std::chrono::milliseconds); - */ + /** + * Initiate continuous i/o latency sampling. + * Handler will be called with this signature: + * void Handler (std::chrono::milliseconds); + */ template void sample(Handler&& handler) diff --git a/include/xrpl/beast/clock/abstract_clock.h b/include/xrpl/beast/clock/abstract_clock.h index 15d785d138..6e23700730 100644 --- a/include/xrpl/beast/clock/abstract_clock.h +++ b/include/xrpl/beast/clock/abstract_clock.h @@ -2,34 +2,35 @@ namespace beast { -/** Abstract interface to a clock. - - This makes now() a member function instead of a static member, so - an instance of the class can be dependency injected, facilitating - unit tests where time may be controlled. - - An abstract_clock inherits all the nested types of the Clock - template parameter. - - Example: - - @code - - struct Implementation - { - using clock_type = abstract_clock ; - clock_type& clock_; - explicit Implementation (clock_type& clock) - : clock_(clock) - { - } - }; - - @endcode - - @tparam Clock A type meeting these requirements: - http://en.cppreference.com/w/cpp/concept/Clock -*/ +/** + * Abstract interface to a clock. + * + * This makes now() a member function instead of a static member, so + * an instance of the class can be dependency injected, facilitating + * unit tests where time may be controlled. + * + * An abstract_clock inherits all the nested types of the Clock + * template parameter. + * + * Example: + * + * @code + * + * struct Implementation + * { + * using clock_type = abstract_clock ; + * clock_type& clock_; + * explicit Implementation (clock_type& clock) + * : clock_(clock) + * { + * } + * }; + * + * @endcode + * + * @tparam Clock A type meeting these requirements: + * http://en.cppreference.com/w/cpp/concept/Clock + */ template class AbstractClock { @@ -46,7 +47,9 @@ public: AbstractClock() = default; AbstractClock(AbstractClock const&) = default; - /** Returns the current time. */ + /** + * Returns the current time. + */ [[nodiscard]] virtual time_point now() const = 0; }; @@ -74,11 +77,12 @@ struct AbstractClockWrapper : public AbstractClock //------------------------------------------------------------------------------ -/** Returns a global instance of an abstract clock. - @tparam Facade A type meeting these requirements: - http://en.cppreference.com/w/cpp/concept/Clock - @tparam Clock The actual concrete clock to use. -*/ +/** + * Returns a global instance of an abstract clock. + * @tparam Facade A type meeting these requirements: + * http://en.cppreference.com/w/cpp/concept/Clock + * @tparam Clock The actual concrete clock to use. + */ template AbstractClock& getAbstractClock() diff --git a/include/xrpl/beast/clock/basic_seconds_clock.h b/include/xrpl/beast/clock/basic_seconds_clock.h index 5a267e9458..dce521d0b8 100644 --- a/include/xrpl/beast/clock/basic_seconds_clock.h +++ b/include/xrpl/beast/clock/basic_seconds_clock.h @@ -4,15 +4,16 @@ namespace beast { -/** A clock whose minimum resolution is one second. - - The purpose of this class is to optimize the performance of the now() - member function call. It uses a dedicated thread that wakes up at least - once per second to sample the requested trivial clock. - - @tparam Clock A type meeting these requirements: - http://en.cppreference.com/w/cpp/concept/Clock -*/ +/** + * A clock whose minimum resolution is one second. + * + * The purpose of this class is to optimize the performance of the now() + * member function call. It uses a dedicated thread that wakes up at least + * once per second to sample the requested trivial clock. + * + * @tparam Clock A type meeting these requirements: + * http://en.cppreference.com/w/cpp/concept/Clock + */ class BasicSecondsClock { public: diff --git a/include/xrpl/beast/clock/manual_clock.h b/include/xrpl/beast/clock/manual_clock.h index 8b3e4e63c6..4dc9553644 100644 --- a/include/xrpl/beast/clock/manual_clock.h +++ b/include/xrpl/beast/clock/manual_clock.h @@ -7,15 +7,16 @@ namespace beast { -/** Manual clock implementation. - - This concrete class implements the @ref abstract_clock interface and - allows the time to be advanced manually, mainly for the purpose of - providing a clock in unit tests. - - @tparam Clock A type meeting these requirements: - http://en.cppreference.com/w/cpp/concept/Clock -*/ +/** + * Manual clock implementation. + * + * This concrete class implements the @ref abstract_clock interface and + * allows the time to be advanced manually, mainly for the purpose of + * providing a clock in unit tests. + * + * @tparam Clock A type meeting these requirements: + * http://en.cppreference.com/w/cpp/concept/Clock + */ template class ManualClock : public AbstractClock { @@ -38,7 +39,9 @@ public: return now_; } - /** Set the current time of the manual clock. */ + /** + * Set the current time of the manual clock. + */ void set(time_point const& when) { @@ -48,7 +51,9 @@ public: now_ = when; } - /** Convenience for setting the time in seconds from epoch. */ + /** + * Convenience for setting the time in seconds from epoch. + */ template void set(Integer secondsFromEpoch) @@ -56,7 +61,9 @@ public: set(time_point(duration(std::chrono::seconds(secondsFromEpoch)))); } - /** Advance the clock by a duration. */ + /** + * Advance the clock by a duration. + */ template void advance(std::chrono::duration const& elapsed) @@ -67,7 +74,9 @@ public: now_ += elapsed; } - /** Convenience for advancing the clock by one second. */ + /** + * Convenience for advancing the clock by one second. + */ ManualClock& operator++() { diff --git a/include/xrpl/beast/container/aged_container_utility.h b/include/xrpl/beast/container/aged_container_utility.h index f43e59b0f7..da3e4e0500 100644 --- a/include/xrpl/beast/container/aged_container_utility.h +++ b/include/xrpl/beast/container/aged_container_utility.h @@ -7,7 +7,9 @@ namespace beast { -/** Expire aged container items past the specified age. */ +/** + * Expire aged container items past the specified age. + */ template std::size_t expire(AgedContainer& c, std::chrono::duration const& age) diff --git a/include/xrpl/beast/container/detail/aged_ordered_container.h b/include/xrpl/beast/container/detail/aged_ordered_container.h index c98f16022f..5b60ef7e6d 100644 --- a/include/xrpl/beast/container/detail/aged_ordered_container.h +++ b/include/xrpl/beast/container/detail/aged_ordered_container.h @@ -39,22 +39,23 @@ struct IsBoostReverseIterator> : std::tru explicit IsBoostReverseIterator() = default; }; -/** Associative container where each element is also indexed by time. - - This container mirrors the interface of the standard library ordered - associative containers, with the addition that each element is associated - with a `when` `time_point` which is obtained from the value of the clock's - `now`. The function `touch` updates the time for an element to the current - time as reported by the clock. - - An extra set of iterator types and member functions are provided in the - `chronological` memberspace that allow traversal in temporal or reverse - temporal order. This container is useful as a building block for caches - whose items expire after a certain amount of time. The chronological - iterators allow for fully customizable expiration strategies. - - @see aged_set, aged_multiset, aged_map, aged_multimap -*/ +/** + * Associative container where each element is also indexed by time. + * + * This container mirrors the interface of the standard library ordered + * associative containers, with the addition that each element is associated + * with a `when` `time_point` which is obtained from the value of the clock's + * `now`. The function `touch` updates the time for an element to the current + * time as reported by the clock. + * + * An extra set of iterator types and member functions are provided in the + * `chronological` memberspace that allow traversal in temporal or reverse + * temporal order. This container is useful as a building block for caches + * whose items expire after a certain amount of time. The chronological + * iterators allow for fully customizable expiration strategies. + * + * @see aged_set, aged_multiset, aged_map, aged_multimap + */ template < bool IsMulti, bool IsMap, @@ -1795,7 +1796,9 @@ swap( lhs.swap(rhs); } -/** Expire aged container items past the specified age. */ +/** + * Expire aged container items past the specified age. + */ template < bool IsMulti, bool IsMap, diff --git a/include/xrpl/beast/container/detail/aged_unordered_container.h b/include/xrpl/beast/container/detail/aged_unordered_container.h index 9be9e96ba2..db10e8cc23 100644 --- a/include/xrpl/beast/container/detail/aged_unordered_container.h +++ b/include/xrpl/beast/container/detail/aged_unordered_container.h @@ -43,23 +43,24 @@ TODO namespace beast { namespace detail { -/** Associative container where each element is also indexed by time. - - This container mirrors the interface of the standard library unordered - associative containers, with the addition that each element is associated - with a `when` `time_point` which is obtained from the value of the clock's - `now`. The function `touch` updates the time for an element to the current - time as reported by the clock. - - An extra set of iterator types and member functions are provided in the - `chronological` memberspace that allow traversal in temporal or reverse - temporal order. This container is useful as a building block for caches - whose items expire after a certain amount of time. The chronological - iterators allow for fully customizable expiration strategies. - - @see aged_unordered_set, aged_unordered_multiset - @see aged_unordered_map, aged_unordered_multimap -*/ +/** + * Associative container where each element is also indexed by time. + * + * This container mirrors the interface of the standard library unordered + * associative containers, with the addition that each element is associated + * with a `when` `time_point` which is obtained from the value of the clock's + * `now`. The function `touch` updates the time for an element to the current + * time as reported by the clock. + * + * An extra set of iterator types and member functions are provided in the + * `chronological` memberspace that allow traversal in temporal or reverse + * temporal order. This container is useful as a building block for caches + * whose items expire after a certain amount of time. The chronological + * iterators allow for fully customizable expiration strategies. + * + * @see aged_unordered_set, aged_unordered_multiset + * @see aged_unordered_map, aged_unordered_multimap + */ template < bool IsMulti, bool IsMap, @@ -2709,7 +2710,9 @@ swap( lhs.swap(rhs); } -/** Expire aged container items past the specified age. */ +/** + * Expire aged container items past the specified age. + */ template < bool IsMulti, bool IsMap, diff --git a/include/xrpl/beast/core/CurrentThreadName.h b/include/xrpl/beast/core/CurrentThreadName.h index 3cdfe4c678..d1f14a6f80 100644 --- a/include/xrpl/beast/core/CurrentThreadName.h +++ b/include/xrpl/beast/core/CurrentThreadName.h @@ -12,9 +12,10 @@ namespace beast { -/** Changes the name of the caller thread. - Different OSes may place different length or content limits on this name. -*/ +/** + * Changes the name of the caller thread. + * Different OSes may place different length or content limits on this name. + */ void setCurrentThreadName(std::string_view newThreadName); @@ -24,13 +25,14 @@ setCurrentThreadName(std::string_view newThreadName); // Maximum number of characters is therefore 15. constexpr std::size_t kMaxThreadNameLength = 15; -/** Sets the name of the caller thread with compile-time size checking. - @tparam N The size of the string literal including null terminator - @param newThreadName A string literal to set as the thread name - - This template overload enforces that thread names are at most 16 characters - (including null terminator) at compile time, matching Linux's limit. -*/ +/** + * Sets the name of the caller thread with compile-time size checking. + * @tparam N The size of the string literal including null terminator + * @param newThreadName A string literal to set as the thread name + * + * This template overload enforces that thread names are at most 16 characters + * (including null terminator) at compile time, matching Linux's limit. + */ template void setCurrentThreadName(char const (&newThreadName)[N]) @@ -41,14 +43,15 @@ setCurrentThreadName(char const (&newThreadName)[N]) } #endif -/** Returns the name of the caller thread. - - The name returned is the name as set by a call to setCurrentThreadName(). - If the thread name is set by an external force, then that name change - will not be reported. - - If no name has ever been set, then the empty string is returned. -*/ +/** + * Returns the name of the caller thread. + * + * The name returned is the name as set by a call to setCurrentThreadName(). + * If the thread name is set by an external force, then that name change + * will not be reported. + * + * If no name has ever been set, then the empty string is returned. + */ std::string getCurrentThreadName(); diff --git a/include/xrpl/beast/core/LexicalCast.h b/include/xrpl/beast/core/LexicalCast.h index 1162d83078..7cf21892bd 100644 --- a/include/xrpl/beast/core/LexicalCast.h +++ b/include/xrpl/beast/core/LexicalCast.h @@ -163,17 +163,19 @@ struct LexicalCast //------------------------------------------------------------------------------ -/** Thrown when a conversion is not possible with LexicalCast. - Only used in the throw variants of lexicalCast. -*/ +/** + * Thrown when a conversion is not possible with LexicalCast. + * Only used in the throw variants of lexicalCast. + */ struct BadLexicalCast : public std::bad_cast { explicit BadLexicalCast() = default; }; -/** Intelligently convert from one type to another. - @return `false` if there was a parsing or range error -*/ +/** + * Intelligently convert from one type to another. + * @return `false` if there was a parsing or range error + */ template bool lexicalCastChecked(Out& out, In in) @@ -181,12 +183,13 @@ lexicalCastChecked(Out& out, In in) return detail::LexicalCast()(out, in); } -/** Convert from one type to another, throw on error - - An exception of type BadLexicalCast is thrown if the conversion fails. - - @return The new type. -*/ +/** + * Convert from one type to another, throw on error + * + * An exception of type BadLexicalCast is thrown if the conversion fails. + * + * @return The new type. + */ template Out lexicalCastThrow(In in) @@ -197,11 +200,12 @@ lexicalCastThrow(In in) throw BadLexicalCast(); } -/** Convert from one type to another. - - @param defaultValue The value returned if parsing fails - @return The new type. -*/ +/** + * Convert from one type to another. + * + * @param defaultValue The value returned if parsing fails + * @return The new type. + */ template Out lexicalCast(In in, Out defaultValue = Out()) diff --git a/include/xrpl/beast/core/List.h b/include/xrpl/beast/core/List.h index 1eeeaa87d1..b9b6829d31 100644 --- a/include/xrpl/beast/core/List.h +++ b/include/xrpl/beast/core/List.h @@ -11,7 +11,9 @@ class List; namespace detail { -/** Copy `const` attribute from T to U if present. */ +/** + * Copy `const` attribute from T to U if present. + */ /** @{ */ template struct CopyConst @@ -153,110 +155,111 @@ private: } // namespace detail -/** Intrusive doubly linked list. - - This intrusive List is a container similar in operation to std::list in the - Standard Template Library (STL). Like all @ref intrusive containers, List - requires you to first derive your class from List<>::Node: - - @code - - struct Object : List ::Node - { - explicit Object (int value) : value_ (value) - { - } - - int value_; - }; - - @endcode - - Now we define the list, and add a couple of items. - - @code - - List list; - - list.push_back (* (new Object (1))); - list.push_back (* (new Object (2))); - - @endcode - - For compatibility with the standard containers, push_back() expects a - reference to the object. Unlike the standard container, however, push_back() - places the actual object in the list and not a copy-constructed duplicate. - - Iterating over the list follows the same idiom as the STL: - - @code - - for (List ::iterator iter = list.begin(); iter != list.end; ++iter) - std::cout << iter->value_; - - @endcode - - You can even use BOOST_FOREACH, or range based for loops: - - @code - - BOOST_FOREACH (Object& object, list) // boost only - std::cout << object.value_; - - for (Object& object : list) // C++11 only - std::cout << object.value_; - - @endcode - - Because List is mostly STL compliant, it can be passed into STL algorithms: - e.g. `std::for_each()` or `std::find_first_of()`. - - In general, objects placed into a List should be dynamically allocated - although this cannot be enforced at compile time. Since the caller provides - the storage for the object, the caller is also responsible for deleting the - object. An object still exists after being removed from a List, until the - caller deletes it. This means an element can be moved from one List to - another with practically no overhead. - - Unlike the standard containers, an object may only exist in one list at a - time, unless special preparations are made. The Tag template parameter is - used to distinguish between different list types for the same object, - allowing the object to exist in more than one list simultaneously. - - For example, consider an actor system where a global list of actors is - maintained, so that they can each be periodically receive processing - time. We wish to also maintain a list of the subset of actors that require - a domain-dependent update. To achieve this, we declare two tags, the - associated list types, and the list element thusly: - - @code - - struct Actor; // Forward declaration required - - struct ProcessTag { }; - struct UpdateTag { }; - - using ProcessList = List ; - using UpdateList = List ; - - // Derive from both node types so we can be in each list at once. - // - struct Actor : ProcessList::Node, UpdateList::Node - { - bool process (); // returns true if we need an update - void update (); - }; - - @endcode - - @tparam T The base type of element which the list will store - pointers to. - - @tparam Tag An optional unique type name used to distinguish lists and - nodes, when the object can exist in multiple lists simultaneously. - - @ingroup beast_core intrusive -*/ +/** + * Intrusive doubly linked list. + * + * This intrusive List is a container similar in operation to std::list in the + * Standard Template Library (STL). Like all @ref intrusive containers, List + * requires you to first derive your class from List<>::Node: + * + * @code + * + * struct Object : List ::Node + * { + * explicit Object (int value) : value_ (value) + * { + * } + * + * int value_; + * }; + * + * @endcode + * + * Now we define the list, and add a couple of items. + * + * @code + * + * List list; + * + * list.push_back (* (new Object (1))); + * list.push_back (* (new Object (2))); + * + * @endcode + * + * For compatibility with the standard containers, push_back() expects a + * reference to the object. Unlike the standard container, however, push_back() + * places the actual object in the list and not a copy-constructed duplicate. + * + * Iterating over the list follows the same idiom as the STL: + * + * @code + * + * for (List ::iterator iter = list.begin(); iter != list.end; ++iter) + * std::cout << iter->value_; + * + * @endcode + * + * You can even use BOOST_FOREACH, or range based for loops: + * + * @code + * + * BOOST_FOREACH (Object& object, list) // boost only + * std::cout << object.value_; + * + * for (Object& object : list) // C++11 only + * std::cout << object.value_; + * + * @endcode + * + * Because List is mostly STL compliant, it can be passed into STL algorithms: + * e.g. `std::for_each()` or `std::find_first_of()`. + * + * In general, objects placed into a List should be dynamically allocated + * although this cannot be enforced at compile time. Since the caller provides + * the storage for the object, the caller is also responsible for deleting the + * object. An object still exists after being removed from a List, until the + * caller deletes it. This means an element can be moved from one List to + * another with practically no overhead. + * + * Unlike the standard containers, an object may only exist in one list at a + * time, unless special preparations are made. The Tag template parameter is + * used to distinguish between different list types for the same object, + * allowing the object to exist in more than one list simultaneously. + * + * For example, consider an actor system where a global list of actors is + * maintained, so that they can each be periodically receive processing + * time. We wish to also maintain a list of the subset of actors that require + * a domain-dependent update. To achieve this, we declare two tags, the + * associated list types, and the list element thusly: + * + * @code + * + * struct Actor; // Forward declaration required + * + * struct ProcessTag { }; + * struct UpdateTag { }; + * + * using ProcessList = List ; + * using UpdateList = List ; + * + * // Derive from both node types so we can be in each list at once. + * // + * struct Actor : ProcessList::Node, UpdateList::Node + * { + * bool process (); // returns true if we need an update + * void update (); + * }; + * + * @endcode + * + * @tparam T The base type of element which the list will store + * pointers to. + * + * @tparam Tag An optional unique type name used to distinguish lists and + * nodes, when the object can exist in multiple lists simultaneously. + * + * @ingroup beast_core intrusive + */ template class List { @@ -274,7 +277,9 @@ public: using iterator = detail::ListIterator; using const_iterator = detail::ListIterator; - /** Create an empty list. */ + /** + * Create an empty list. + */ List() { head_.prev_ = nullptr; // identifies the head @@ -286,119 +291,133 @@ public: List& operator=(List const&) = delete; - /** Determine if the list is empty. - @return `true` if the list is empty. - */ + /** + * Determine if the list is empty. + * @return `true` if the list is empty. + */ [[nodiscard]] bool empty() const noexcept { return size() == 0; } - /** Returns the number of elements in the list. */ + /** + * Returns the number of elements in the list. + */ [[nodiscard]] size_type size() const noexcept { return size_; } - /** Obtain a reference to the first element. - @invariant The list may not be empty. - @return A reference to the first element. - */ + /** + * Obtain a reference to the first element. + * @invariant The list may not be empty. + * @return A reference to the first element. + */ reference front() noexcept { return element_from(head_.next_); } - /** Obtain a const reference to the first element. - @invariant The list may not be empty. - @return A const reference to the first element. - */ + /** + * Obtain a const reference to the first element. + * @invariant The list may not be empty. + * @return A const reference to the first element. + */ [[nodiscard]] const_reference front() const noexcept { return element_from(head_.next_); } - /** Obtain a reference to the last element. - @invariant The list may not be empty. - @return A reference to the last element. - */ + /** + * Obtain a reference to the last element. + * @invariant The list may not be empty. + * @return A reference to the last element. + */ reference back() noexcept { return element_from(tail_.prev_); } - /** Obtain a const reference to the last element. - @invariant The list may not be empty. - @return A const reference to the last element. - */ + /** + * Obtain a const reference to the last element. + * @invariant The list may not be empty. + * @return A const reference to the last element. + */ [[nodiscard]] const_reference back() const noexcept { return element_from(tail_.prev_); } - /** Obtain an iterator to the beginning of the list. - @return An iterator pointing to the beginning of the list. - */ + /** + * Obtain an iterator to the beginning of the list. + * @return An iterator pointing to the beginning of the list. + */ iterator begin() noexcept { return iterator(head_.next_); } - /** Obtain a const iterator to the beginning of the list. - @return A const iterator pointing to the beginning of the list. - */ + /** + * Obtain a const iterator to the beginning of the list. + * @return A const iterator pointing to the beginning of the list. + */ [[nodiscard]] const_iterator begin() const noexcept { return const_iterator(head_.next_); } - /** Obtain a const iterator to the beginning of the list. - @return A const iterator pointing to the beginning of the list. - */ + /** + * Obtain a const iterator to the beginning of the list. + * @return A const iterator pointing to the beginning of the list. + */ [[nodiscard]] const_iterator cbegin() const noexcept { return const_iterator(head_.next_); } - /** Obtain a iterator to the end of the list. - @return An iterator pointing to the end of the list. - */ + /** + * Obtain a iterator to the end of the list. + * @return An iterator pointing to the end of the list. + */ iterator end() noexcept { return iterator(&tail_); } - /** Obtain a const iterator to the end of the list. - @return A constiterator pointing to the end of the list. - */ + /** + * Obtain a const iterator to the end of the list. + * @return A constiterator pointing to the end of the list. + */ [[nodiscard]] const_iterator end() const noexcept { return const_iterator(&tail_); } - /** Obtain a const iterator to the end of the list - @return A constiterator pointing to the end of the list. - */ + /** + * Obtain a const iterator to the end of the list + * @return A constiterator pointing to the end of the list. + */ [[nodiscard]] const_iterator cend() const noexcept { return const_iterator(&tail_); } - /** Clear the list. - @note This does not free the elements. - */ + /** + * Clear the list. + * @note This does not free the elements. + */ void clear() noexcept { @@ -407,12 +426,13 @@ public: size_ = 0; } - /** Insert an element. - @invariant The element must not already be in the list. - @param pos The location to insert after. - @param element The element to insert. - @return An iterator pointing to the newly inserted element. - */ + /** + * Insert an element. + * @invariant The element must not already be in the list. + * @param pos The location to insert after. + * @param element The element to insert. + * @return An iterator pointing to the newly inserted element. + */ iterator insert(iterator pos, T& element) noexcept { @@ -425,11 +445,12 @@ public: return iterator(node); } - /** Insert another list into this one. - The other list is cleared. - @param pos The location to insert after. - @param other The list to insert. - */ + /** + * Insert another list into this one. + * The other list is cleared. + * @param pos The location to insert after. + * @param other The list to insert. + */ void insert(iterator pos, List& other) noexcept { @@ -445,11 +466,12 @@ public: } } - /** Remove an element. - @invariant The element must exist in the list. - @param pos An iterator pointing to the element to remove. - @return An iterator pointing to the next element after the one removed. - */ + /** + * Remove an element. + * @invariant The element must exist in the list. + * @param pos An iterator pointing to the element to remove. + * @return An iterator pointing to the next element after the one removed. + */ iterator erase(iterator pos) noexcept { @@ -461,20 +483,22 @@ public: return pos; } - /** Insert an element at the beginning of the list. - @invariant The element must not exist in the list. - @param element The element to insert. - */ + /** + * Insert an element at the beginning of the list. + * @invariant The element must not exist in the list. + * @param element The element to insert. + */ iterator pushFront(T& element) noexcept { return insert(begin(), element); } - /** Remove the element at the beginning of the list. - @invariant The list must not be empty. - @return A reference to the popped element. - */ + /** + * Remove the element at the beginning of the list. + * @invariant The list must not be empty. + * @return A reference to the popped element. + */ T& popFront() noexcept { @@ -483,20 +507,22 @@ public: return element; } - /** Append an element at the end of the list. - @invariant The element must not exist in the list. - @param element The element to append. - */ + /** + * Append an element at the end of the list. + * @invariant The element must not exist in the list. + * @param element The element to append. + */ iterator pushBack(T& element) noexcept { return insert(end(), element); } - /** Remove the element at the end of the list. - @invariant The list must not be empty. - @return A reference to the popped element. - */ + /** + * Remove the element at the end of the list. + * @invariant The list must not be empty. + * @return A reference to the popped element. + */ T& popBack() noexcept { @@ -505,7 +531,9 @@ public: return element; } - /** Swap contents with another list. */ + /** + * Swap contents with another list. + */ void swap(List& other) noexcept { @@ -515,42 +543,46 @@ public: append(temp); } - /** Insert another list at the beginning of this list. - The other list is cleared. - @param list The other list to insert. - */ + /** + * Insert another list at the beginning of this list. + * The other list is cleared. + * @param list The other list to insert. + */ iterator prepend(List& list) noexcept { return insert(begin(), list); } - /** Append another list at the end of this list. - The other list is cleared. - @param list the other list to append. - */ + /** + * Append another list at the end of this list. + * The other list is cleared. + * @param list the other list to append. + */ iterator append(List& list) noexcept { return insert(end(), list); } - /** Obtain an iterator from an element. - @invariant The element must exist in the list. - @param element The element to obtain an iterator for. - @return An iterator to the element. - */ + /** + * Obtain an iterator from an element. + * @invariant The element must exist in the list. + * @param element The element to obtain an iterator for. + * @return An iterator to the element. + */ iterator iteratorTo(T& element) const noexcept { return iterator(static_cast(&element)); } - /** Obtain a const iterator from an element. - @invariant The element must exist in the list. - @param element The element to obtain an iterator for. - @return A const iterator to the element. - */ + /** + * Obtain a const iterator from an element. + * @invariant The element must exist in the list. + * @param element The element to obtain an iterator for. + * @return A const iterator to the element. + */ [[nodiscard]] const_iterator constIteratorTo(T const& element) const noexcept { diff --git a/include/xrpl/beast/core/LockFreeStack.h b/include/xrpl/beast/core/LockFreeStack.h index dd135d2d98..849edc8fce 100644 --- a/include/xrpl/beast/core/LockFreeStack.h +++ b/include/xrpl/beast/core/LockFreeStack.h @@ -103,18 +103,19 @@ operator!=( //------------------------------------------------------------------------------ -/** Multiple Producer, Multiple Consumer (MPMC) intrusive stack. - - This stack is implemented using the same intrusive interface as List. - All mutations are lock-free. - - The caller is responsible for preventing the "ABA" problem: - http://en.wikipedia.org/wiki/ABA_problem - - @param Tag A type name used to distinguish lists and nodes, for - putting objects in multiple lists. If this parameter is - omitted, the default tag is used. -*/ +/** + * Multiple Producer, Multiple Consumer (MPMC) intrusive stack. + * + * This stack is implemented using the same intrusive interface as List. + * All mutations are lock-free. + * + * The caller is responsible for preventing the "ABA" problem: + * http://en.wikipedia.org/wiki/ABA_problem + * + * @param Tag A type name used to distinguish lists and nodes, for + * putting objects in multiple lists. If this parameter is + * omitted, the default tag is used. + */ template class LockFreeStack { @@ -162,24 +163,27 @@ public: LockFreeStack& operator=(LockFreeStack const&) = delete; - /** Returns true if the stack is empty. */ + /** + * Returns true if the stack is empty. + */ [[nodiscard]] bool empty() const { return head_.load() == &end_; } - /** Push a node onto the stack. - The caller is responsible for preventing the ABA problem. - This operation is lock-free. - Thread safety: - Safe to call from any thread. - - @param node The node to push. - - @return `true` if the stack was previously empty. If multiple threads - are attempting to push, only one will receive `true`. - */ + /** + * Push a node onto the stack. + * The caller is responsible for preventing the ABA problem. + * This operation is lock-free. + * Thread safety: + * Safe to call from any thread. + * + * @param node The node to push. + * + * @return `true` if the stack was previously empty. If multiple threads + * are attempting to push, only one will receive `true`. + */ // VFALCO NOTE Fix this, shouldn't it be a reference like intrusive list? bool pushFront(Node* node) @@ -195,15 +199,16 @@ public: return first; } - /** Pop an element off the stack. - The caller is responsible for preventing the ABA problem. - This operation is lock-free. - Thread safety: - Safe to call from any thread. - - @return The element that was popped, or `nullptr` if the stack - was empty. - */ + /** + * Pop an element off the stack. + * The caller is responsible for preventing the ABA problem. + * This operation is lock-free. + * Thread safety: + * Safe to call from any thread. + * + * @return The element that was popped, or `nullptr` if the stack + * was empty. + */ Element* popFront() { @@ -219,12 +224,13 @@ public: return static_cast(node); } - /** Return a forward iterator to the beginning or end of the stack. - Undefined behavior results if push_front or pop_front is called - while an iteration is in progress. - Thread safety: - Caller is responsible for synchronization. - */ + /** + * Return a forward iterator to the beginning or end of the stack. + * Undefined behavior results if push_front or pop_front is called + * while an iteration is in progress. + * Thread safety: + * Caller is responsible for synchronization. + */ /** @{ */ iterator begin() diff --git a/include/xrpl/beast/core/SemanticVersion.h b/include/xrpl/beast/core/SemanticVersion.h index 826a43d3f8..338942c252 100644 --- a/include/xrpl/beast/core/SemanticVersion.h +++ b/include/xrpl/beast/core/SemanticVersion.h @@ -6,13 +6,14 @@ namespace beast { -/** A Semantic Version number. - - Identifies the build of a particular version of software using - the Semantic Versioning Specification described here: - - http://semver.org/ -*/ +/** + * A Semantic Version number. + * + * Identifies the build of a particular version of software using + * the Semantic Versioning Specification described here: + * + * http://semver.org/ + */ class SemanticVersion { public: @@ -29,14 +30,17 @@ public: SemanticVersion(std::string_view version); - /** Parse a semantic version string. - The parsing is as strict as possible. - @return `true` if the string was parsed. - */ + /** + * Parse a semantic version string. + * The parsing is as strict as possible. + * @return `true` if the string was parsed. + */ bool parse(std::string_view input); - /** Produce a string from semantic version components. */ + /** + * Produce a string from semantic version components. + */ [[nodiscard]] std::string print() const; @@ -52,9 +56,10 @@ public: } }; -/** Compare two SemanticVersions against each other. - The comparison follows the rules as per the specification. -*/ +/** + * Compare two SemanticVersions against each other. + * The comparison follows the rules as per the specification. + */ int compare(SemanticVersion const& lhs, SemanticVersion const& rhs); diff --git a/include/xrpl/beast/hash/hash_append.h b/include/xrpl/beast/hash/hash_append.h index 3592ffbfe8..c5374f95e5 100644 --- a/include/xrpl/beast/hash/hash_append.h +++ b/include/xrpl/beast/hash/hash_append.h @@ -135,19 +135,20 @@ struct IsUniquelyRepresented> explicit IsUniquelyRepresented() = default; }; -/** Metafunction returning `true` if the type can be hashed in one call. - - For `IsContiguouslyHashable::value` to be true, then for every - combination of possible values of `T` held in `x` and `y`, - if `x == y`, then it must be true that `memcmp(&x, &y, sizeof(T))` - return 0; i.e. that `x` and `y` are represented by the same bit pattern. - - For example: A two's complement `int` should be contiguously hashable. - Every bit pattern produces a unique value that does not compare equal to - any other bit pattern's value. A IEEE floating point should not be - contiguously hashable because -0. and 0. have different bit patterns, - though they compare equal. -*/ +/** + * Metafunction returning `true` if the type can be hashed in one call. + * + * For `IsContiguouslyHashable::value` to be true, then for every + * combination of possible values of `T` held in `x` and `y`, + * if `x == y`, then it must be true that `memcmp(&x, &y, sizeof(T))` + * return 0; i.e. that `x` and `y` are represented by the same bit pattern. + * + * For example: A two's complement `int` should be contiguously hashable. + * Every bit pattern produces a unique value that does not compare equal to + * any other bit pattern's value. A IEEE floating point should not be + * contiguously hashable because -0. and 0. have different bit patterns, + * though they compare equal. + */ /** @{ */ template struct IsContiguouslyHashable @@ -172,29 +173,30 @@ struct IsContiguouslyHashable //------------------------------------------------------------------------------ -/** Logically concatenate input data to a `Hasher`. - - Hasher requirements: - - `X` is the type `Hasher` - `h` is a value of type `x` - `p` is a value convertible to `void const*` - `n` is a value of type `std::size_t`, greater than zero - - Expression: - `h.append (p, n);` - Throws: - Never - Effect: - Adds the input data to the hasher state. - - Expression: - `static_cast(j)` - Throws: - Never - Effect: - Returns the resulting hash of all the input data. -*/ +/** + * Logically concatenate input data to a `Hasher`. + * + * Hasher requirements: + * + * `X` is the type `Hasher` + * `h` is a value of type `x` + * `p` is a value convertible to `void const*` + * `n` is a value of type `std::size_t`, greater than zero + * + * Expression: + * `h.append (p, n);` + * Throws: + * Never + * Effect: + * Adds the input data to the hasher state. + * + * Expression: + * `static_cast(j)` + * Throws: + * Never + * Effect: + * Returns the resulting hash of all the input data. + */ /** @{ */ // scalars diff --git a/include/xrpl/beast/insight/Collector.h b/include/xrpl/beast/insight/Collector.h index 3f83e329d4..9da2a8bb74 100644 --- a/include/xrpl/beast/insight/Collector.h +++ b/include/xrpl/beast/insight/Collector.h @@ -12,16 +12,17 @@ namespace beast::insight { -/** Interface for a manager that allows collection of metrics. - - To export metrics from a class, pass and save a shared_ptr to this - interface in the class constructor. Create the metric objects - as desired (counters, events, gauges, meters, and an optional hook) - using the interface. - - @see Counter, Event, Gauge, Hook, Meter - @see NullCollector, StatsDCollector -*/ +/** + * Interface for a manager that allows collection of metrics. + * + * To export metrics from a class, pass and save a shared_ptr to this + * interface in the class constructor. Create the metric objects + * as desired (counters, events, gauges, meters, and an optional hook) + * using the interface. + * + * @see Counter, Event, Gauge, Hook, Meter + * @see NullCollector, StatsDCollector + */ class Collector { public: @@ -29,18 +30,19 @@ public: virtual ~Collector() = 0; - /** Create a hook. - - A hook is called at each collection interval, on an implementation - defined thread. This is a convenience facility for gathering metrics - in the polling style. The typical usage is to update all the metrics - of interest in the handler. - - Handler will be called with this signature: - void handler (void) - - @see Hook - */ + /** + * Create a hook. + * + * A hook is called at each collection interval, on an implementation + * defined thread. This is a convenience facility for gathering metrics + * in the polling style. The typical usage is to update all the metrics + * of interest in the handler. + * + * Handler will be called with this signature: + * void handler (void) + * + * @see Hook + */ /** @{ */ template Hook @@ -53,9 +55,10 @@ public: makeHook(HookImpl::HandlerType const& handler) = 0; /** @} */ - /** Create a counter with the specified name. - @see Counter - */ + /** + * Create a counter with the specified name. + * @see Counter + */ /** @{ */ virtual Counter makeCounter(std::string const& name) = 0; @@ -69,9 +72,10 @@ public: } /** @} */ - /** Create an event with the specified name. - @see Event - */ + /** + * Create an event with the specified name. + * @see Event + */ /** @{ */ virtual Event makeEvent(std::string const& name) = 0; @@ -85,9 +89,10 @@ public: } /** @} */ - /** Create a gauge with the specified name. - @see Gauge - */ + /** + * Create a gauge with the specified name. + * @see Gauge + */ /** @{ */ virtual Gauge makeGauge(std::string const& name) = 0; @@ -101,9 +106,10 @@ public: } /** @} */ - /** Create a meter with the specified name. - @see Meter - */ + /** + * Create a meter with the specified name. + * @see Meter + */ /** @{ */ virtual Meter makeMeter(std::string const& name) = 0; diff --git a/include/xrpl/beast/insight/Counter.h b/include/xrpl/beast/insight/Counter.h index 482808b2c7..875fadf33a 100644 --- a/include/xrpl/beast/insight/Counter.h +++ b/include/xrpl/beast/insight/Counter.h @@ -7,34 +7,39 @@ namespace beast::insight { -/** A metric for measuring an integral value. - - A counter is a gauge calculated at the server. The owner of the counter - may increment and decrement the value by an amount. - - This is a lightweight reference wrapper which is cheap to copy and assign. - When the last reference goes away, the metric is no longer collected. -*/ +/** + * A metric for measuring an integral value. + * + * A counter is a gauge calculated at the server. The owner of the counter + * may increment and decrement the value by an amount. + * + * This is a lightweight reference wrapper which is cheap to copy and assign. + * When the last reference goes away, the metric is no longer collected. + */ class Counter final { public: using value_type = CounterImpl::value_type; - /** Create a null metric. - A null metric reports no information. - */ + /** + * Create a null metric. + * A null metric reports no information. + */ Counter() = default; - /** Create the metric reference the specified implementation. - Normally this won't be called directly. Instead, call the appropriate - factory function in the Collector interface. - @see Collector. - */ + /** + * Create the metric reference the specified implementation. + * Normally this won't be called directly. Instead, call the appropriate + * factory function in the Collector interface. + * @see Collector. + */ explicit Counter(std::shared_ptr impl) : impl_(std::move(impl)) { } - /** Increment the counter. */ + /** + * Increment the counter. + */ /** @{ */ void increment(value_type amount) const diff --git a/include/xrpl/beast/insight/Event.h b/include/xrpl/beast/insight/Event.h index afccf9baba..c3ff1a8877 100644 --- a/include/xrpl/beast/insight/Event.h +++ b/include/xrpl/beast/insight/Event.h @@ -8,35 +8,40 @@ namespace beast::insight { -/** A metric for reporting event timing. - - An event is an operation that has an associated millisecond time, or - other integral value. Because events happen at a specific moment, the - metric only supports a push-style interface. - - This is a lightweight reference wrapper which is cheap to copy and assign. - When the last reference goes away, the metric is no longer collected. -*/ +/** + * A metric for reporting event timing. + * + * An event is an operation that has an associated millisecond time, or + * other integral value. Because events happen at a specific moment, the + * metric only supports a push-style interface. + * + * This is a lightweight reference wrapper which is cheap to copy and assign. + * When the last reference goes away, the metric is no longer collected. + */ class Event final { public: using value_type = EventImpl::value_type; - /** Create a null metric. - A null metric reports no information. - */ + /** + * Create a null metric. + * A null metric reports no information. + */ Event() = default; - /** Create the metric reference the specified implementation. - Normally this won't be called directly. Instead, call the appropriate - factory function in the Collector interface. - @see Collector. - */ + /** + * Create the metric reference the specified implementation. + * Normally this won't be called directly. Instead, call the appropriate + * factory function in the Collector interface. + * @see Collector. + */ explicit Event(std::shared_ptr impl) : impl_(std::move(impl)) { } - /** Push an event notification. */ + /** + * Push an event notification. + */ template void notify(std::chrono::duration const& value) const diff --git a/include/xrpl/beast/insight/Gauge.h b/include/xrpl/beast/insight/Gauge.h index 9a23ea6665..ef62e252b3 100644 --- a/include/xrpl/beast/insight/Gauge.h +++ b/include/xrpl/beast/insight/Gauge.h @@ -7,40 +7,44 @@ namespace beast::insight { -/** A metric for measuring an integral value. - - A gauge is an instantaneous measurement of a value, like the gas gauge - in a car. The caller directly sets the value, or adjusts it by a - specified amount. The value is kept in the client rather than the collector. - - This is a lightweight reference wrapper which is cheap to copy and assign. - When the last reference goes away, the metric is no longer collected. -*/ +/** + * A metric for measuring an integral value. + * + * A gauge is an instantaneous measurement of a value, like the gas gauge + * in a car. The caller directly sets the value, or adjusts it by a + * specified amount. The value is kept in the client rather than the collector. + * + * This is a lightweight reference wrapper which is cheap to copy and assign. + * When the last reference goes away, the metric is no longer collected. + */ class Gauge final { public: using value_type = GaugeImpl::value_type; using difference_type = GaugeImpl::difference_type; - /** Create a null metric. - A null metric reports no information. - */ + /** + * Create a null metric. + * A null metric reports no information. + */ Gauge() = default; - /** Create the metric reference the specified implementation. - Normally this won't be called directly. Instead, call the appropriate - factory function in the Collector interface. - @see Collector. - */ + /** + * Create the metric reference the specified implementation. + * Normally this won't be called directly. Instead, call the appropriate + * factory function in the Collector interface. + * @see Collector. + */ explicit Gauge(std::shared_ptr impl) : impl_(std::move(impl)) { } - /** Set the value on the gauge. - A Collector implementation should combine multiple calls to value - changes into a single change if the calls occur within a single - collection interval. - */ + /** + * Set the value on the gauge. + * A Collector implementation should combine multiple calls to value + * changes into a single change if the calls occur within a single + * collection interval. + */ /** @{ */ void set(value_type value) const @@ -62,7 +66,9 @@ public: } /** @} */ - /** Adjust the value of the gauge. */ + /** + * Adjust the value of the gauge. + */ /** @{ */ void increment(difference_type amount) const diff --git a/include/xrpl/beast/insight/Group.h b/include/xrpl/beast/insight/Group.h index 3e0eb93452..ecf7709546 100644 --- a/include/xrpl/beast/insight/Group.h +++ b/include/xrpl/beast/insight/Group.h @@ -7,13 +7,17 @@ namespace beast::insight { -/** A collector front-end that manages a group of metrics. */ +/** + * A collector front-end that manages a group of metrics. + */ class Group : public Collector { public: using ptr = std::shared_ptr; - /** Returns the name of this group, for diagnostics. */ + /** + * Returns the name of this group, for diagnostics. + */ [[nodiscard]] virtual std::string const& name() const = 0; }; diff --git a/include/xrpl/beast/insight/Groups.h b/include/xrpl/beast/insight/Groups.h index cfe4d99bdc..77fc2d3336 100644 --- a/include/xrpl/beast/insight/Groups.h +++ b/include/xrpl/beast/insight/Groups.h @@ -8,13 +8,17 @@ namespace beast::insight { -/** A container for managing a set of metric groups. */ +/** + * A container for managing a set of metric groups. + */ class Groups { public: virtual ~Groups() = 0; - /** Find or create a new collector with a given name. */ + /** + * Find or create a new collector with a given name. + */ /** @{ */ virtual Group::ptr const& get(std::string const& name) = 0; @@ -27,7 +31,9 @@ public: /** @} */ }; -/** Create a group container that uses the specified collector. */ +/** + * Create a group container that uses the specified collector. + */ std::unique_ptr makeGroups(Collector::ptr const& collector); diff --git a/include/xrpl/beast/insight/Hook.h b/include/xrpl/beast/insight/Hook.h index 8dbe5a4be0..572a9ffcb4 100644 --- a/include/xrpl/beast/insight/Hook.h +++ b/include/xrpl/beast/insight/Hook.h @@ -7,20 +7,24 @@ namespace beast::insight { -/** A reference to a handler for performing polled collection. */ +/** + * A reference to a handler for performing polled collection. + */ class Hook final { public: - /** Create a null hook. - A null hook has no associated handler. - */ + /** + * Create a null hook. + * A null hook has no associated handler. + */ Hook() = default; - /** Create a hook referencing the specified implementation. - Normally this won't be called directly. Instead, call the appropriate - factory function in the Collector interface. - @see Collector. - */ + /** + * Create a hook referencing the specified implementation. + * Normally this won't be called directly. Instead, call the appropriate + * factory function in the Collector interface. + * @see Collector. + */ explicit Hook(std::shared_ptr impl) : impl_(std::move(impl)) { } diff --git a/include/xrpl/beast/insight/Meter.h b/include/xrpl/beast/insight/Meter.h index 25ffabd928..ac2f3a352c 100644 --- a/include/xrpl/beast/insight/Meter.h +++ b/include/xrpl/beast/insight/Meter.h @@ -7,33 +7,38 @@ namespace beast::insight { -/** A metric for measuring an integral value. - - A meter may be thought of as an increment-only counter. - - This is a lightweight reference wrapper which is cheap to copy and assign. - When the last reference goes away, the metric is no longer collected. -*/ +/** + * A metric for measuring an integral value. + * + * A meter may be thought of as an increment-only counter. + * + * This is a lightweight reference wrapper which is cheap to copy and assign. + * When the last reference goes away, the metric is no longer collected. + */ class Meter final { public: using value_type = MeterImpl::value_type; - /** Create a null metric. - A null metric reports no information. - */ + /** + * Create a null metric. + * A null metric reports no information. + */ Meter() = default; - /** Create the metric reference the specified implementation. - Normally this won't be called directly. Instead, call the appropriate - factory function in the Collector interface. - @see Collector. - */ + /** + * Create the metric reference the specified implementation. + * Normally this won't be called directly. Instead, call the appropriate + * factory function in the Collector interface. + * @see Collector. + */ explicit Meter(std::shared_ptr impl) : impl_(std::move(impl)) { } - /** Increment the meter. */ + /** + * Increment the meter. + */ /** @{ */ void increment(value_type amount) const diff --git a/include/xrpl/beast/insight/NullCollector.h b/include/xrpl/beast/insight/NullCollector.h index 67903420fa..ffafe6d6d5 100644 --- a/include/xrpl/beast/insight/NullCollector.h +++ b/include/xrpl/beast/insight/NullCollector.h @@ -6,7 +6,9 @@ namespace beast::insight { -/** A Collector which does not collect metrics. */ +/** + * A Collector which does not collect metrics. + */ class NullCollector : public Collector { public: diff --git a/include/xrpl/beast/insight/StatsDCollector.h b/include/xrpl/beast/insight/StatsDCollector.h index 9a438c48f1..e14d3a27ff 100644 --- a/include/xrpl/beast/insight/StatsDCollector.h +++ b/include/xrpl/beast/insight/StatsDCollector.h @@ -9,20 +9,22 @@ namespace beast::insight { -/** A Collector that reports metrics to a StatsD server. - Reference: - https://github.com/b/statsd_spec -*/ +/** + * A Collector that reports metrics to a StatsD server. + * Reference: + * https://github.com/b/statsd_spec + */ class StatsDCollector : public Collector { public: explicit StatsDCollector() = default; - /** Create a StatsD collector. - @param address The IP address and port of the StatsD server. - @param prefix A string pre-pended before each metric name. - @param journal Destination for logging output. - */ + /** + * Create a StatsD collector. + * @param address The IP address and port of the StatsD server. + * @param prefix A string pre-pended before each metric name. + * @param journal Destination for logging output. + */ static std::shared_ptr make(IP::Endpoint const& address, std::string const& prefix, Journal journal); }; diff --git a/include/xrpl/beast/net/IPAddress.h b/include/xrpl/beast/net/IPAddress.h index f4327b7b8a..4f4fb189a6 100644 --- a/include/xrpl/beast/net/IPAddress.h +++ b/include/xrpl/beast/net/IPAddress.h @@ -19,42 +19,54 @@ namespace IP { using Address = boost::asio::ip::address; -/** Returns the address represented as a string. */ +/** + * Returns the address represented as a string. + */ inline std::string to_string(Address const& addr) { return addr.to_string(); } -/** Returns `true` if this is a loopback address. */ +/** + * Returns `true` if this is a loopback address. + */ inline bool isLoopback(Address const& addr) { return addr.is_loopback(); } -/** Returns `true` if the address is unspecified. */ +/** + * Returns `true` if the address is unspecified. + */ inline bool isUnspecified(Address const& addr) { return addr.is_unspecified(); } -/** Returns `true` if the address is a multicast address. */ +/** + * Returns `true` if the address is a multicast address. + */ inline bool isMulticast(Address const& addr) { return addr.is_multicast(); } -/** Returns `true` if the address is a private unroutable address. */ +/** + * Returns `true` if the address is a private unroutable address. + */ inline bool isPrivate(Address const& addr) { return (addr.is_v4()) ? isPrivate(addr.to_v4()) : isPrivate(addr.to_v6()); } -/** Returns `true` if the address is a public routable address. */ +/** + * Returns `true` if the address is a public routable address. + */ inline bool isPublic(Address const& addr) { diff --git a/include/xrpl/beast/net/IPAddressConversion.h b/include/xrpl/beast/net/IPAddressConversion.h index b5fb697233..73777cf841 100644 --- a/include/xrpl/beast/net/IPAddressConversion.h +++ b/include/xrpl/beast/net/IPAddressConversion.h @@ -6,23 +6,29 @@ namespace beast::IP { -/** Convert to Endpoint. - The port is set to zero. -*/ +/** + * Convert to Endpoint. + * The port is set to zero. + */ Endpoint fromAsio(boost::asio::ip::address const& address); -/** Convert to Endpoint. */ +/** + * Convert to Endpoint. + */ Endpoint fromAsio(boost::asio::ip::tcp::endpoint const& endpoint); -/** Convert to asio::ip::address. - The port is ignored. -*/ +/** + * Convert to asio::ip::address. + * The port is ignored. + */ boost::asio::ip::address toAsioAddress(Endpoint const& endpoint); -/** Convert to asio::ip::tcp::endpoint. */ +/** + * Convert to asio::ip::tcp::endpoint. + */ boost::asio::ip::tcp::endpoint toAsioEndpoint(Endpoint const& endpoint); diff --git a/include/xrpl/beast/net/IPAddressV4.h b/include/xrpl/beast/net/IPAddressV4.h index 9367fbe1eb..94943af3ea 100644 --- a/include/xrpl/beast/net/IPAddressV4.h +++ b/include/xrpl/beast/net/IPAddressV4.h @@ -6,17 +6,22 @@ namespace beast::IP { using AddressV4 = boost::asio::ip::address_v4; -/** Returns `true` if the address is a private unroutable address. */ +/** + * Returns `true` if the address is a private unroutable address. + */ bool isPrivate(AddressV4 const& addr); -/** Returns `true` if the address is a public routable address. */ +/** + * Returns `true` if the address is a public routable address. + */ bool isPublic(AddressV4 const& addr); -/** Returns the address class for the given address. - @note Class 'D' represents multicast addresses (224.*.*.*). -*/ +/** + * Returns the address class for the given address. + * @note Class 'D' represents multicast addresses (224.*.*.*). + */ char getClass(AddressV4 const& address); diff --git a/include/xrpl/beast/net/IPAddressV6.h b/include/xrpl/beast/net/IPAddressV6.h index 1bfa079990..b51cb62532 100644 --- a/include/xrpl/beast/net/IPAddressV6.h +++ b/include/xrpl/beast/net/IPAddressV6.h @@ -6,11 +6,15 @@ namespace beast::IP { using AddressV6 = boost::asio::ip::address_v6; -/** Returns `true` if the address is a private unroutable address. */ +/** + * Returns `true` if the address is a private unroutable address. + */ bool isPrivate(AddressV6 const& addr); -/** Returns `true` if the address is a public routable address. */ +/** + * Returns `true` if the address is a public routable address. + */ bool isPublic(AddressV6 const& addr); diff --git a/include/xrpl/beast/net/IPEndpoint.h b/include/xrpl/beast/net/IPEndpoint.h index 0b661108f2..c4b269e9c3 100644 --- a/include/xrpl/beast/net/IPEndpoint.h +++ b/include/xrpl/beast/net/IPEndpoint.h @@ -17,51 +17,68 @@ namespace beast::IP { using Port = std::uint16_t; -/** A version-independent IP address and port combination. */ +/** + * A version-independent IP address and port combination. + */ class Endpoint { public: - /** Create an unspecified endpoint. */ + /** + * Create an unspecified endpoint. + */ Endpoint(); - /** Create an endpoint from the address and optional port. */ + /** + * Create an endpoint from the address and optional port. + */ explicit Endpoint(Address addr, Port port = 0); - /** Create an Endpoint from a string. - If the port is omitted, the endpoint will have a zero port. - @return An optional endpoint; will be `std::nullopt` on failure - */ + /** + * Create an Endpoint from a string. + * If the port is omitted, the endpoint will have a zero port. + * @return An optional endpoint; will be `std::nullopt` on failure + */ static std::optional fromStringChecked(std::string const& s); static Endpoint fromString(std::string const& s); - /** Returns a string representing the endpoint. */ + /** + * Returns a string representing the endpoint. + */ [[nodiscard]] std::string toString() const; - /** Returns the port number on the endpoint. */ + /** + * Returns the port number on the endpoint. + */ [[nodiscard]] Port port() const { return port_; } - /** Returns a new Endpoint with a different port. */ + /** + * Returns a new Endpoint with a different port. + */ [[nodiscard]] Endpoint atPort(Port port) const { return Endpoint(addr_, port); } - /** Returns the address portion of this endpoint. */ + /** + * Returns the address portion of this endpoint. + */ [[nodiscard]] Address const& address() const { return addr_; } - /** Convenience accessors for the address part. */ + /** + * Convenience accessors for the address part. + */ /** @{ */ [[nodiscard]] bool isV4() const @@ -85,7 +102,9 @@ public: } /** @} */ - /** Arithmetic comparison. */ + /** + * Arithmetic comparison. + */ /** @{ */ friend bool operator==(Endpoint const& lhs, Endpoint const& rhs); @@ -131,35 +150,45 @@ private: // Properties -/** Returns `true` if the endpoint is a loopback address. */ +/** + * Returns `true` if the endpoint is a loopback address. + */ inline bool isLoopback(Endpoint const& endpoint) { return isLoopback(endpoint.address()); } -/** Returns `true` if the endpoint is unspecified. */ +/** + * Returns `true` if the endpoint is unspecified. + */ inline bool isUnspecified(Endpoint const& endpoint) { return isUnspecified(endpoint.address()); } -/** Returns `true` if the endpoint is a multicast address. */ +/** + * Returns `true` if the endpoint is a multicast address. + */ inline bool isMulticast(Endpoint const& endpoint) { return isMulticast(endpoint.address()); } -/** Returns `true` if the endpoint is a private unroutable address. */ +/** + * Returns `true` if the endpoint is a private unroutable address. + */ inline bool isPrivate(Endpoint const& endpoint) { return isPrivate(endpoint.address()); } -/** Returns `true` if the endpoint is a public routable address. */ +/** + * Returns `true` if the endpoint is a public routable address. + */ inline bool isPublic(Endpoint const& endpoint) { @@ -168,14 +197,18 @@ isPublic(Endpoint const& endpoint) //------------------------------------------------------------------------------ -/** Returns the endpoint represented as a string. */ +/** + * Returns the endpoint represented as a string. + */ inline std::string to_string(Endpoint const& endpoint) { return endpoint.toString(); } -/** Output stream conversion. */ +/** + * Output stream conversion. + */ template OutputStream& operator<<(OutputStream& os, Endpoint const& endpoint) @@ -184,7 +217,9 @@ operator<<(OutputStream& os, Endpoint const& endpoint) return os; } -/** Input stream conversion. */ +/** + * Input stream conversion. + */ std::istream& operator>>(std::istream& is, Endpoint& endpoint); @@ -193,7 +228,9 @@ operator>>(std::istream& is, Endpoint& endpoint); //------------------------------------------------------------------------------ namespace std { -/** std::hash support. */ +/** + * std::hash support. + */ template <> struct hash<::beast::IP::Endpoint> { @@ -208,7 +245,9 @@ struct hash<::beast::IP::Endpoint> } // namespace std namespace boost { -/** boost::hash support. */ +/** + * boost::hash support. + */ template <> struct hash<::beast::IP::Endpoint> { diff --git a/include/xrpl/beast/rfc2616.h b/include/xrpl/beast/rfc2616.h index 7c681ab140..bd9a78fddb 100644 --- a/include/xrpl/beast/rfc2616.h +++ b/include/xrpl/beast/rfc2616.h @@ -30,17 +30,20 @@ struct CiEqualPred } }; -/** Returns `true` if `c` is linear white space. - - This excludes the CRLF sequence allowed for line continuations. -*/ +/** + * Returns `true` if `c` is linear white space. + * + * This excludes the CRLF sequence allowed for line continuations. + */ inline bool isLws(char c) { return c == ' ' || c == '\t'; } -/** Returns `true` if `c` is any whitespace character. */ +/** + * Returns `true` if `c` is any whitespace character. + */ inline bool isWhite(char c) { @@ -87,14 +90,15 @@ trimRight(String const& s) } // namespace detail -/** Parse a character sequence of values separated by commas. - Double quotes and escape sequences will be converted. Excess white - space, commas, double quotes, and empty elements are not copied. - Format: - #(token|quoted-string) - Reference: - http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2 -*/ +/** + * Parse a character sequence of values separated by commas. + * Double quotes and escape sequences will be converted. Excess white + * space, commas, double quotes, and empty elements are not copied. + * Format: + * #(token|quoted-string) + * Reference: + * http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2 + */ template < class FwdIt, class Result = std::vector::value_type>>, @@ -189,14 +193,15 @@ splitCommas(boost::beast::string_view const& s) //------------------------------------------------------------------------------ -/** Iterates through a comma separated list. - - Meets the requirements of ForwardIterator. - - List defined in rfc2616 2.1. - - @note Values returned may contain backslash escapes. -*/ +/** + * Iterates through a comma separated list. + * + * Meets the requirements of ForwardIterator. + * + * List defined in rfc2616 2.1. + * + * @note Values returned may contain backslash escapes. + */ class ListIterator { using iter_type = boost::string_ref::const_iterator; @@ -323,17 +328,20 @@ ListIterator::increment() } } } -/** Returns true if two strings are equal. - - A case-insensitive comparison is used. -*/ +/** + * Returns true if two strings are equal. + * + * A case-insensitive comparison is used. + */ inline bool ciEqual(boost::string_ref s1, boost::string_ref s2) { return boost::range::equal(s1, s2, detail::CiEqualPred{}); } -/** Returns a range representing the list. */ +/** + * Returns a range representing the list. + */ inline boost::iterator_range makeList(boost::string_ref const& field) { @@ -341,10 +349,11 @@ makeList(boost::string_ref const& field) ListIterator{field.begin(), field.end()}, ListIterator{field.end(), field.end()}}; } -/** Returns true if the specified token exists in the list. - - A case-insensitive comparison is used. -*/ +/** + * Returns true if the specified token exists in the list. + * + * A case-insensitive comparison is used. + */ template bool tokenInList(boost::string_ref const& value, boost::string_ref const& token) diff --git a/include/xrpl/beast/test/yield_to.h b/include/xrpl/beast/test/yield_to.h index 1a34ec436e..b3aa482dd5 100644 --- a/include/xrpl/beast/test/yield_to.h +++ b/include/xrpl/beast/test/yield_to.h @@ -19,12 +19,13 @@ namespace beast::test { -/** Mix-in to support tests using asio coroutines. - - Derive from this class and use yield_to to launch test - functions inside coroutines. This is handy for testing - asynchronous asio code. -*/ +/** + * Mix-in to support tests using asio coroutines. + * + * Derive from this class and use yield_to to launch test + * functions inside coroutines. This is handy for testing + * asynchronous asio code. + */ class EnableYieldTo { protected: @@ -38,7 +39,9 @@ private: std::size_t running_ = 0; public: - /// The type of yield context passed to functions. + /** + * The type of yield context passed to functions. + */ using yield_context = boost::asio::yield_context; explicit EnableYieldTo(std::size_t concurrency = 1) : work_(boost::asio::make_work_guard(ios_)) @@ -57,24 +60,27 @@ public: t.join(); } - /// Return the `io_context` associated with the object + /** + * Return the `io_context` associated with the object + */ boost::asio::io_context& getIoContext() { return ios_; } - /** Run one or more functions, each in a coroutine. - - This call will block until all coroutines terminate. - - Each functions should have this signature: - @code - void f(yield_context); - @endcode - - @param fn... One or more functions to invoke. - */ + /** + * Run one or more functions, each in a coroutine. + * + * This call will block until all coroutines terminate. + * + * Each functions should have this signature: + * @code + * void f(yield_context); + * @endcode + * + * @param fn... One or more functions to invoke. + */ #if BEAST_DOXYGEN template void diff --git a/include/xrpl/beast/unit_test/amount.h b/include/xrpl/beast/unit_test/amount.h index 3a392f393f..c1e4357753 100644 --- a/include/xrpl/beast/unit_test/amount.h +++ b/include/xrpl/beast/unit_test/amount.h @@ -10,7 +10,9 @@ namespace beast::unit_test { -/** Utility for producing nicely composed output of amounts with units. */ +/** + * Utility for producing nicely composed output of amounts with units. + */ class Amount { private: diff --git a/include/xrpl/beast/unit_test/detail/const_container.h b/include/xrpl/beast/unit_test/detail/const_container.h index 6826bf4258..9f4646cbdb 100644 --- a/include/xrpl/beast/unit_test/detail/const_container.h +++ b/include/xrpl/beast/unit_test/detail/const_container.h @@ -6,10 +6,11 @@ namespace beast::unit_test::detail { -/** Adapter to constrain a container interface. - The interface allows for limited read only operations. Derived classes - provide additional behavior. -*/ +/** + * Adapter to constrain a container interface. + * The interface allows for limited read only operations. Derived classes + * provide additional behavior. + */ template class ConstContainer { @@ -38,21 +39,27 @@ public: using iterator = cont_type::const_iterator; using const_iterator = cont_type::const_iterator; - /** Returns `true` if the container is empty. */ + /** + * Returns `true` if the container is empty. + */ [[nodiscard]] bool empty() const { return cont_.empty(); } - /** Returns the number of items in the container. */ + /** + * Returns the number of items in the container. + */ [[nodiscard]] size_type size() const { return cont_.size(); } - /** Returns forward iterators for traversal. */ + /** + * Returns forward iterators for traversal. + */ /** @{ */ [[nodiscard]] const_iterator begin() const diff --git a/include/xrpl/beast/unit_test/global_suites.h b/include/xrpl/beast/unit_test/global_suites.h index 72ed738bdb..18e5bc3a6b 100644 --- a/include/xrpl/beast/unit_test/global_suites.h +++ b/include/xrpl/beast/unit_test/global_suites.h @@ -10,7 +10,9 @@ namespace beast::unit_test { namespace detail { -/// Holds test suites registered during static initialization. +/** + * Holds test suites registered during static initialization. + */ inline SuiteList& globalSuites() { @@ -34,7 +36,9 @@ struct InsertSuite } // namespace detail -/// Holds test suites registered during static initialization. +/** + * Holds test suites registered during static initialization. + */ inline SuiteList const& globalSuites() { diff --git a/include/xrpl/beast/unit_test/match.h b/include/xrpl/beast/unit_test/match.h index 5faeaa1100..966574b833 100644 --- a/include/xrpl/beast/unit_test/match.h +++ b/include/xrpl/beast/unit_test/match.h @@ -121,42 +121,48 @@ Selector::operator()(SuiteInfo const& s) // Utility functions for producing predicates to select suites. -/** Returns a predicate that implements a smart matching rule. - The predicate checks the suite, module, and library fields of the - SuiteInfo in that order. When it finds a match, it changes modes - depending on what was found: - - If a suite is matched first, then only the suite is selected. The - suite may be marked manual. - - If a module is matched first, then only suites from that module - and library not marked manual are selected from then on. - - If a library is matched first, then only suites from that library - not marked manual are selected from then on. - -*/ +/** + * Returns a predicate that implements a smart matching rule. + * The predicate checks the suite, module, and library fields of the + * SuiteInfo in that order. When it finds a match, it changes modes + * depending on what was found: + * + * If a suite is matched first, then only the suite is selected. The + * suite may be marked manual. + * + * If a module is matched first, then only suites from that module + * and library not marked manual are selected from then on. + * + * If a library is matched first, then only suites from that library + * not marked manual are selected from then on. + */ inline Selector matchAuto(std::string const& name) { return Selector(Selector::ModeT::Automatch, name); } -/** Return a predicate that matches all suites not marked manual. */ +/** + * Return a predicate that matches all suites not marked manual. + */ inline Selector matchAll() { return Selector(Selector::ModeT::All); } -/** Returns a predicate that matches a specific suite. */ +/** + * Returns a predicate that matches a specific suite. + */ inline Selector matchSuite(std::string const& name) { return Selector(Selector::ModeT::Suite, name); } -/** Returns a predicate that matches all suites in a library. */ +/** + * Returns a predicate that matches all suites in a library. + */ inline Selector matchLibrary(std::string const& name) { diff --git a/include/xrpl/beast/unit_test/recorder.h b/include/xrpl/beast/unit_test/recorder.h index 1b7347dc2e..fcadb63fc9 100644 --- a/include/xrpl/beast/unit_test/recorder.h +++ b/include/xrpl/beast/unit_test/recorder.h @@ -13,7 +13,9 @@ namespace beast::unit_test { -/** A test runner that stores the results. */ +/** + * A test runner that stores the results. + */ class Recorder : public Runner { private: @@ -24,7 +26,9 @@ private: public: Recorder() = default; - /** Returns a report with the results of all completed suites. */ + /** + * Returns a report with the results of all completed suites. + */ [[nodiscard]] Results const& report() const { diff --git a/include/xrpl/beast/unit_test/reporter.h b/include/xrpl/beast/unit_test/reporter.h index a903d9f8c2..0fe77a7862 100644 --- a/include/xrpl/beast/unit_test/reporter.h +++ b/include/xrpl/beast/unit_test/reporter.h @@ -25,9 +25,10 @@ namespace beast::unit_test { namespace detail { -/** A simple test runner that writes everything to a stream in real time. - The totals are output when the object is destroyed. -*/ +/** + * A simple test runner that writes everything to a stream in real time. + * The totals are output when the object is destroyed. + */ template class Reporter : public Runner { diff --git a/include/xrpl/beast/unit_test/results.h b/include/xrpl/beast/unit_test/results.h index 718d764c9f..273ad5b129 100644 --- a/include/xrpl/beast/unit_test/results.h +++ b/include/xrpl/beast/unit_test/results.h @@ -13,11 +13,15 @@ namespace beast::unit_test { -/** Holds a set of test condition outcomes in a testcase. */ +/** + * Holds a set of test condition outcomes in a testcase. + */ class CaseResults { public: - /** Holds the result of evaluating one test condition. */ + /** + * Holds the result of evaluating one test condition. + */ struct Test { explicit Test(bool pass) : pass(pass) @@ -41,28 +45,36 @@ private: public: TestsT() = default; - /** Returns the total number of test conditions. */ + /** + * Returns the total number of test conditions. + */ [[nodiscard]] std::size_t total() const { return cont().size(); } - /** Returns the number of failed test conditions. */ + /** + * Returns the number of failed test conditions. + */ [[nodiscard]] std::size_t failed() const { return failed_; } - /** Register a successful test condition. */ + /** + * Register a successful test condition. + */ void pass() { cont().emplace_back(true); } - /** Register a failed test condition. */ + /** + * Register a failed test condition. + */ void fail(std::string const& reason = "") { @@ -74,7 +86,9 @@ private: class LogT : public detail::ConstContainer> { public: - /** Insert a string into the log. */ + /** + * Insert a string into the log. + */ void insert(std::string const& s) { @@ -89,23 +103,31 @@ public: { } - /** Returns the name of this testcase. */ + /** + * Returns the name of this testcase. + */ [[nodiscard]] std::string const& name() const { return name_; } - /** Memberspace for a container of test condition outcomes. */ + /** + * Memberspace for a container of test condition outcomes. + */ TestsT tests; - /** Memberspace for a container of testcase log messages. */ + /** + * Memberspace for a container of testcase log messages. + */ LogT log; }; //-------------------------------------------------------------------------- -/** Holds the set of testcase results in a suite. */ +/** + * Holds the set of testcase results in a suite. + */ class SuiteResults : public detail::ConstContainer> { private: @@ -118,28 +140,36 @@ public: { } - /** Returns the name of this suite. */ + /** + * Returns the name of this suite. + */ [[nodiscard]] std::string const& name() const { return name_; } - /** Returns the total number of test conditions. */ + /** + * Returns the total number of test conditions. + */ [[nodiscard]] std::size_t total() const { return total_; } - /** Returns the number of failures. */ + /** + * Returns the number of failures. + */ [[nodiscard]] std::size_t failed() const { return failed_; } - /** Insert a set of testcase results. */ + /** + * Insert a set of testcase results. + */ /** @{ */ void insert(CaseResults&& r) @@ -162,7 +192,9 @@ public: //------------------------------------------------------------------------------ // VFALCO TODO Make this a template class using scoped allocators -/** Holds the results of running a set of testsuites. */ +/** + * Holds the results of running a set of testsuites. + */ class Results : public detail::ConstContainer> { private: @@ -173,28 +205,36 @@ private: public: Results() = default; - /** Returns the total number of test cases. */ + /** + * Returns the total number of test cases. + */ [[nodiscard]] std::size_t cases() const { return cases_; } - /** Returns the total number of test conditions. */ + /** + * Returns the total number of test conditions. + */ [[nodiscard]] std::size_t total() const { return total_; } - /** Returns the number of failures. */ + /** + * Returns the number of failures. + */ [[nodiscard]] std::size_t failed() const { return failed_; } - /** Insert a set of suite results. */ + /** + * Insert a set of suite results. + */ /** @{ */ void insert(SuiteResults&& r) diff --git a/include/xrpl/beast/unit_test/runner.h b/include/xrpl/beast/unit_test/runner.h index b88bfc5fe1..f8f9deca48 100644 --- a/include/xrpl/beast/unit_test/runner.h +++ b/include/xrpl/beast/unit_test/runner.h @@ -13,11 +13,12 @@ namespace beast::unit_test { -/** Unit test runner interface. - - Derived classes can customize the reporting behavior. This interface is - injected into the unit_test class to receive the results of the tests. -*/ +/** + * Unit test runner interface. + * + * Derived classes can customize the reporting behavior. This interface is + * injected into the unit_test class to receive the results of the tests. + */ class Runner { std::string arg_; @@ -33,110 +34,132 @@ public: Runner& operator=(Runner const&) = delete; - /** Set the argument string. - - The argument string is available to suites and - allows for customization of the test. Each suite - defines its own syntax for the argument string. - The same argument is passed to all suites. - */ + /** + * Set the argument string. + * + * The argument string is available to suites and + * allows for customization of the test. Each suite + * defines its own syntax for the argument string. + * The same argument is passed to all suites. + */ void arg(std::string const& s) { arg_ = s; } - /** Returns the argument string. */ + /** + * Returns the argument string. + */ [[nodiscard]] std::string const& arg() const { return arg_; } - /** Run the specified suite. - @return `true` if any conditions failed. - */ + /** + * Run the specified suite. + * @return `true` if any conditions failed. + */ template bool run(SuiteInfo const& s); - /** Run a sequence of suites. - The expression - `FwdIter::value_type` - must be convertible to `SuiteInfo`. - @return `true` if any conditions failed. - */ + /** + * Run a sequence of suites. + * The expression + * `FwdIter::value_type` + * must be convertible to `SuiteInfo`. + * @return `true` if any conditions failed. + */ template bool run(FwdIter first, FwdIter last); - /** Conditionally run a sequence of suites. - pred will be called as: - @code - bool pred(SuiteInfo const&); - @endcode - @return `true` if any conditions failed. - */ + /** + * Conditionally run a sequence of suites. + * pred will be called as: + * @code + * bool pred(SuiteInfo const&); + * @endcode + * @return `true` if any conditions failed. + */ template bool runIf(FwdIter first, FwdIter last, Pred pred = Pred{}); - /** Run all suites in a container. - @return `true` if any conditions failed. - */ + /** + * Run all suites in a container. + * @return `true` if any conditions failed. + */ template bool runEach(SequenceContainer const& c); - /** Conditionally run suites in a container. - pred will be called as: - @code - bool pred(SuiteInfo const&); - @endcode - @return `true` if any conditions failed. - */ + /** + * Conditionally run suites in a container. + * pred will be called as: + * @code + * bool pred(SuiteInfo const&); + * @endcode + * @return `true` if any conditions failed. + */ template bool runEachIf(SequenceContainer const& c, Pred pred = Pred{}); protected: - /// Called when a new suite starts. + /** + * Called when a new suite starts. + */ virtual void onSuiteBegin(SuiteInfo const&) { } - /// Called when a suite ends. + /** + * Called when a suite ends. + */ virtual void onSuiteEnd() { } - /// Called when a new case starts. + /** + * Called when a new case starts. + */ virtual void onCaseBegin(std::string const&) { } - /// Called when a new case ends. + /** + * Called when a new case ends. + */ virtual void onCaseEnd() { } - /// Called for each passing condition. + /** + * Called for each passing condition. + */ virtual void onPass() { } - /// Called for each failing condition. + /** + * Called for each failing condition. + */ virtual void onFail(std::string const&) { } - /// Called when a test logs output. + /** + * Called when a test logs output. + */ virtual void onLog(std::string const&) { diff --git a/include/xrpl/beast/unit_test/suite.h b/include/xrpl/beast/unit_test/suite.h index 487663fcc5..c20fe2522c 100644 --- a/include/xrpl/beast/unit_test/suite.h +++ b/include/xrpl/beast/unit_test/suite.h @@ -41,13 +41,14 @@ class Thread; enum class AbortT { NoAbortOnFail, AbortOnFail }; -/** A testsuite class. - - Derived classes execute a series of testcases, where each testcase is - a series of pass/fail tests. To provide a unit test using this class, - derive from it and use the BEAST_DEFINE_UNIT_TEST macro in a - translation unit. -*/ +/** + * A testsuite class. + * + * Derived classes execute a series of testcases, where each testcase is + * a series of pass/fail tests. To provide a unit test using this class, + * derive from it and use the BEAST_DEFINE_UNIT_TEST macro in a + * translation unit. + */ class Suite { private: @@ -118,16 +119,17 @@ private: { } - /** Open a new testcase. - - A testcase is a series of evaluated test conditions. A test - suite may have multiple test cases. A test is associated with - the last opened testcase. When the test first runs, a default - unnamed case is opened. Tests with only one case may omit the - call to testcase. - - @param abort Determines if suite continues running after a failure. - */ + /** + * Open a new testcase. + * + * A testcase is a series of evaluated test conditions. A test + * suite may have multiple test cases. A test is associated with + * the last opened testcase. When the test first runs, a default + * unnamed case is opened. Tests with only one case may omit the + * call to testcase. + * + * @param abort Determines if suite continues running after a failure. + */ void operator()(std::string const& name, AbortT abort = AbortT::NoAbortOnFail); @@ -140,19 +142,23 @@ private: }; public: - /** Logging output stream. - - Text sent to the log output stream will be forwarded to - the output stream associated with the runner. - */ + /** + * Logging output stream. + * + * Text sent to the log output stream will be forwarded to + * the output stream associated with the runner. + */ LogOs log; - /** Memberspace for declaring test cases. */ + /** + * Memberspace for declaring test cases. + */ TestcaseT testcase; - /** Returns the "current" running suite. - If no suite is running, nullptr is returned. - */ + /** + * Returns the "current" running suite. + * If no suite is running, nullptr is returned. + */ static Suite* thisSuite() { @@ -168,30 +174,34 @@ public: Suite& operator=(Suite const&) = delete; - /** Invokes the test using the specified runner. - - Data members are set up here instead of the constructor as a - convenience to writing the derived class to avoid repetition of - forwarded constructor arguments to the base. - Normally this is called by the framework for you. - */ + /** + * Invokes the test using the specified runner. + * + * Data members are set up here instead of the constructor as a + * convenience to writing the derived class to avoid repetition of + * forwarded constructor arguments to the base. + * Normally this is called by the framework for you. + */ template void operator()(Runner& r); - /** Record a successful test condition. */ + /** + * Record a successful test condition. + */ template void pass(); - /** Record a failure. - - @param reason Optional text added to the output on a failure. - - @param file The source code file where the test failed. - - @param line The source code line number where the test failed. - */ + /** + * Record a failure. + * + * @param reason Optional text added to the output on a failure. + * + * @param file The source code file where the test failed. + * + * @param line The source code line number where the test failed. + */ /** @{ */ template void @@ -202,23 +212,24 @@ public: fail(std::string const& reason = ""); /** @} */ - /** Evaluate a test condition. - - This function provides improved logging by incorporating the - file name and line number into the reported output on failure, - as well as additional text specified by the caller. - - @param shouldBeTrue The condition to test. The condition - is evaluated in a boolean context. - - @param reason Optional added text to output on a failure. - - @param file The source code file where the test failed. - - @param line The source code line number where the test failed. - - @return `true` if the test condition indicates success. - */ + /** + * Evaluate a test condition. + * + * This function provides improved logging by incorporating the + * file name and line number into the reported output on failure, + * as well as additional text specified by the caller. + * + * @param shouldBeTrue The condition to test. The condition + * is evaluated in a boolean context. + * + * @param reason Optional added text to output on a failure. + * + * @param file The source code file where the test failed. + * + * @param line The source code line number where the test failed. + * + * @return `true` if the test condition indicates success. + */ /** @{ */ template bool @@ -275,15 +286,19 @@ public: return unexcept(f, ""); } - /** Return the argument associated with the runner. */ + /** + * Return the argument associated with the runner. + */ std::string const& arg() const { return runner_->arg(); } - // DEPRECATED - // @return `true` if the test condition indicates success(a false value) + /** + * DEPRECATED + * @return `true` if the test condition indicates success(a false value) + */ template bool unexpected(Condition shouldBeFalse, String const& reason); @@ -305,7 +320,9 @@ private: return &kPTs; } - /** Runs the suite. */ + /** + * Runs the suite. + */ virtual void run() = 0; @@ -558,18 +575,20 @@ Suite::run(Runner& r) } #ifndef BEAST_EXPECT -/** Check a precondition. - - If the condition is false, the file and line number are reported. -*/ +/** + * Check a precondition. + * + * If the condition is false, the file and line number are reported. + */ #define BEAST_EXPECT(cond) expect(cond, __FILE__, __LINE__) #endif #ifndef BEAST_EXPECTS -/** Check a precondition. - - If the condition is false, the file and line number are reported. -*/ +/** + * Check a precondition. + * + * If the condition is false, the file and line number are reported. + */ #define BEAST_EXPECTS(cond, reason) \ ((cond) ? (pass(), true) : (fail((reason), __FILE__, __LINE__), false)) #endif @@ -593,41 +612,43 @@ Suite::run(Runner& r) // #ifndef BEAST_DEFINE_TESTSUITE -/** Enables insertion of test suites into the global container. - The default is to insert all test suite definitions into the global - container. If BEAST_DEFINE_TESTSUITE is user defined, this macro - has no effect. -*/ +/** + * Enables insertion of test suites into the global container. + * The default is to insert all test suite definitions into the global + * container. If BEAST_DEFINE_TESTSUITE is user defined, this macro + * has no effect. + */ #ifndef BEAST_NO_UNIT_TEST_INLINE #define BEAST_NO_UNIT_TEST_INLINE 0 #endif -/** Define a unit test suite. - - Class The type representing the class being tested. - Module Identifies the module. - Library Identifies the library. - - The declaration for the class implementing the test should be the same - as Class ## _test. For example, if Class is aged_ordered_container, the - test class must be declared as: - - @code - - struct aged_ordered_container_test : beast::unit_test::suite - { - //... - }; - - @endcode - - The macro invocation must appear in the same namespace as the test class. - - Unit test priorities were introduced so parallel unit_test::suites would - execute faster. Suites with longer running times have higher priorities - than unit tests with shorter running times. Suites with no priorities - are assumed to run most quickly, so they run last. -*/ +/** + * Define a unit test suite. + * + * Class The type representing the class being tested. + * Module Identifies the module. + * Library Identifies the library. + * + * The declaration for the class implementing the test should be the same + * as Class ## _test. For example, if Class is aged_ordered_container, the + * test class must be declared as: + * + * @code + * + * struct aged_ordered_container_test : beast::unit_test::suite + * { + * //... + * }; + * + * @endcode + * + * The macro invocation must appear in the same namespace as the test class. + * + * Unit test priorities were introduced so parallel unit_test::suites would + * execute faster. Suites with longer running times have higher priorities + * than unit tests with shorter running times. Suites with no priorities + * are assumed to run most quickly, so they run last. + */ #if BEAST_NO_UNIT_TEST_INLINE #define BEAST_DEFINE_TESTSUITE(Class, Module, Library) diff --git a/include/xrpl/beast/unit_test/suite_info.h b/include/xrpl/beast/unit_test/suite_info.h index bda10ae7e3..c4e3496f13 100644 --- a/include/xrpl/beast/unit_test/suite_info.h +++ b/include/xrpl/beast/unit_test/suite_info.h @@ -13,7 +13,9 @@ namespace beast::unit_test { class Runner; -/** Associates a unit test type with metadata. */ +/** + * Associates a unit test type with metadata. + */ class SuiteInfo { using run_type = std::function; @@ -60,21 +62,27 @@ public: return library_; } - /// Returns `true` if this suite only runs manually. + /** + * Returns `true` if this suite only runs manually. + */ [[nodiscard]] bool manual() const { return manual_; } - /// Return the canonical suite name as a string. + /** + * Return the canonical suite name as a string. + */ [[nodiscard]] std::string fullName() const { return library_ + "." + module_ + "." + name_; } - /// Run a new instance of the associated test suite. + /** + * Run a new instance of the associated test suite. + */ void run(Runner& r) const { @@ -93,7 +101,9 @@ public: //------------------------------------------------------------------------------ -/// Convenience for producing SuiteInfo for a given test type. +/** + * Convenience for producing SuiteInfo for a given test type. + */ template SuiteInfo makeSuiteInfo(std::string name, std::string module, std::string library, bool manual, int priority) diff --git a/include/xrpl/beast/unit_test/suite_list.h b/include/xrpl/beast/unit_test/suite_list.h index 7dd0dd80f0..057a362859 100644 --- a/include/xrpl/beast/unit_test/suite_list.h +++ b/include/xrpl/beast/unit_test/suite_list.h @@ -16,7 +16,9 @@ namespace beast::unit_test { -/// A container of test suites. +/** + * A container of test suites. + */ class SuiteList : public detail::ConstContainer> { private: @@ -26,10 +28,11 @@ private: #endif public: - /** Insert a suite into the set. - - The suite must not already exist. - */ + /** + * Insert a suite into the set. + * + * The suite must not already exist. + */ template void insert(char const* name, char const* module, char const* library, bool manual, int priority); diff --git a/include/xrpl/beast/unit_test/thread.h b/include/xrpl/beast/unit_test/thread.h index 91d8cf3cab..5a5a99d149 100644 --- a/include/xrpl/beast/unit_test/thread.h +++ b/include/xrpl/beast/unit_test/thread.h @@ -14,7 +14,9 @@ namespace beast::unit_test { -/** Replacement for std::thread that handles exceptions in unit tests. */ +/** + * Replacement for std::thread that handles exceptions in unit tests. + */ class Thread { private: diff --git a/include/xrpl/beast/utility/Journal.h b/include/xrpl/beast/utility/Journal.h index 3de3cfb0e0..9f0a1ead66 100644 --- a/include/xrpl/beast/utility/Journal.h +++ b/include/xrpl/beast/utility/Journal.h @@ -10,7 +10,9 @@ namespace beast { -/** Severity level / threshold of a Journal message. */ +/** + * Severity level / threshold of a Journal message. + */ enum class Severity : std::uint8_t { All = 0, @@ -25,18 +27,19 @@ enum class Severity : std::uint8_t { None = Disabled }; -/** A generic endpoint for log messages. - - The Journal has a few simple goals: - - * To be light-weight and copied by value. - * To allow logging statements to be left in source code. - * The logging is controlled at run-time based on a logging threshold. - - It is advisable to check Journal::active(level) prior to formatting log - text. Doing so sidesteps expensive text formatting when the results - will not be sent to the log. -*/ +/** + * A generic endpoint for log messages. + * + * The Journal has a few simple goals: + * + * * To be light-weight and copied by value. + * * To allow logging statements to be left in source code. + * * The logging is controlled at run-time based on a logging threshold. + * + * It is advisable to check Journal::active(level) prior to formatting log + * text. Doing so sidesteps expensive text formatting when the results + * will not be sent to the log. + */ class Journal { public: @@ -49,7 +52,9 @@ private: public: //-------------------------------------------------------------------------- - /** Abstraction for the underlying message destination. */ + /** + * Abstraction for the underlying message destination. + */ class Sink { protected: @@ -63,36 +68,47 @@ public: Sink& operator=(Sink const& lhs) = delete; - /** Returns `true` if text at the passed severity produces output. */ + /** + * Returns `true` if text at the passed severity produces output. + */ [[nodiscard]] virtual bool active(Severity level) const; - /** Returns `true` if a message is also written to the Output Window - * (MSVC). */ + /** + * Returns `true` if a message is also written to the Output Window + * (MSVC). + */ [[nodiscard]] virtual bool console() const; - /** Set whether messages are also written to the Output Window (MSVC). + /** + * Set whether messages are also written to the Output Window (MSVC). */ virtual void console(bool output); - /** Returns the minimum severity level this sink will report. */ + /** + * Returns the minimum severity level this sink will report. + */ [[nodiscard]] virtual Severity threshold() const; - /** Set the minimum severity this sink will report. */ + /** + * Set the minimum severity this sink will report. + */ virtual void threshold(Severity thresh); - /** Write text to the sink at the specified severity. - A conforming implementation will not write the text if the passed - level is below the current threshold(). - */ + /** + * Write text to the sink at the specified severity. + * A conforming implementation will not write the text if the passed + * level is below the current threshold(). + */ virtual void write(Severity level, std::string const& text) = 0; - /** Bypass filter and write text to the sink at the specified severity. + /** + * Bypass filter and write text to the sink at the specified severity. * Always write the message, but maintain the same formatting as if * it passed through a level filter. * @@ -116,7 +132,9 @@ public: static_assert(std::is_nothrow_destructible_v); #endif - /** Returns a Sink which does nothing. */ + /** + * Returns a Sink which does nothing. + */ static Sink& getNullSink(); @@ -174,26 +192,33 @@ public: //-------------------------------------------------------------------------- public: - /** Provide a light-weight way to check active() before string formatting */ + /** + * Provide a light-weight way to check active() before string formatting + */ class Stream { public: - /** Create a stream which produces no output. */ + /** + * Create a stream which produces no output. + */ explicit Stream() : sink_(getNullSink()), level_(Severity::Disabled) { } - /** Create a stream that writes at the given level. - - Constructor is inlined so checking active() very inexpensive. - */ + /** + * Create a stream that writes at the given level. + * + * Constructor is inlined so checking active() very inexpensive. + */ Stream(Sink& sink, Severity level) : sink_(sink), level_(level) { XRPL_ASSERT( level_ < Severity::Disabled, "beast::Journal::Stream::Stream : maximum level"); } - /** Construct or copy another Stream. */ + /** + * Construct or copy another Stream. + */ Stream(Stream const& other) : Stream(other.sink_, other.level_) { } @@ -201,21 +226,27 @@ public: Stream& operator=(Stream const& other) = delete; - /** Returns the Sink that this Stream writes to. */ + /** + * Returns the Sink that this Stream writes to. + */ [[nodiscard]] Sink& sink() const { return sink_; } - /** Returns the Severity level of messages this Stream reports. */ + /** + * Returns the Severity level of messages this Stream reports. + */ [[nodiscard]] Severity level() const { return level_; } - /** Returns `true` if sink logs anything at this stream's level. */ + /** + * Returns `true` if sink logs anything at this stream's level. + */ /** @{ */ [[nodiscard]] bool active() const @@ -230,7 +261,9 @@ public: } /** @} */ - /** Output stream support. */ + /** + * Output stream support. + */ /** @{ */ ScopedStream operator<<(std::ostream& manip(std::ostream&)) const; @@ -256,39 +289,50 @@ public: //-------------------------------------------------------------------------- - /** Journal has no default constructor. */ + /** + * Journal has no default constructor. + */ Journal() = delete; - /** Create a journal that writes to the specified sink. */ + /** + * Create a journal that writes to the specified sink. + */ explicit Journal(Sink& sink) : sink_(&sink) { } - /** Returns the Sink associated with this Journal. */ + /** + * Returns the Sink associated with this Journal. + */ [[nodiscard]] Sink& sink() const { return *sink_; } - /** Returns a stream for this sink, with the specified severity level. */ + /** + * Returns a stream for this sink, with the specified severity level. + */ [[nodiscard]] Stream stream(Severity level) const { return Stream(*sink_, level); } - /** Returns `true` if any message would be logged at this severity level. - For a message to be logged, the severity must be at or above the - sink's severity threshold. - */ + /** + * Returns `true` if any message would be logged at this severity level. + * For a message to be logged, the severity must be at or above the + * sink's severity threshold. + */ [[nodiscard]] bool active(Severity level) const { return sink_->active(level); } - /** Severity stream access functions. */ + /** + * Severity stream access functions. + */ /** @{ */ [[nodiscard]] Stream trace() const diff --git a/include/xrpl/beast/utility/PropertyStream.h b/include/xrpl/beast/utility/PropertyStream.h index 3fb6df53d9..f32f5b7fef 100644 --- a/include/xrpl/beast/utility/PropertyStream.h +++ b/include/xrpl/beast/utility/PropertyStream.h @@ -12,7 +12,9 @@ namespace beast { //------------------------------------------------------------------------------ -/** Abstract stream with RAII containers that produce a property tree. */ +/** + * Abstract stream with RAII containers that produce a property tree. + */ class PropertyStream { public: @@ -306,7 +308,9 @@ public: // //------------------------------------------------------------------------------ -/** Subclasses can be called to write to a stream and have children. */ +/** + * Subclasses can be called to write to a stream and have children. + */ class PropertyStream::Source { private: @@ -324,17 +328,22 @@ public: Source& operator=(Source const&) = delete; - /** Returns the name of this source. */ + /** + * Returns the name of this source. + */ [[nodiscard]] std::string const& name() const; - /** Add a child source. */ + /** + * Add a child source. + */ void add(Source& source); - /** Add a child source by pointer. - The source pointer is returned so it can be used in ctor-initializers. - */ + /** + * Add a child source by pointer. + * The source pointer is returned so it can be used in ctor-initializers. + */ template Derived* add(Derived* child) @@ -343,45 +352,55 @@ public: return child; } - /** Remove a child source from this Source. */ + /** + * Remove a child source from this Source. + */ void remove(Source& child); - /** Remove all child sources from this Source. */ + /** + * Remove all child sources from this Source. + */ void removeAll(); - /** Write only this Source to the stream. */ + /** + * Write only this Source to the stream. + */ void writeOne(PropertyStream& stream); - /** write this source and all its children recursively to the stream. */ + /** + * write this source and all its children recursively to the stream. + */ void write(PropertyStream& stream); - /** Parse the path and write the corresponding Source and optional children. - If the source is found, it is written. If the wildcard character '*' - exists as the last character in the path, then all the children are - written recursively. - */ + /** + * Parse the path and write the corresponding Source and optional children. + * If the source is found, it is written. If the wildcard character '*' + * exists as the last character in the path, then all the children are + * written recursively. + */ void write(PropertyStream& stream, std::string const& path); - /** Parse the dot-delimited Source path and return the result. - The first value will be a pointer to the Source object corresponding - to the given path. If no Source object exists, then the first value - will be nullptr and the second value will be undefined. - The second value is a boolean indicating whether or not the path string - specifies the wildcard character '*' as the last character. - - print statement examples - "parent.child" prints child and all of its children - "parent.child." start at the parent and print down to child - "parent.grandchild" prints nothing- grandchild not direct descendent - "parent.grandchild." starts at the parent and prints down to grandchild - "parent.grandchild.*" starts at parent, print through grandchild - children - */ + /** + * Parse the dot-delimited Source path and return the result. + * The first value will be a pointer to the Source object corresponding + * to the given path. If no Source object exists, then the first value + * will be nullptr and the second value will be undefined. + * The second value is a boolean indicating whether or not the path string + * specifies the wildcard character '*' as the last character. + * + * print statement examples + * "parent.child" prints child and all of its children + * "parent.child." start at the parent and print down to child + * "parent.grandchild" prints nothing- grandchild not direct descendent + * "parent.grandchild." starts at the parent and prints down to grandchild + * "parent.grandchild.*" starts at parent, print through grandchild + * children + */ std::pair find(std::string path); @@ -401,9 +420,10 @@ public: //-------------------------------------------------------------------------- - /** Subclass override. - The default version does nothing. - */ + /** + * Subclass override. + * The default version does nothing. + */ virtual void onWrite(Map&); }; diff --git a/include/xrpl/beast/utility/WrappedSink.h b/include/xrpl/beast/utility/WrappedSink.h index a24ad595db..3ab48e1939 100644 --- a/include/xrpl/beast/utility/WrappedSink.h +++ b/include/xrpl/beast/utility/WrappedSink.h @@ -7,7 +7,9 @@ namespace beast { -/** Wraps a Journal::Sink to prefix its output with a string. */ +/** + * Wraps a Journal::Sink to prefix its output with a string. + */ // A WrappedSink both is a Sink and has a Sink: // o It inherits from Sink so it has the correct interface. diff --git a/include/xrpl/beast/utility/Zero.h b/include/xrpl/beast/utility/Zero.h index e28589760b..406921c500 100644 --- a/include/xrpl/beast/utility/Zero.h +++ b/include/xrpl/beast/utility/Zero.h @@ -4,22 +4,23 @@ namespace beast { -/** Zero allows classes to offer efficient comparisons to zero. - - Zero is a struct to allow classes to efficiently compare with zero without - requiring an rvalue construction. - - It's often the case that we have classes which combine a number and a unit. - In such cases, comparisons like t > 0 or t != 0 make sense, but comparisons - like t > 1 or t != 1 do not. - - The class Zero allows such comparisons to be easily made. - - The comparing class T either needs to have a method called signum() which - returns a positive number, 0, or a negative; or there needs to be a signum - function which resolves in the namespace which takes an instance of T and - returns a positive, zero or negative number. -*/ +/** + * Zero allows classes to offer efficient comparisons to zero. + * + * Zero is a struct to allow classes to efficiently compare with zero without + * requiring an rvalue construction. + * + * It's often the case that we have classes which combine a number and a unit. + * In such cases, comparisons like t > 0 or t != 0 make sense, but comparisons + * like t > 1 or t != 1 do not. + * + * The class Zero allows such comparisons to be easily made. + * + * The comparing class T either needs to have a method called signum() which + * returns a positive number, 0, or a negative; or there needs to be a signum + * function which resolves in the namespace which takes an instance of T and + * returns a positive, zero or negative number. + */ struct Zero { @@ -28,7 +29,9 @@ struct Zero inline constexpr Zero kZero{}; -/** Default implementation of signum calls the method on the class. */ +/** + * Default implementation of signum calls the method on the class. + */ template auto signum(T const& t) diff --git a/include/xrpl/beast/utility/maybe_const.h b/include/xrpl/beast/utility/maybe_const.h index 10b2eaf7f6..848ea86cb2 100644 --- a/include/xrpl/beast/utility/maybe_const.h +++ b/include/xrpl/beast/utility/maybe_const.h @@ -4,7 +4,9 @@ namespace beast { -/** Makes T const or non const depending on a bool. */ +/** + * Makes T const or non const depending on a bool. + */ template struct MaybeConst { @@ -13,7 +15,9 @@ struct MaybeConst conditional_t::type const, std::remove_const_t>; }; -/** Alias for omitting `typename`. */ +/** + * Alias for omitting `typename`. + */ template using maybe_const_t = MaybeConst::type; diff --git a/include/xrpl/beast/utility/temp_dir.h b/include/xrpl/beast/utility/temp_dir.h index ec661b51c4..a0ff1e6940 100644 --- a/include/xrpl/beast/utility/temp_dir.h +++ b/include/xrpl/beast/utility/temp_dir.h @@ -6,11 +6,12 @@ namespace beast { -/** RAII temporary directory. - - The directory and all its contents are deleted when - the instance of `temp_dir` is destroyed. -*/ +/** + * RAII temporary directory. + * + * The directory and all its contents are deleted when + * the instance of `temp_dir` is destroyed. + */ class TempDir { boost::filesystem::path path_; @@ -22,7 +23,9 @@ public: operator=(TempDir const&) = delete; #endif - /// Construct a temporary directory. + /** + * Construct a temporary directory. + */ TempDir() { auto const dir = boost::filesystem::temp_directory_path(); @@ -33,7 +36,9 @@ public: boost::filesystem::create_directory(path_); } - /// Destroy a temporary directory. + /** + * Destroy a temporary directory. + */ ~TempDir() { // use non-throwing calls in the destructor @@ -42,17 +47,20 @@ public: // TODO: warn/notify if ec set ? } - /// Get the native path for the temporary directory + /** + * Get the native path for the temporary directory + */ [[nodiscard]] std::string path() const { return path_.string(); } - /** Get the native path for the a file. - - The file does not need to exist. - */ + /** + * Get the native path for the a file. + * + * The file does not need to exist. + */ [[nodiscard]] std::string file(std::string const& name) const { diff --git a/include/xrpl/beast/xor_shift_engine.h b/include/xrpl/beast/xor_shift_engine.h index 45baecf101..6a7272c195 100644 --- a/include/xrpl/beast/xor_shift_engine.h +++ b/include/xrpl/beast/xor_shift_engine.h @@ -85,14 +85,15 @@ XorShiftEngine::murmurhash3(result_type x) -> result_type } // namespace detail -/** XOR-shift Generator. - - Meets the requirements of UniformRandomNumberGenerator. - - Simple and fast RNG based on: - http://xorshift.di.unimi.it/xorshift128plus.c - does not accept seed==0 -*/ +/** + * XOR-shift Generator. + * + * Meets the requirements of UniformRandomNumberGenerator. + * + * Simple and fast RNG based on: + * http://xorshift.di.unimi.it/xorshift128plus.c + * does not accept seed==0 + */ using xor_shift_engine = detail::XorShiftEngine<>; } // namespace beast diff --git a/include/xrpl/conditions/Condition.h b/include/xrpl/conditions/Condition.h index 3c798663d7..365a41a087 100644 --- a/include/xrpl/conditions/Condition.h +++ b/include/xrpl/conditions/Condition.h @@ -24,42 +24,49 @@ enum class Type : std::uint8_t { class Condition { public: - /** The largest binary condition we support. - - @note This value will be increased in the future, but it - must never decrease, as that could cause conditions - that were previously considered valid to no longer - be allowed. - */ + /** + * The largest binary condition we support. + * + * @note This value will be increased in the future, but it + * must never decrease, as that could cause conditions + * that were previously considered valid to no longer + * be allowed. + */ static constexpr std::size_t kMaxSerializedCondition = 128; - /** Load a condition from its binary form - - @param s The buffer containing the fulfillment to load. - @param ec Set to the error, if any occurred. - - The binary format for a condition is specified in the - cryptoconditions RFC. See: - - https://tools.ietf.org/html/draft-thomas-crypto-conditions-02#section-7.2 - */ + /** + * Load a condition from its binary form + * + * @param s The buffer containing the fulfillment to load. + * @param ec Set to the error, if any occurred. + * + * The binary format for a condition is specified in the + * cryptoconditions RFC. See: + * + * https://tools.ietf.org/html/draft-thomas-crypto-conditions-02#section-7.2 + */ static std::unique_ptr deserialize(Slice s, std::error_code& ec); public: Type type; - /** An identifier for this condition. - - This fingerprint is meant to be unique only with - respect to other conditions of the same type. - */ + /** + * An identifier for this condition. + * + * This fingerprint is meant to be unique only with + * respect to other conditions of the same type. + */ Buffer fingerprint; - /** The cost associated with this condition. */ + /** + * The cost associated with this condition. + */ std::uint32_t cost; - /** For compound conditions, set of conditions includes */ + /** + * For compound conditions, set of conditions includes + */ std::set subtypes; Condition(Type t, std::uint32_t c, Slice fp) : type(t), fingerprint(fp), cost(c) diff --git a/include/xrpl/conditions/Fulfillment.h b/include/xrpl/conditions/Fulfillment.h index a3001b2620..11f3165a58 100644 --- a/include/xrpl/conditions/Fulfillment.h +++ b/include/xrpl/conditions/Fulfillment.h @@ -14,64 +14,73 @@ namespace xrpl::cryptoconditions { struct Fulfillment { public: - /** The largest binary fulfillment we support. - - @note This value will be increased in the future, but it - must never decrease, as that could cause fulfillments - that were previously considered valid to no longer - be allowed. - */ + /** + * The largest binary fulfillment we support. + * + * @note This value will be increased in the future, but it + * must never decrease, as that could cause fulfillments + * that were previously considered valid to no longer + * be allowed. + */ static constexpr std::size_t kMaxSerializedFulfillment = 256; - /** Load a fulfillment from its binary form - - @param s The buffer containing the fulfillment to load. - @param ec Set to the error, if any occurred. - - The binary format for a fulfillment is specified in the - cryptoconditions RFC. See: - - https://tools.ietf.org/html/draft-thomas-crypto-conditions-02#section-7.3 - */ + /** + * Load a fulfillment from its binary form + * + * @param s The buffer containing the fulfillment to load. + * @param ec Set to the error, if any occurred. + * + * The binary format for a fulfillment is specified in the + * cryptoconditions RFC. See: + * + * https://tools.ietf.org/html/draft-thomas-crypto-conditions-02#section-7.3 + */ static std::unique_ptr deserialize(Slice s, std::error_code& ec); public: virtual ~Fulfillment() = default; - /** Returns the fulfillment's fingerprint: - - The fingerprint is an octet string uniquely - representing this fulfillment's condition - with respect to other conditions of the - same type. - */ + /** + * Returns the fulfillment's fingerprint: + * + * The fingerprint is an octet string uniquely + * representing this fulfillment's condition + * with respect to other conditions of the + * same type. + */ [[nodiscard]] virtual Buffer fingerprint() const = 0; - /** Returns the type of this condition. */ + /** + * Returns the type of this condition. + */ [[nodiscard]] virtual Type type() const = 0; - /** Validates a fulfillment. */ + /** + * Validates a fulfillment. + */ [[nodiscard]] virtual bool validate(Slice data) const = 0; - /** Calculates the cost associated with this fulfillment. * - - The cost function is deterministic and depends on the - type and properties of the condition and the fulfillment - that the condition is generated from. - */ + /** + * Calculates the cost associated with this fulfillment. * + * + * The cost function is deterministic and depends on the + * type and properties of the condition and the fulfillment + * that the condition is generated from. + */ [[nodiscard]] virtual std::uint32_t cost() const = 0; - /** Returns the condition associated with the given fulfillment. - - This process is completely deterministic. All implementations - will, if compliant, produce the identical condition for the - same fulfillment. - */ + /** + * Returns the condition associated with the given fulfillment. + * + * This process is completely deterministic. All implementations + * will, if compliant, produce the identical condition for the + * same fulfillment. + */ [[nodiscard]] virtual Condition condition() const = 0; }; @@ -90,36 +99,40 @@ operator!=(Fulfillment const& lhs, Fulfillment const& rhs) return !(lhs == rhs); } -/** Determine whether the given fulfillment and condition match */ +/** + * Determine whether the given fulfillment and condition match + */ bool match(Fulfillment const& f, Condition const& c); -/** Verify if the given message satisfies the fulfillment. - - @param f The fulfillment - @param c The condition - @param m The message - - @note the message is not relevant for some conditions - and a fulfillment will successfully satisfy its - condition for any given message. -*/ +/** + * Verify if the given message satisfies the fulfillment. + * + * @param f The fulfillment + * @param c The condition + * @param m The message + * + * @note the message is not relevant for some conditions + * and a fulfillment will successfully satisfy its + * condition for any given message. + */ bool validate(Fulfillment const& f, Condition const& c, Slice m); -/** Verify a cryptoconditional trigger. - - A cryptoconditional trigger is a cryptocondition with - an empty message. - - When using such triggers, it is recommended that the - trigger be of type preimage, prefix or threshold. If - a signature type is used (i.e. Ed25519 or RSA-SHA256) - then the Ed25519 or RSA keys should be single-use keys. - - @param f The fulfillment - @param c The condition -*/ +/** + * Verify a cryptoconditional trigger. + * + * A cryptoconditional trigger is a cryptocondition with + * an empty message. + * + * When using such triggers, it is recommended that the + * trigger be of type preimage, prefix or threshold. If + * a signature type is used (i.e. Ed25519 or RSA-SHA256) + * then the Ed25519 or RSA keys should be single-use keys. + * + * @param f The fulfillment + * @param c The condition + */ bool validate(Fulfillment const& f, Condition const& c); diff --git a/include/xrpl/conditions/detail/PreimageSha256.h b/include/xrpl/conditions/detail/PreimageSha256.h index 0973a52e4a..007588a0b5 100644 --- a/include/xrpl/conditions/detail/PreimageSha256.h +++ b/include/xrpl/conditions/detail/PreimageSha256.h @@ -18,23 +18,25 @@ namespace xrpl::cryptoconditions { class PreimageSha256 final : public Fulfillment { public: - /** The maximum allowed length of a preimage. - - The specification does not specify a minimum supported - length, nor does it require all conditions to support - the same minimum length. - - While future versions of this code will never lower - this limit, they may opt to raise it. - */ + /** + * The maximum allowed length of a preimage. + * + * The specification does not specify a minimum supported + * length, nor does it require all conditions to support + * the same minimum length. + * + * While future versions of this code will never lower + * this limit, they may opt to raise it. + */ static constexpr std::size_t kMaxPreimageLength = 128; - /** Parse the payload for a PreimageSha256 condition - - @param s A slice containing the DER encoded payload - @param ec indicates success or failure of the operation - @return the preimage, if successful; empty pointer otherwise. - */ + /** + * Parse the payload for a PreimageSha256 condition + * + * @param s A slice containing the DER encoded payload + * @param ec indicates success or failure of the operation + * @return the preimage, if successful; empty pointer otherwise. + */ static std::unique_ptr deserialize(Slice s, std::error_code& ec) { diff --git a/include/xrpl/config/BasicConfig.h b/include/xrpl/config/BasicConfig.h index 5680b51fe7..607a0c3e5f 100644 --- a/include/xrpl/config/BasicConfig.h +++ b/include/xrpl/config/BasicConfig.h @@ -21,9 +21,10 @@ using IniFileSections = std::unordered_map //------------------------------------------------------------------------------ -/** Holds a collection of configuration values. - A configuration file contains zero or more sections. -*/ +/** + * Holds a collection of configuration values. + * A configuration file contains zero or more sections. + */ class Section { private: @@ -36,28 +37,34 @@ private: using const_iterator = decltype(lookup_)::const_iterator; public: - /** Create an empty section. */ + /** + * Create an empty section. + */ explicit Section(std::string name = ""); - /** Returns the name of this section. */ + /** + * Returns the name of this section. + */ [[nodiscard]] std::string const& name() const { return name_; } - /** Returns all the lines in the section. - This includes everything. - */ + /** + * Returns all the lines in the section. + * This includes everything. + */ [[nodiscard]] std::vector const& lines() const { return lines_; } - /** Returns all the values in the section. - Values are non-empty lines which are not key/value pairs. - */ + /** + * Returns all the values in the section. + * Values are non-empty lines which are not key/value pairs. + */ [[nodiscard]] std::vector const& values() const { @@ -84,7 +91,7 @@ public: * Get the legacy value for this section. * * @return The retrieved value. A section with an empty legacy value returns - an empty string. + * an empty string. */ [[nodiscard]] std::string legacy() const @@ -99,28 +106,34 @@ public: return lines_[0]; } - /** Set a key/value pair. - The previous value is discarded. - */ + /** + * Set a key/value pair. + * The previous value is discarded. + */ void set(std::string const& key, std::string const& value); - /** Append a set of lines to this section. - Lines containing key/value pairs are added to the map, - else they are added to the values list. Everything is - added to the lines list. - */ + /** + * Append a set of lines to this section. + * Lines containing key/value pairs are added to the map, + * else they are added to the values list. Everything is + * added to the lines list. + */ void append(std::vector const& lines); - /** Append a line to this section. */ + /** + * Append a line to this section. + */ void append(std::string const& line) { append(std::vector{line}); } - /** Returns `true` if a key with the given name exists. */ + /** + * Returns `true` if a key with the given name exists. + */ [[nodiscard]] bool exists(std::string const& name) const; @@ -134,7 +147,9 @@ public: return boost::lexical_cast(iter->second); } - /// Returns a value if present, else another value. + /** + * Returns a value if present, else another value. + */ template [[nodiscard]] T valueOr(std::string const& name, T const& other) const @@ -199,23 +214,27 @@ public: //------------------------------------------------------------------------------ -/** Holds unparsed configuration information. - The raw data sections are processed with intermediate parsers specific - to each module instead of being all parsed in a central location. -*/ +/** + * Holds unparsed configuration information. + * The raw data sections are processed with intermediate parsers specific + * to each module instead of being all parsed in a central location. + */ class BasicConfig { private: std::unordered_map map_; public: - /** Returns `true` if a section with the given name exists. */ + /** + * Returns `true` if a section with the given name exists. + */ [[nodiscard]] bool exists(std::string const& name) const; - /** Returns the section with the given name. - If the section does not exist, an empty section is returned. - */ + /** + * Returns the section with the given name. + * If the section does not exist, an empty section is returned. + */ /** @{ */ Section& section(std::string const& name); @@ -236,37 +255,39 @@ public: } /** @} */ - /** Overwrite a key/value pair with a command line argument - If the section does not exist it is created. - The previous value, if any, is overwritten. - */ + /** + * Overwrite a key/value pair with a command line argument + * If the section does not exist it is created. + * The previous value, if any, is overwritten. + */ void overwrite(std::string const& section, std::string const& key, std::string const& value); - /** Remove all the key/value pairs from the section. + /** + * Remove all the key/value pairs from the section. */ void deprecatedClearSection(std::string const& section); /** - * Set a value that is not a key/value pair. + * Set a value that is not a key/value pair. * - * The value is stored as the section's first value and may be retrieved - * through section::legacy. + * The value is stored as the section's first value and may be retrieved + * through section::legacy. * - * @param section Name of the section to modify. - * @param value Contents of the legacy value. + * @param section Name of the section to modify. + * @param value Contents of the legacy value. */ void legacy(std::string const& section, std::string value); /** - * Get the legacy value of a section. A section with a - * single-line value may be retrieved as a legacy value. + * Get the legacy value of a section. A section with a + * single-line value may be retrieved as a legacy value. * - * @param sectionName Retrieve the contents of this section's - * legacy value. - * @return Contents of the legacy value. + * @param sectionName Retrieve the contents of this section's + * legacy value. + * @return Contents of the legacy value. */ [[nodiscard]] std::string legacy(std::string const& sectionName) const; @@ -289,11 +310,12 @@ protected: //------------------------------------------------------------------------------ -/** Set a value from a configuration Section - If the named value is not found or doesn't parse as a T, - the variable is unchanged. - @return `true` if value was set. -*/ +/** + * Set a value from a configuration Section + * If the named value is not found or doesn't parse as a T, + * the variable is unchanged. + * @return `true` if value was set. + */ template bool set(T& target, std::string const& name, Section const& section) @@ -312,11 +334,12 @@ set(T& target, std::string const& name, Section const& section) return foundAndValid; } -/** Set a value from a configuration Section - If the named value is not found or doesn't cast to T, - the variable is assigned the default. - @return `true` if the named value was found and is valid. -*/ +/** + * Set a value from a configuration Section + * If the named value is not found or doesn't cast to T, + * the variable is assigned the default. + * @return `true` if the named value was found and is valid. + */ template bool set(T& target, T const& defaultValue, std::string const& name, Section const& section) @@ -327,10 +350,11 @@ set(T& target, T const& defaultValue, std::string const& name, Section const& se return foundAndValid; } -/** Retrieve a key/value pair from a section. - @return The value string converted to T if it exists - and can be parsed, or else defaultValue. -*/ +/** + * Retrieve a key/value pair from a section. + * @return The value string converted to T if it exists + * and can be parsed, or else defaultValue. + */ // NOTE This routine might be more clumsy than the previous two template T diff --git a/include/xrpl/core/ClosureCounter.h b/include/xrpl/core/ClosureCounter.h index ed15db032e..33899d671b 100644 --- a/include/xrpl/core/ClosureCounter.h +++ b/include/xrpl/core/ClosureCounter.h @@ -30,8 +30,8 @@ namespace xrpl { * the caller that they should drop the closure and cancel their operation. * `join` blocks until all existing closure substitutes are destroyed. * - * \tparam Ret The return type of the closure. - * \tparam Args The argument types of the closure. + * @tparam Ret The return type of the closure. + * @tparam Args The argument types of the closure. */ template class ClosureCounter @@ -131,18 +131,21 @@ public: ClosureCounter& operator=(ClosureCounter const&) = delete; - /** Destructor verifies all in-flight closures are complete. */ + /** + * Destructor verifies all in-flight closures are complete. + */ ~ClosureCounter() { using namespace std::chrono_literals; join("ClosureCounter", 1s, debugLog()); } - /** Returns once all counted in-flight closures are destroyed. - - @param name Name reported if join time exceeds wait. - @param wait If join() exceeds this duration report to Journal. - @param j Journal written to if wait is exceeded. + /** + * Returns once all counted in-flight closures are destroyed. + * + * @param name Name reported if join time exceeds wait. + * @param wait If join() exceeds this duration report to Journal. + * @param j Journal written to if wait is exceeded. */ void join(char const* name, std::chrono::milliseconds wait, beast::Journal j) @@ -160,13 +163,14 @@ public: } } - /** Wrap the passed closure with a reference counter. - - @param closure Closure that accepts Args parameters and returns Ret. - @return If join() has been called returns std::nullopt. Otherwise - returns a std::optional that wraps closure with a - reference counter. - */ + /** + * Wrap the passed closure with a reference counter. + * + * @param closure Closure that accepts Args parameters and returns Ret. + * @return If join() has been called returns std::nullopt. Otherwise + * returns a std::optional that wraps closure with a + * reference counter. + */ template std::optional> wrap(Closure&& closure) @@ -180,19 +184,22 @@ public: return ret; } - /** Current number of Closures outstanding. Only useful for testing. */ + /** + * Current number of Closures outstanding. Only useful for testing. + */ int count() const { return closureCount_; } - /** Returns true if this has been joined. - - Even if true is returned, counted closures may still be in flight. - However if (joined() && (count() == 0)) there should be no more - counted closures in flight. - */ + /** + * Returns true if this has been joined. + * + * Even if true is returned, counted closures may still be in flight. + * However if (joined() && (count() == 0)) there should be no more + * counted closures in flight. + */ bool joined() const { diff --git a/include/xrpl/core/Coro.ipp b/include/xrpl/core/Coro.ipp index 133caf37a9..9a45dac504 100644 --- a/include/xrpl/core/Coro.ipp +++ b/include/xrpl/core/Coro.ipp @@ -4,8 +4,10 @@ namespace xrpl { -/// Coroutine stack size (1.5 MB). Increased from 1 MB because -/// ASAN-instrumented deep call stacks exceeded the original limit. +/** + * Coroutine stack size (1.5 MB). Increased from 1 MB because + * ASAN-instrumented deep call stacks exceeded the original limit. + */ constexpr std::size_t kCoroStackSize = 1536 * 1024; template diff --git a/include/xrpl/core/HashRouter.h b/include/xrpl/core/HashRouter.h index dad1afb405..20aafecc5f 100644 --- a/include/xrpl/core/HashRouter.h +++ b/include/xrpl/core/HashRouter.h @@ -75,19 +75,21 @@ any(HashRouterFlags flags) class Config; -/** Routing table for objects identified by hash. - - This table keeps track of which hashes have been received by which peers. - It is used to manage the routing and broadcasting of messages in the peer - to peer overlay. -*/ +/** + * Routing table for objects identified by hash. + * + * This table keeps track of which hashes have been received by which peers. + * It is used to manage the routing and broadcasting of messages in the peer + * to peer overlay. + */ class HashRouter { public: // The type here *MUST* match the type of Peer::id_t using PeerShortID = std::uint32_t; - /** Structure used to customize @ref HashRouter behavior. + /** + * Structure used to customize @ref HashRouter behavior. * * Even though these items are configurable, they are undocumented. Don't * change them unless there is a good reason, and network-wide coordination @@ -97,22 +99,27 @@ public: */ struct Setup { - /// Default constructor + /** + * Default constructor + */ explicit Setup() = default; using seconds = std::chrono::seconds; - /** Expiration time for a hash entry + /** + * Expiration time for a hash entry */ seconds holdTime{300}; - /** Amount of time required before a relayed item will be relayed again. + /** + * Amount of time required before a relayed item will be relayed again. */ seconds relayTime{30}; }; private: - /** An entry in the routing table. + /** + * An entry in the routing table. */ class Entry : public CountedObject { @@ -138,26 +145,31 @@ private: flags_ |= flagsToSet; } - /** Return set of peers we've relayed to and reset tracking */ + /** + * Return set of peers we've relayed to and reset tracking + */ std::set releasePeerSet() { return std::move(peers_); } - /** Return seated relay time point if the message has been relayed */ + /** + * Return seated relay time point if the message has been relayed + */ [[nodiscard]] std::optional relayed() const { return relayed_; } - /** Determines if this item should be relayed. - - Checks whether the item has been recently relayed. - If it has, return false. If it has not, update the - last relay timestamp and return true. - */ + /** + * Determines if this item should be relayed. + * + * Checks whether the item has been recently relayed. + * If it has, return false. If it has not, update the + * last relay timestamp and return true. + */ bool shouldRelay(Stopwatch::time_point const& now, std::chrono::seconds relayTime) { @@ -203,11 +215,13 @@ public: bool addSuppressionPeer(uint256 const& key, PeerShortID peer); - /** Add a suppression peer and get message's relay status. + /** + * Add a suppression peer and get message's relay status. * Return pair: * element 1: true if the peer is added. * element 2: optional is seated to the relay time point or - * is unseated if has not relayed yet. */ + * is unseated if has not relayed yet. + */ std::pair> addSuppressionPeerWithStatus(uint256 const& key, PeerShortID peer); @@ -222,28 +236,30 @@ public: HashRouterFlags& flags, std::chrono::seconds txInterval); - /** Set the flags on a hash. - - @return `true` if the flags were changed. `false` if unchanged. - */ + /** + * Set the flags on a hash. + * + * @return `true` if the flags were changed. `false` if unchanged. + */ bool setFlags(uint256 const& key, HashRouterFlags flags); HashRouterFlags getFlags(uint256 const& key); - /** Determines whether the hashed item should be relayed. - - Effects: - - If the item should be relayed, this function will not - return a seated optional again until the relay time has expired. - The internal set of peers will also be reset. - - @return A `std::optional` set of peers which do not need to be - relayed to. If the result is unseated, the item should - _not_ be relayed. - */ + /** + * Determines whether the hashed item should be relayed. + * + * Effects: + * + * If the item should be relayed, this function will not + * return a seated optional again until the relay time has expired. + * The internal set of peers will also be reset. + * + * @return A `std::optional` set of peers which do not need to be + * relayed to. If the result is unseated, the item should + * _not_ be relayed. + */ std::optional> shouldRelay(uint256 const& key); diff --git a/include/xrpl/core/Job.h b/include/xrpl/core/Job.h index e16d7412bf..93b39701be 100644 --- a/include/xrpl/core/Job.h +++ b/include/xrpl/core/Job.h @@ -83,12 +83,13 @@ class Job : public CountedObject public: using clock_type = std::chrono::steady_clock; - /** Default constructor. - - Allows Job to be used as a container type. - - This is used to allow things like jobMap [key] = value. - */ + /** + * Default constructor. + * + * Allows Job to be used as a container type. + * + * This is used to allow things like jobMap [key] = value. + */ // VFALCO NOTE I'd prefer not to have a default constructed object. // What is the semantic meaning of a Job with no associated // function? Having the invariant "all Job objects refer to @@ -108,7 +109,9 @@ public: [[nodiscard]] JobType getType() const; - /** Returns the time when the job was queued. */ + /** + * Returns the time when the job was queued. + */ [[nodiscard]] clock_type::time_point const& queueTime() const; diff --git a/include/xrpl/core/JobQueue.h b/include/xrpl/core/JobQueue.h index e4b64546f3..0c9fc76357 100644 --- a/include/xrpl/core/JobQueue.h +++ b/include/xrpl/core/JobQueue.h @@ -45,20 +45,23 @@ struct CoroCreateT explicit CoroCreateT() = default; }; -/** A pool of threads to perform work. - - A job posted will always run to completion. - - Coroutines that are suspended must be resumed, - and run to completion. - - When the JobQueue stops, it waits for all jobs - and coroutines to finish. -*/ +/** + * A pool of threads to perform work. + * + * A job posted will always run to completion. + * + * Coroutines that are suspended must be resumed, + * and run to completion. + * + * When the JobQueue stops, it waits for all jobs + * and coroutines to finish. + */ class JobQueue : private Workers::Callback { public: - /** Coroutines must run to completion. */ + /** + * Coroutines must run to completion. + */ class Coro : public std::enable_shared_from_this { private: @@ -87,55 +90,64 @@ public: ~Coro(); - /** Suspend coroutine execution. - Effects: - The coroutine's stack is saved. - The associated Job thread is released. - Note: - The associated Job function returns. - Undefined behavior if called consecutively without a corresponding - post. - */ + /** + * Suspend coroutine execution. + * Effects: + * The coroutine's stack is saved. + * The associated Job thread is released. + * Note: + * The associated Job function returns. + * Undefined behavior if called consecutively without a corresponding + * post. + */ void yield() const; - /** Schedule coroutine execution. - Effects: - Returns immediately. - A new job is scheduled to resume the execution of the coroutine. - When the job runs, the coroutine's stack is restored and execution - continues at the beginning of coroutine function or the - statement after the previous call to yield. Undefined behavior if - called after the coroutine has completed with a return (as opposed to - a yield()). Undefined behavior if post() or resume() called - consecutively without a corresponding yield. - - @return true if the Coro's job is added to the JobQueue. - */ + /** + * Schedule coroutine execution. + * Effects: + * Returns immediately. + * A new job is scheduled to resume the execution of the coroutine. + * When the job runs, the coroutine's stack is restored and execution + * continues at the beginning of coroutine function or the + * statement after the previous call to yield. Undefined behavior if + * called after the coroutine has completed with a return (as opposed to + * a yield()). Undefined behavior if post() or resume() called + * consecutively without a corresponding yield. + * + * @return true if the Coro's job is added to the JobQueue. + */ bool post(); - /** Resume coroutine execution. - Effects: - The coroutine continues execution from where it last left off - using this same thread. - If the coroutine has already completed, returns immediately - (handles the documented post-before-yield race condition). - Undefined behavior if resume() or post() called consecutively - without a corresponding yield. - */ + /** + * Resume coroutine execution. + * Effects: + * The coroutine continues execution from where it last left off + * using this same thread. + * If the coroutine has already completed, returns immediately + * (handles the documented post-before-yield race condition). + * Undefined behavior if resume() or post() called consecutively + * without a corresponding yield. + */ void resume(); - /** Returns true if the Coro is still runnable (has not returned). */ + /** + * Returns true if the Coro is still runnable (has not returned). + */ [[nodiscard]] bool runnable() const; - /** Once called, the Coro allows early exit without an assert. */ + /** + * Once called, the Coro allows early exit without an assert. + */ void expectEarlyExit(); - /** Waits until coroutine returns from the user function. */ + /** + * Waits until coroutine returns from the user function. + */ void join(); }; @@ -150,14 +162,15 @@ public: perf::PerfLog& perfLog); ~JobQueue() override; - /** Adds a job to the JobQueue. - - @param type The type of job. - @param name Name of the job. - @param jobHandler Callable with signature void(). Called when the job is executed. - - @return true if jobHandler added to queue. - */ + /** + * Adds a job to the JobQueue. + * + * @param type The type of job. + * @param name Name of the job. + * @param jobHandler Callable with signature void(). Called when the job is executed. + * + * @return true if jobHandler added to queue. + */ template bool addJob(JobType type, std::string const& name, JobHandler&& jobHandler) @@ -170,40 +183,46 @@ public: return false; } - /** Creates a coroutine and adds a job to the queue which will run it. - - @param t The type of job. - @param name Name of the job. - @param f Has a signature of void(std::shared_ptr). Called when the - job executes. - - @return shared_ptr to posted Coro. nullptr if post was not successful. - */ + /** + * Creates a coroutine and adds a job to the queue which will run it. + * + * @param t The type of job. + * @param name Name of the job. + * @param f Has a signature of void(std::shared_ptr). Called when the + * job executes. + * + * @return shared_ptr to posted Coro. nullptr if post was not successful. + */ template std::shared_ptr postCoro(JobType t, std::string const& name, F&& f); - /** Jobs waiting at this priority. + /** + * Jobs waiting at this priority. */ int getJobCount(JobType t) const; - /** Jobs waiting plus running at this priority. + /** + * Jobs waiting plus running at this priority. */ int getJobCountTotal(JobType t) const; - /** All waiting jobs at or greater than this priority. + /** + * All waiting jobs at or greater than this priority. */ int getJobCountGE(JobType t) const; - /** Return a scoped LoadEvent. + /** + * Return a scoped LoadEvent. */ std::unique_ptr makeLoadEvent(JobType t, std::string const& name); - /** Add multiple load events. + /** + * Add multiple load events. */ void addLoadEvents(JobType t, int count, std::chrono::milliseconds elapsed); @@ -216,7 +235,9 @@ public: json::Value getJson(int c = 0); - /** Block until no jobs running. */ + /** + * Block until no jobs running. + */ void rendezvous(); diff --git a/include/xrpl/core/JobTypeInfo.h b/include/xrpl/core/JobTypeInfo.h index b5db0dbaab..302a462ac6 100644 --- a/include/xrpl/core/JobTypeInfo.h +++ b/include/xrpl/core/JobTypeInfo.h @@ -8,21 +8,26 @@ namespace xrpl { -/** Holds all the 'static' information about a job, which does not change */ +/** + * Holds all the 'static' information about a job, which does not change + */ class JobTypeInfo { private: JobType const type_; std::string const name_; - /** The limit on the number of running jobs for this job type. - - A limit of 0 marks this as a "special job" which is not - dispatched via the job queue. + /** + * The limit on the number of running jobs for this job type. + * + * A limit of 0 marks this as a "special job" which is not + * dispatched via the job queue. */ int const limit_; - /** Average and peak latencies for this job type. 0 is none specified */ + /** + * Average and peak latencies for this job type. 0 is none specified + */ std::chrono::milliseconds const avgLatency_; std::chrono::milliseconds const peakLatency_; diff --git a/include/xrpl/core/NetworkIDService.h b/include/xrpl/core/NetworkIDService.h index 009f9ba6f8..8e2b3fcfe2 100644 --- a/include/xrpl/core/NetworkIDService.h +++ b/include/xrpl/core/NetworkIDService.h @@ -4,25 +4,27 @@ namespace xrpl { -/** Service that provides access to the network ID. - - This service provides read-only access to the network ID configured - for this server. The network ID identifies which network (mainnet, - testnet, devnet, or custom network) this server is configured to - connect to. - - Well-known network IDs: - - 0: Mainnet - - 1: Testnet - - 2: Devnet - - 1025+: Custom networks (require NetworkID field in transactions) -*/ +/** + * Service that provides access to the network ID. + * + * This service provides read-only access to the network ID configured + * for this server. The network ID identifies which network (mainnet, + * testnet, devnet, or custom network) this server is configured to + * connect to. + * + * Well-known network IDs: + * - 0: Mainnet + * - 1: Testnet + * - 2: Devnet + * - 1025+: Custom networks (require NetworkID field in transactions) + */ class NetworkIDService { public: virtual ~NetworkIDService() = default; - /** Get the configured network ID + /** + * Get the configured network ID * * @return The network ID this server is configured for */ diff --git a/include/xrpl/core/PeerReservationTable.h b/include/xrpl/core/PeerReservationTable.h index e6f6dd622e..c95c88b967 100644 --- a/include/xrpl/core/PeerReservationTable.h +++ b/include/xrpl/core/PeerReservationTable.h @@ -81,7 +81,7 @@ public: /** * @return the replaced reservation if it existed - * @throw soci::soci_error + * @throws soci::soci_error */ std::optional insertOrAssign(PeerReservation const& reservation); diff --git a/include/xrpl/core/ServiceRegistry.h b/include/xrpl/core/ServiceRegistry.h index 50bf2d7c10..592964134b 100644 --- a/include/xrpl/core/ServiceRegistry.h +++ b/include/xrpl/core/ServiceRegistry.h @@ -83,17 +83,17 @@ using RCLValidations = Validations; using NodeCache = TaggedCache; -/** Service registry for dependency injection. - - This abstract interface provides access to various services and components - used throughout the application. It separates the service locator pattern - from the Application lifecycle management. - - Components that need access to services can hold a reference to - ServiceRegistry rather than Application when they only need service - access and not lifecycle management. - -*/ +/** + * Service registry for dependency injection. + * + * This abstract interface provides access to various services and components + * used throughout the application. It separates the service locator pattern + * from the Application lifecycle management. + * + * Components that need access to services can hold a reference to + * ServiceRegistry rather than Application when they only need service + * access and not lifecycle management. + */ class ServiceRegistry { public: @@ -240,7 +240,9 @@ public: [[nodiscard]] virtual std::optional const& getTrapTxID() const = 0; - /** Retrieve the "wallet database" */ + /** + * Retrieve the "wallet database" + */ virtual DatabaseCon& getWalletDB() = 0; diff --git a/include/xrpl/core/detail/Workers.h b/include/xrpl/core/detail/Workers.h index d20ebf7a64..6829d5a14b 100644 --- a/include/xrpl/core/detail/Workers.h +++ b/include/xrpl/core/detail/Workers.h @@ -60,7 +60,9 @@ class PerfLog; class Workers { public: - /** Called to perform tasks as needed. */ + /** + * Called to perform tasks as needed. + */ struct Callback { virtual ~Callback() = default; @@ -69,27 +71,29 @@ public: Callback& operator=(Callback const&) = delete; - /** Perform a task. - - The call is made on a thread owned by Workers. It is important - that you only process one task from inside your callback. Each - call to addTask will result in exactly one call to processTask. - - @param instance The worker thread instance. - - @see Workers::addTask - */ + /** + * Perform a task. + * + * The call is made on a thread owned by Workers. It is important + * that you only process one task from inside your callback. Each + * call to addTask will result in exactly one call to processTask. + * + * @param instance The worker thread instance. + * + * @see Workers::addTask + */ virtual void processTask(int instance) = 0; }; - /** Create the object. - - A number of initial threads may be optionally specified. The - default is to create one thread per CPU. - - @param threadNames The name given to each created worker thread. - */ + /** + * Create the object. + * + * A number of initial threads may be optionally specified. The + * default is to create one thread per CPU. + * + * @param threadNames The name given to each created worker thread. + */ explicit Workers( Callback& callback, perf::PerfLog* perfLog, @@ -98,49 +102,54 @@ public: ~Workers(); - /** Retrieve the desired number of threads. - - This just returns the number of active threads that were requested. If - there was a recent call to setNumberOfThreads, the actual number of - active threads may be temporarily different from what was last requested. - - @note This function is not thread-safe. - */ + /** + * Retrieve the desired number of threads. + * + * This just returns the number of active threads that were requested. If + * there was a recent call to setNumberOfThreads, the actual number of + * active threads may be temporarily different from what was last requested. + * + * @note This function is not thread-safe. + */ [[nodiscard]] int getNumberOfThreads() const noexcept; - /** Set the desired number of threads. - @note This function is not thread-safe. - */ + /** + * Set the desired number of threads. + * @note This function is not thread-safe. + */ void setNumberOfThreads(int numberOfThreads); - /** Pause all threads and wait until they are paused. - - If a thread is processing a task it will pause as soon as the task - completes. There may still be tasks signaled even after all threads - have paused. - - @note This function is not thread-safe. - */ + /** + * Pause all threads and wait until they are paused. + * + * If a thread is processing a task it will pause as soon as the task + * completes. There may still be tasks signaled even after all threads + * have paused. + * + * @note This function is not thread-safe. + */ void stop(); - /** Add a task to be performed. - - Every call to addTask will eventually result in a call to - Callback::processTask unless the Workers object is destroyed or - the number of threads is never set above zero. - - @note This function is thread-safe. - */ + /** + * Add a task to be performed. + * + * Every call to addTask will eventually result in a call to + * Callback::processTask unless the Workers object is destroyed or + * the number of threads is never set above zero. + * + * @note This function is thread-safe. + */ void addTask(); - /** Get the number of currently executing calls of Callback::processTask. - While this function is thread-safe, the value may not stay - accurate for very long. It's mainly for diagnostic purposes. - */ + /** + * Get the number of currently executing calls of Callback::processTask. + * While this function is thread-safe, the value may not stay + * accurate for very long. It's mainly for diagnostic purposes. + */ [[nodiscard]] int numberOfCurrentlyRunningTasks() const noexcept; diff --git a/include/xrpl/core/detail/semaphore.h b/include/xrpl/core/detail/semaphore.h index e40463e322..abf6705097 100644 --- a/include/xrpl/core/detail/semaphore.h +++ b/include/xrpl/core/detail/semaphore.h @@ -45,14 +45,17 @@ private: public: using size_type = std::size_t; - /** Create the semaphore, with an optional initial count. - If unspecified, the initial count is zero. - */ + /** + * Create the semaphore, with an optional initial count. + * If unspecified, the initial count is zero. + */ explicit BasicSemaphore(size_type count = 0) : count_(count) { } - /** Increment the count and unblock one waiting thread. */ + /** + * Increment the count and unblock one waiting thread. + */ void notify() { @@ -61,7 +64,9 @@ public: cond_.notify_one(); } - /** Block until notify is called. */ + /** + * Block until notify is called. + */ void wait() { @@ -71,9 +76,10 @@ public: --count_; } - /** Perform a non-blocking wait. - @return `true` If the wait would be satisfied. - */ + /** + * Perform a non-blocking wait. + * @return `true` If the wait would be satisfied. + */ bool tryWait() { diff --git a/include/xrpl/crypto/RFC1751.h b/include/xrpl/crypto/RFC1751.h index 278f3c207b..3de65c3028 100644 --- a/include/xrpl/crypto/RFC1751.h +++ b/include/xrpl/crypto/RFC1751.h @@ -16,13 +16,14 @@ public: static void getEnglishFromKey(std::string& strHuman, std::string const& strKey); - /** Chooses a single dictionary word from the data. - - This is not particularly secure but it can be useful to provide - a unique name for something given a GUID or fixed data. We use - it to turn the pubkey_node into an easily remembered and identified - 4 character string. - */ + /** + * Chooses a single dictionary word from the data. + * + * This is not particularly secure but it can be useful to provide + * a unique name for something given a GUID or fixed data. We use + * it to turn the pubkey_node into an easily remembered and identified + * 4 character string. + */ static std::string getWordFromBlob(void const* blob, size_t bytes); diff --git a/include/xrpl/crypto/csprng.h b/include/xrpl/crypto/csprng.h index cdc6a723c8..e19d33a464 100644 --- a/include/xrpl/crypto/csprng.h +++ b/include/xrpl/crypto/csprng.h @@ -7,14 +7,15 @@ namespace xrpl { -/** A cryptographically secure random number engine - - The engine is thread-safe (it uses a lock to serialize - access) and will, automatically, mix in some randomness - from std::random_device. - - Meets the requirements of UniformRandomNumberEngine -*/ +/** + * A cryptographically secure random number engine + * + * The engine is thread-safe (it uses a lock to serialize + * access) and will, automatically, mix in some randomness + * from std::random_device. + * + * Meets the requirements of UniformRandomNumberEngine + */ class CsprngEngine { private: @@ -34,15 +35,21 @@ public: CsprngEngine(); ~CsprngEngine(); - /** Mix entropy into the pool */ + /** + * Mix entropy into the pool + */ void mixEntropy(void* buffer = nullptr, std::size_t count = 0); - /** Generate a random integer */ + /** + * Generate a random integer + */ result_type operator()(); - /** Fill a buffer with the requested amount of random data */ + /** + * Fill a buffer with the requested amount of random data + */ void operator()(void* ptr, std::size_t count); @@ -61,14 +68,15 @@ public: } }; -/** The default cryptographically secure PRNG - - Use this when you need to generate random numbers or - data that will be used for encryption or passed into - cryptographic routines. - - This meets the requirements of UniformRandomNumberEngine -*/ +/** + * The default cryptographically secure PRNG + * + * Use this when you need to generate random numbers or + * data that will be used for encryption or passed into + * cryptographic routines. + * + * This meets the requirements of UniformRandomNumberEngine + */ CsprngEngine& cryptoPrng(); diff --git a/include/xrpl/crypto/secure_erase.h b/include/xrpl/crypto/secure_erase.h index 74284b03f7..38531afc1d 100644 --- a/include/xrpl/crypto/secure_erase.h +++ b/include/xrpl/crypto/secure_erase.h @@ -4,20 +4,21 @@ namespace xrpl { -/** Attempts to clear the given blob of memory. - - The underlying implementation of this function takes pains to - attempt to outsmart the compiler from optimizing the clearing - away. Please note that, despite that, remnants of content may - remain floating around in memory as well as registers, caches - and more. - - For a more in-depth discussion of the subject please see the - below posts by Colin Percival: - - http://www.daemonology.net/blog/2014-09-04-how-to-zero-a-buffer.html - http://www.daemonology.net/blog/2014-09-06-zeroing-buffers-is-insufficient.html -*/ +/** + * Attempts to clear the given blob of memory. + * + * The underlying implementation of this function takes pains to + * attempt to outsmart the compiler from optimizing the clearing + * away. Please note that, despite that, remnants of content may + * remain floating around in memory as well as registers, caches + * and more. + * + * For a more in-depth discussion of the subject please see the + * below posts by Colin Percival: + * + * http://www.daemonology.net/blog/2014-09-04-how-to-zero-a-buffer.html + * http://www.daemonology.net/blog/2014-09-06-zeroing-buffers-is-insufficient.html + */ void secureErase(void* dest, std::size_t bytes); diff --git a/include/xrpl/json/JsonPropertyStream.h b/include/xrpl/json/JsonPropertyStream.h index 405a61cd34..498283c16b 100644 --- a/include/xrpl/json/JsonPropertyStream.h +++ b/include/xrpl/json/JsonPropertyStream.h @@ -8,7 +8,9 @@ namespace xrpl { -/** A PropertyStream::Sink which produces a json::Value of type ValueType::Object. */ +/** + * A PropertyStream::Sink which produces a json::Value of type ValueType::Object. + */ class JsonPropertyStream : public beast::PropertyStream { public: diff --git a/include/xrpl/json/Output.h b/include/xrpl/json/Output.h index c01253f713..53d453c277 100644 --- a/include/xrpl/json/Output.h +++ b/include/xrpl/json/Output.h @@ -17,18 +17,20 @@ stringOutput(std::string& s) return [&](boost::beast::string_view const& b) { s.append(b.data(), b.size()); }; } -/** Writes a minimal representation of a Json value to an Output in O(n) time. - - Data is streamed right to the output, so only a marginal amount of memory is - used. This can be very important for a very large json::Value. +/** + * Writes a minimal representation of a Json value to an Output in O(n) time. + * + * Data is streamed right to the output, so only a marginal amount of memory is + * used. This can be very important for a very large json::Value. */ void outputJson(json::Value const&, Output const&); -/** Return the minimal string representation of a json::Value in O(n) time. - - This requires a memory allocation for the full size of the output. - If possible, use outputJson(). +/** + * Return the minimal string representation of a json::Value in O(n) time. + * + * This requires a memory allocation for the full size of the output. + * If possible, use outputJson(). */ std::string jsonAsString(json::Value const&); diff --git a/include/xrpl/json/Writer.h b/include/xrpl/json/Writer.h index 024876a43c..ec7fd6a0d2 100644 --- a/include/xrpl/json/Writer.h +++ b/include/xrpl/json/Writer.h @@ -12,98 +12,98 @@ namespace json { /** - * Writer implements an O(1)-space, O(1)-granular output JSON writer. + * Writer implements an O(1)-space, O(1)-granular output JSON writer. * - * O(1)-space means that it uses a fixed amount of memory, and that there are - * no heap allocations at each step of the way. + * O(1)-space means that it uses a fixed amount of memory, and that there are + * no heap allocations at each step of the way. * - * O(1)-granular output means the writer only outputs in small segments of a - * bounded size, using a bounded number of CPU cycles in doing so. This is - * very helpful in scheduling long jobs. + * O(1)-granular output means the writer only outputs in small segments of a + * bounded size, using a bounded number of CPU cycles in doing so. This is + * very helpful in scheduling long jobs. * - * The tradeoff is that you have to fill items in the JSON tree as you go, - * and you can never go backward. + * The tradeoff is that you have to fill items in the JSON tree as you go, + * and you can never go backward. * - * Writer can write single JSON tokens, but the typical use is to write out an - * entire JSON object. For example: + * Writer can write single JSON tokens, but the typical use is to write out an + * entire JSON object. For example: * - * { - * Writer w (out); + * { + * Writer w (out); * - * w.startObject (); // Start the root object. - * w.set ("hello", "world"); - * w.set ("goodbye", 23); - * w.finishObject (); // Finish the root object. - * } + * w.startObject (); // Start the root object. + * w.set ("hello", "world"); + * w.set ("goodbye", 23); + * w.finishObject (); // Finish the root object. + * } * - * which outputs the string + * which outputs the string * - * {"hello":"world","goodbye":23} + * {"hello":"world","goodbye":23} * - * There can be an object inside an object: + * There can be an object inside an object: * - * { - * Writer w (out); + * { + * Writer w (out); * - * w.startObject (); // Start the root object. - * w.set ("hello", "world"); + * w.startObject (); // Start the root object. + * w.set ("hello", "world"); * - * w.startObjectSet ("subobject"); // Start a sub-object. - * w.set ("goodbye", 23); // Add a key, value assignment. - * w.finishObject (); // Finish the sub-object. + * w.startObjectSet ("subobject"); // Start a sub-object. + * w.set ("goodbye", 23); // Add a key, value assignment. + * w.finishObject (); // Finish the sub-object. * - * w.finishObject (); // Finish the root-object. - * } + * w.finishObject (); // Finish the root-object. + * } * - * which outputs the string + * which outputs the string * - * {"hello":"world","subobject":{"goodbye":23}}. + * {"hello":"world","subobject":{"goodbye":23}}. * - * Arrays work similarly + * Arrays work similarly * - * { - * Writer w (out); - * w.startObject (); // Start the root object. + * { + * Writer w (out); + * w.startObject (); // Start the root object. * - * w.startArraySet ("hello"); // Start an array. - * w.append (23) // Append some items. - * w.append ("skidoo") - * w.finishArray (); // Finish the array. + * w.startArraySet ("hello"); // Start an array. + * w.append (23) // Append some items. + * w.append ("skidoo") + * w.finishArray (); // Finish the array. * - * w.finishObject (); // Finish the root object. - * } + * w.finishObject (); // Finish the root object. + * } * - * which outputs the string + * which outputs the string * - * {"hello":[23,"skidoo"]}. + * {"hello":[23,"skidoo"]}. * * - * If you've reached the end of a long object, you can just use finishAll() - * which finishes all arrays and objects that you have started. + * If you've reached the end of a long object, you can just use finishAll() + * which finishes all arrays and objects that you have started. * - * { - * Writer w (out); - * w.startObject (); // Start the root object. + * { + * Writer w (out); + * w.startObject (); // Start the root object. * - * w.startArraySet ("hello"); // Start an array. - * w.append (23) // Append an item. + * w.startArraySet ("hello"); // Start an array. + * w.append (23) // Append an item. * - * w.startArrayAppend () // Start a sub-array. - * w.append ("one"); - * w.append ("two"); + * w.startArrayAppend () // Start a sub-array. + * w.append ("one"); + * w.append ("two"); * - * w.startObjectAppend (); // Append a sub-object. - * w.finishAll (); // Finish everything. - * } + * w.startObjectAppend (); // Append a sub-object. + * w.finishAll (); // Finish everything. + * } * - * which outputs the string + * which outputs the string * - * {"hello":[23,["one","two",{}]]}. + * {"hello":[23,["one","two",{}]]}. * - * For convenience, the destructor of Writer calls w.finishAll() which makes - * sure that all arrays and objects are closed. This means that you can throw - * an exception, or have a coroutine simply clean up the stack, and be sure - * that you do in fact generate a complete JSON object. + * For convenience, the destructor of Writer calls w.finishAll() which makes + * sure that all arrays and objects are closed. This means that you can throw + * an exception, or have a coroutine simply clean up the stack, and be sure + * that you do in fact generate a complete JSON object. */ class Writer @@ -118,26 +118,37 @@ public: ~Writer(); - /** Start a new collection at the root level. */ + /** + * Start a new collection at the root level. + */ void startRoot(CollectionType); - /** Start a new collection inside an array. */ + /** + * Start a new collection inside an array. + */ void startAppend(CollectionType); - /** Start a new collection inside an object. */ + /** + * Start a new collection inside an object. + */ void startSet(CollectionType, std::string const& key); - /** Finish the collection most recently started. */ + /** + * Finish the collection most recently started. + */ void finish(); - /** Finish all objects and arrays. After finishArray() has been called, no - * more operations can be performed. */ + /** + * Finish all objects and arrays. After finishArray() has been called, no + * more operations can be performed. + */ void finishAll(); - /** Append a value to an array. + /** + * Append a value to an array. * * Scalar must be a scalar - that is, a number, boolean, string, string * literal, nullptr or json::Value @@ -150,12 +161,15 @@ public: output(t); } - /** Add a comma before this next item if not the first item in an array. - Useful if you are writing the actual array yourself. */ + /** + * Add a comma before this next item if not the first item in an array. + * Useful if you are writing the actual array yourself. + */ void rawAppend(); - /** Add a key, value assignment to an object. + /** + * Add a key, value assignment to an object. * * Scalar must be a scalar - that is, a number, boolean, string, string * literal, or nullptr. @@ -174,8 +188,10 @@ public: output(t); } - /** Emit just "tag": as part of an object. Useful if you are writing the - actual value data yourself. */ + /** + * Emit just "tag": as part of an object. Useful if you are writing the + * actual value data yourself. + */ void rawSet(std::string const& key); @@ -194,22 +210,32 @@ public: void output(json::Value const&); - /** Output a null. */ + /** + * Output a null. + */ void output(std::nullptr_t); - /** Output a float. */ + /** + * Output a float. + */ void output(float); - /** Output a double. */ + /** + * Output a double. + */ void output(double); - /** Output a bool. */ + /** + * Output a bool. + */ void output(bool); - /** Output numbers or booleans. */ + /** + * Output numbers or booleans. + */ template void output(Type t) diff --git a/include/xrpl/json/json_reader.h b/include/xrpl/json/json_reader.h index d1e4ada579..ed60f49ce4 100644 --- a/include/xrpl/json/json_reader.h +++ b/include/xrpl/json/json_reader.h @@ -12,9 +12,9 @@ namespace json { -/** \brief Unserialize a JSON document into a +/** + * @brief Unserialize a JSON document into a * Value. - * */ class Reader { @@ -22,48 +22,55 @@ public: using Char = char; using Location = Char const*; - /** \brief Constructs a Reader allowing all features + /** + * @brief Constructs a Reader allowing all features * for parsing. */ Reader() = default; - /** \brief Read a Value from a JSON - * document. \param document UTF-8 encoded string containing the document to - * read. \param root [out] Contains the root value of the document if it was + /** + * @brief Read a Value from a JSON + * document. @param document UTF-8 encoded string containing the document to + * read. @param root [out] Contains the root value of the document if it was * successfully parsed. - * \return \c true if the document was successfully parsed, \c false if an + * @return @c true if the document was successfully parsed, @c false if an * error occurred. */ bool parse(std::string const& document, Value& root); - /** \brief Read a Value from a JSON - * document. \param document UTF-8 encoded string containing the document to - * read. \param root [out] Contains the root value of the document if it was + /** + * @brief Read a Value from a JSON + * document. @param document UTF-8 encoded string containing the document to + * read. @param root [out] Contains the root value of the document if it was * successfully parsed. - * \return \c true if the document was successfully parsed, \c false if an + * @return @c true if the document was successfully parsed, @c false if an * error occurred. */ bool parse(char const* beginDoc, char const* endDoc, Value& root); - /// \brief Parse from input stream. - /// \see json::operator>>(std::istream&, json::Value&). + /** + * @brief Parse from input stream. + * @see json::operator>>(std::istream&, json::Value&). + */ bool parse(std::istream& is, Value& root); - /** \brief Read a Value from a JSON buffer - * sequence. \param root [out] Contains the root value of the document if it - * was successfully parsed. \param UTF-8 encoded buffer sequence. \return \c - * true if the buffer was successfully parsed, \c false if an error + /** + * @brief Read a Value from a JSON buffer + * sequence. @param root [out] Contains the root value of the document if it + * was successfully parsed. @param UTF-8 encoded buffer sequence. @return @c + * true if the buffer was successfully parsed, @c false if an error * occurred. */ template bool parse(Value& root, BufferSequence const& bs); - /** \brief Returns a user friendly string that list errors in the parsed - * document. \return Formatted error message with the list of errors with + /** + * @brief Returns a user friendly string that list errors in the parsed + * document. @return Formatted error message with the list of errors with * their location in the parsed document. An empty string is returned if no * error occurred during parsing. */ @@ -195,30 +202,31 @@ Reader::parse(Value& root, BufferSequence const& bs) return parse(s, root); } -/** \brief Read from 'sin' into 'root'. - - Always keep comments from the input JSON. - - This can be used to read a file into a particular sub-object. - For example: - \code - json::Value root; - cin >> root["dir"]["file"]; - cout << root; - \endcode - Result: - \verbatim - { -"dir": { - "file": { - // The input stream JSON would be nested here. - } -} - } - \endverbatim - \throw std::exception on parse error. - \see json::operator<<() -*/ +/** + * @brief Read from 'sin' into 'root'. + * + * Always keep comments from the input JSON. + * + * This can be used to read a file into a particular sub-object. + * For example: + * @code + * json::Value root; + * cin >> root["dir"]["file"]; + * cout << root; + * @endcode + * Result: + * @verbatim + * { + * "dir": { + * "file": { + * // The input stream JSON would be nested here. + * } + * } + * } + * @endverbatim + * @throws std::exception on parse error. + * @see json::operator<<() + */ std::istream& operator>>(std::istream&, Value&); diff --git a/include/xrpl/json/json_value.h b/include/xrpl/json/json_value.h index f786c6a9dc..47ad3ac1e0 100644 --- a/include/xrpl/json/json_value.h +++ b/include/xrpl/json/json_value.h @@ -9,11 +9,13 @@ #include #include -/** \brief JSON (JavaScript Object Notation). +/** + * @brief JSON (JavaScript Object Notation). */ namespace json { -/** \brief Type of the value held by a Value object. +/** + * @brief Type of the value held by a Value object. */ enum class ValueType { Null = 0, ///< 'null' value @@ -26,19 +28,20 @@ enum class ValueType { Object ///< object value (collection of name/value pairs). }; -/** \brief Lightweight wrapper to tag static string. +/** + * @brief Lightweight wrapper to tag static string. * * Value constructor and ValueType::Object member assignment takes advantage of the * StaticString and avoid the cost of string duplication when storing the * string or the member name. * * Example of usage: - * \code + * @code * json::Value aValue( StaticString("some text") ); * json::Value object; * static const StaticString code("code"); * object[code] = 1234; - * \endcode + * @endcode */ class StaticString { @@ -99,7 +102,8 @@ operator!=(StaticString x, std::string const& y) return !(y == x); } -/** \brief Represents a JSON value. +/** + * @brief Represents a JSON value. * * This class is a discriminated union wrapper that can represent a: * - signed integer [range: Value::kMinInt - Value::kMaxInt] @@ -175,37 +179,39 @@ public: using ObjectValues = std::map; public: - /** \brief Create a default Value of the given type. - - This is a very useful constructor. - To create an empty array, pass ValueType::Array. - To create an empty object, pass ValueType::Object. - Another Value can then be set to this one by assignment. - This is useful since clear() and resize() will not alter types. - - Examples: - \code - json::Value null_value; // null - json::Value arr_value(json::ValueType::Array); // [] - json::Value obj_value(json::ValueType::Object); // {} - \endcode - */ + /** + * @brief Create a default Value of the given type. + * + * This is a very useful constructor. + * To create an empty array, pass ValueType::Array. + * To create an empty object, pass ValueType::Object. + * Another Value can then be set to this one by assignment. + * This is useful since clear() and resize() will not alter types. + * + * Examples: + * @code + * json::Value null_value; // null + * json::Value arr_value(json::ValueType::Array); // [] + * json::Value obj_value(json::ValueType::Object); // {} + * @endcode + */ Value(ValueType type = ValueType::Null); Value(Int value); Value(UInt value); Value(double value); Value(char const* value); Value(xrpl::Number const& value); - /** \brief Constructs a value from a static string. - + /** + * @brief Constructs a value from a static string. + * * Like other value string constructor but do not duplicate the string for * internal storage. The given string must remain alive after the call to - this + * this * constructor. * Example of usage: - * \code + * @code * json::Value aValue( StaticString("some text") ); - * \endcode + * @endcode */ Value(StaticString const& value); Value(std::string const& value); @@ -220,7 +226,9 @@ public: Value(Value&& other) noexcept; - /// Swap values. + /** + * Swap values. + */ void swap(Value& other) noexcept; @@ -229,7 +237,9 @@ public: [[nodiscard]] char const* asCString() const; - /** Returns the unquoted string value. */ + /** + * Returns the unquoted string value. + */ [[nodiscard]] std::string asString() const; [[nodiscard]] Int @@ -241,13 +251,17 @@ public: [[nodiscard]] bool asBool() const; - /** Correct absolute value from int or unsigned int */ + /** + * Correct absolute value from int or unsigned int + */ [[nodiscard]] UInt asAbsUInt() const; // TODO: What is the "empty()" method this docstring mentions? - /** isNull() tests to see if this field is null. Don't use this method to - test for emptiness: use empty(). */ + /** + * isNull() tests to see if this field is null. Don't use this method to + * test for emptiness: use empty(). + */ [[nodiscard]] bool isNull() const; [[nodiscard]] bool @@ -276,116 +290,157 @@ public: [[nodiscard]] bool isConvertibleTo(ValueType other) const; - /// Number of values in array or object + /** + * Number of values in array or object + */ [[nodiscard]] UInt size() const; - /** Returns false if this is an empty array, empty object, empty string, - or null. */ + /** + * Returns false if this is an empty array, empty object, empty string, + * or null. + */ explicit operator bool() const; - /// Remove all object members and array elements. - /// \pre type() is ValueType::Array, ValueType::Object, or ValueType::Null - /// \post type() is unchanged + /** + * Remove all object members and array elements. + * @pre type() is ValueType::Array, ValueType::Object, or ValueType::Null + * @post type() is unchanged + */ void clear(); - /// Access an array element (zero based index ). - /// If the array contains less than index element, then null value are - /// inserted in the array so that its size is index+1. (You may need to say - /// 'value[0u]' to get your compiler to distinguish - /// this from the operator[] which takes a string.) + /** + * Access an array element (zero based index ). + * If the array contains less than index element, then null value are + * inserted in the array so that its size is index+1. (You may need to say + * 'value[0u]' to get your compiler to distinguish + * this from the operator[] which takes a string.) + */ Value& operator[](UInt index); - /// Access an array element (zero based index ) - /// (You may need to say 'value[0u]' to get your compiler to distinguish - /// this from the operator[] which takes a string.) + /** + * Access an array element (zero based index ) + * (You may need to say 'value[0u]' to get your compiler to distinguish + * this from the operator[] which takes a string.) + */ Value const& operator[](UInt index) const; - /// If the array contains at least index+1 elements, returns the element - /// value, otherwise returns defaultValue. + /** + * If the array contains at least index+1 elements, returns the element + * value, otherwise returns defaultValue. + */ [[nodiscard]] Value get(UInt index, Value const& defaultValue) const; - /// Return true if index < size(). + /** + * Return true if index < size(). + */ [[nodiscard]] bool isValidIndex(UInt index) const; - /// \brief Append value to array at the end. - /// - /// Equivalent to jsonvalue[jsonvalue.size()] = value; + /** + * @brief Append value to array at the end. + * + * Equivalent to jsonvalue[jsonvalue.size()] = value; + */ Value& append(Value const& value); Value& append(Value&& value); - /// Access an object value by name, create a null member if it does not - /// exist. + /** + * Access an object value by name, create a null member if it does not + * exist. + */ Value& operator[](char const* key); - /// Access an object value by name, returns null if there is no member with - /// that name. + /** + * Access an object value by name, returns null if there is no member with + * that name. + */ Value const& operator[](char const* key) const; - /// Access an object value by name, create a null member if it does not - /// exist. + /** + * Access an object value by name, create a null member if it does not + * exist. + */ Value& operator[](std::string const& key); - /// Access an object value by name, returns null if there is no member with - /// that name. + /** + * Access an object value by name, returns null if there is no member with + * that name. + */ Value const& operator[](std::string const& key) const; - /** \brief Access an object value by name, create a null member if it does - not exist. - + /** + * @brief Access an object value by name, create a null member if it does + * not exist. + * * If the object as no entry for that name, then the member name used to - store + * store * the new entry is not duplicated. * Example of use: - * \code + * @code * json::Value object; * static const StaticString code("code"); * object[code] = 1234; - * \endcode + * @endcode */ Value& operator[](StaticString const& key); Value const& operator[](StaticString const& key) const; - /// Return the member named key if it exist, defaultValue otherwise. + /** + * Return the member named key if it exist, defaultValue otherwise. + */ Value get(char const* key, Value const& defaultValue) const; - /// Return the member named key if it exist, defaultValue otherwise. + /** + * Return the member named key if it exist, defaultValue otherwise. + */ [[nodiscard]] Value get(std::string const& key, Value const& defaultValue) const; - /// \brief Remove and return the named member. - /// - /// Do nothing if it did not exist. - /// \return the removed Value, or null. - /// \pre type() is ValueType::Object or ValueType::Null - /// \post type() is unchanged + /** + * @brief Remove and return the named member. + * + * Do nothing if it did not exist. + * @return the removed Value, or null. + * @pre type() is ValueType::Object or ValueType::Null + * @post type() is unchanged + */ Value removeMember(char const* key); - /// Same as removeMember(const char*) + /** + * Same as removeMember(const char*) + */ Value removeMember(std::string const& key); - /// Return true if the object has a member named key. + /** + * Return true if the object has a member named key. + */ bool isMember(char const* key) const; - /// Return true if the object has a member named key. + /** + * Return true if the object has a member named key. + */ [[nodiscard]] bool isMember(std::string const& key) const; - /// Return true if the object has a member named key. + /** + * Return true if the object has a member named key. + */ [[nodiscard]] bool isMember(StaticString const& key) const; - /// \brief Return a list of the member names. - /// - /// If null, return an empty list. - /// \pre type() is ValueType::Object or ValueType::Null - /// \post if type() was ValueType::Null, it remains ValueType::Null + /** + * @brief Return a list of the member names. + * + * If null, return an empty list. + * @pre type() is ValueType::Object or ValueType::Null + * @post if type() was ValueType::Null, it remains ValueType::Null + */ [[nodiscard]] Members getMemberNames() const; @@ -461,7 +516,8 @@ operator>=(Value const& x, Value const& y) return !(x < y); } -/** \brief Experimental do not use: Allocator to customize member name and +/** + * @brief Experimental do not use: Allocator to customize member name and * string value memory management done by Value. * * - makeMemberName() and releaseMemberName() are called to respectively @@ -486,8 +542,8 @@ public: releaseStringValue(char* value) = 0; }; -/** \brief base class for Value iterators. - * +/** + * @brief base class for Value iterators. */ class ValueIteratorBase { @@ -512,17 +568,23 @@ public: return !isEqual(other); } - /// Return either the index or the member name of the referenced value as a - /// Value. + /** + * Return either the index or the member name of the referenced value as a + * Value. + */ [[nodiscard]] Value key() const; - /// Return the index of the referenced Value. -1 if it is not an ValueType::Array. + /** + * Return the index of the referenced Value. -1 if it is not an ValueType::Array. + */ [[nodiscard]] UInt index() const; - /// Return the member name of the referenced Value. "" if it is not an - /// ValueType::Object. + /** + * Return the member name of the referenced Value. "" if it is not an + * ValueType::Object. + */ [[nodiscard]] char const* memberName() const; @@ -551,8 +613,8 @@ private: bool isNull_; }; -/** \brief const iterator for object and array value. - * +/** + * @brief const iterator for object and array value. */ class ValueConstIterator : public ValueIteratorBase { @@ -569,7 +631,8 @@ public: ValueConstIterator(ValueConstIterator const& other) = default; private: - /*! \internal Use by Value to create an iterator. + /** + * @internal Use by Value to create an iterator. */ explicit ValueConstIterator(Value::ObjectValues::iterator const& current); @@ -614,7 +677,8 @@ public: } }; -/** \brief Iterator for object and array value. +/** + * @brief Iterator for object and array value. */ class ValueIterator : public ValueIteratorBase { @@ -632,7 +696,8 @@ public: ValueIterator(ValueIterator const& other); private: - /*! \internal Use by Value to create an iterator. + /** + * @internal Use by Value to create an iterator. */ explicit ValueIterator(Value::ObjectValues::iterator const& current); diff --git a/include/xrpl/json/json_writer.h b/include/xrpl/json/json_writer.h index 4bc15b71da..65c8b20931 100644 --- a/include/xrpl/json/json_writer.h +++ b/include/xrpl/json/json_writer.h @@ -13,7 +13,8 @@ namespace json { class Value; -/** \brief Abstract class for writers. +/** + * @brief Abstract class for writers. */ class WriterBase { @@ -23,12 +24,13 @@ public: write(Value const& root) = 0; }; -/** \brief Outputs a Value in JSON format +/** + * @brief Outputs a Value in JSON format * without formatting (not human friendly). * * The JSON document is written in a single line. It is not intended for 'human' * consumption, but may be useful to support feature such as RPC where bandwidth - * is limited. \sa Reader, Value + * is limited. @see Reader, Value */ class FastWriter : public WriterBase @@ -48,7 +50,8 @@ private: std::string document_; }; -/** \brief Writes a Value in JSON format in a +/** + * @brief Writes a Value in JSON format in a * human friendly way. * * The rules for line break and indent are as follow: @@ -64,7 +67,7 @@ private: * - otherwise, it the values do not fit on one line, or the array contains * object or non empty array, then print one value per line. * - * \sa Reader, Value + * @see Reader, Value */ class StyledWriter : public WriterBase { @@ -73,8 +76,9 @@ public: ~StyledWriter() override = default; public: // overridden from Writer - /** \brief Serialize a Value in JSON - * format. \param root Value to serialize. \return String containing the + /** + * @brief Serialize a Value in JSON + * format. @param root Value to serialize. @return String containing the * JSON document that represents the root value. */ std::string @@ -108,26 +112,27 @@ private: bool addChildValues_{}; }; -/** \brief Writes a Value in JSON format in a - human friendly way, to a stream rather than to a string. +/** + * @brief Writes a Value in JSON format in a + * human friendly way, to a stream rather than to a string. * * The rules for line break and indent are as follow: * - Object value: * - if empty then print {} without indent and line break * - if not empty the print '{', line break & indent, print one value per - line + * line * and then unindent and line break and print '}'. * - Array value: * - if empty then print [] without indent and line break * - if the array contains no object value, empty array or some other value - types, + * types, * and all the values fit on one lines, then print the array on a single - line. + * line. * - otherwise, it the values do not fit on one line, or the array contains * object or non empty array, then print one value per line. * - * \param indentation Each level will be indented by this amount extra. - * \sa Reader, Value + * @param indentation Each level will be indented by this amount extra. + * @see Reader, Value */ class StyledStreamWriter { @@ -136,10 +141,11 @@ public: ~StyledStreamWriter() = default; public: - /** \brief Serialize a Value in JSON - * format. \param out Stream to write to. (Can be ostringstream, e.g.) - * \param root Value to serialize. - * \note There is no point in deriving from Writer, since write() should not + /** + * @brief Serialize a Value in JSON + * format. @param out Stream to write to. (Can be ostringstream, e.g.) + * @param root Value to serialize. + * @note There is no point in deriving from Writer, since write() should not * return a value. */ void @@ -184,8 +190,10 @@ valueToString(bool value); std::string valueToQuotedString(char const* value); -/// \brief Output using the StyledStreamWriter. -/// \see json::operator>>() +/** + * @brief Output using the StyledStreamWriter. + * @see json::operator>>() + */ std::ostream& operator<<(std::ostream&, Value const& root); @@ -265,12 +273,13 @@ writeValue(Write const& write, Value const& value) } // namespace detail -/** Stream compact JSON to the specified function. - - @param jv The json::Value to write - @param write Invocable with signature void(void const*, std::size_t) that - is called when output should be written to the stream. -*/ +/** + * Stream compact JSON to the specified function. + * + * @param jv The json::Value to write + * @param write Invocable with signature void(void const*, std::size_t) that + * is called when output should be written to the stream. + */ template void stream(json::Value const& jv, Write const& write) @@ -279,29 +288,31 @@ stream(json::Value const& jv, Write const& write) write("\n", 1); } -/** Decorator for streaming out compact json - - Use - - json::Value jv; - out << json::Compact{jv} - - to write a single-line, compact version of `jv` to the stream, rather - than the styled format that comes from undecorated streaming. -*/ +/** + * Decorator for streaming out compact json + * + * Use + * + * json::Value jv; + * out << json::Compact{jv} + * + * to write a single-line, compact version of `jv` to the stream, rather + * than the styled format that comes from undecorated streaming. + */ class Compact { json::Value jv_; public: - /** Wrap a json::Value for compact streaming - - @param jv The json::Value to stream - - @note For now, we do not support wrapping lvalues to avoid - potentially costly copies. If we find a need, we can consider - adding support for compact lvalue streaming in the future. - */ + /** + * Wrap a json::Value for compact streaming + * + * @param jv The json::Value to stream + * + * @note For now, we do not support wrapping lvalues to avoid + * potentially costly copies. If we find a need, we can consider + * adding support for compact lvalue streaming in the future. + */ Compact(json::Value&& jv) : jv_{std::move(jv)} { } diff --git a/include/xrpl/json/to_string.h b/include/xrpl/json/to_string.h index 1d7b4c785a..bdd7a51e6a 100644 --- a/include/xrpl/json/to_string.h +++ b/include/xrpl/json/to_string.h @@ -6,11 +6,15 @@ namespace json { -/** Writes a json::Value to an std::string. */ +/** + * Writes a json::Value to an std::string. + */ std::string to_string(Value const&); -/** Writes a json::Value to an std::string. */ +/** + * Writes a json::Value to an std::string. + */ std::string pretty(Value const&); diff --git a/include/xrpl/ledger/AcceptedLedgerTx.h b/include/xrpl/ledger/AcceptedLedgerTx.h index f59b8a074d..283dcf6e24 100644 --- a/include/xrpl/ledger/AcceptedLedgerTx.h +++ b/include/xrpl/ledger/AcceptedLedgerTx.h @@ -21,17 +21,17 @@ namespace xrpl { /** - A transaction that is in a closed ledger. - - Description - - An accepted ledger transaction contains additional information that the - server needs to tell clients about the transaction. For example, - - The transaction in JSON form - - Which accounts are affected - * This is used by InfoSub to report to clients - - Cached stuff -*/ + * A transaction that is in a closed ledger. + * + * Description + * + * An accepted ledger transaction contains additional information that the + * server needs to tell clients about the transaction. For example, + * - The transaction in JSON form + * - Which accounts are affected + * * This is used by InfoSub to report to clients + * - Cached stuff + */ class AcceptedLedgerTx : public CountedObject { public: diff --git a/include/xrpl/ledger/AmendmentTable.h b/include/xrpl/ledger/AmendmentTable.h index 6598be5a5c..c3ef779eb1 100644 --- a/include/xrpl/ledger/AmendmentTable.h +++ b/include/xrpl/ledger/AmendmentTable.h @@ -37,10 +37,11 @@ namespace xrpl { class ServiceRegistry; -/** The amendment table stores the list of enabled and potential amendments. - Individuals amendments are voted on by validators during the consensus - process. -*/ +/** + * The amendment table stores the list of enabled and potential amendments. + * Individuals amendments are voted on by validators during the consensus + * process. + */ class AmendmentTable { public: @@ -90,11 +91,15 @@ public: [[nodiscard]] virtual json::Value getJson(bool isAdmin) const = 0; - /** Returns a json::ValueType::Object. */ + /** + * Returns a json::ValueType::Object. + */ [[nodiscard]] virtual json::Value getJson(uint256 const& amendment, bool isAdmin) const = 0; - /** Called when a new fully-validated ledger is accepted. */ + /** + * Called when a new fully-validated ledger is accepted. + */ void doValidatedLedger(std::shared_ptr const& lastValidatedLedger) { @@ -107,9 +112,10 @@ public: } } - /** Called to determine whether the amendment logic needs to process - a new validated ledger. (If it could have changed things.) - */ + /** + * Called to determine whether the amendment logic needs to process + * a new validated ledger. (If it could have changed things.) + */ [[nodiscard]] virtual bool needValidatedLedger(LedgerIndex seq) const = 0; diff --git a/include/xrpl/ledger/ApplyView.h b/include/xrpl/ledger/ApplyView.h index afe6b291b2..724d89b7c6 100644 --- a/include/xrpl/ledger/ApplyView.h +++ b/include/xrpl/ledger/ApplyView.h @@ -92,47 +92,50 @@ operator&=(ApplyFlags& lhs, ApplyFlags const& rhs) //------------------------------------------------------------------------------ -/** Writeable view to a ledger, for applying a transaction. - - This refinement of ReadView provides an interface where - the SLE can be "checked out" for modifications and put - back in an updated or removed state. Also added is an - interface to provide contextual information necessary - to calculate the results of transaction processing, - including the metadata if the view is later applied to - the parent (using an interface in the derived class). - The context info also includes values from the base - ledger such as sequence number and the network time. - - This allows implementations to journal changes made to - the state items in a ledger, with the option to apply - those changes to the base or discard the changes without - affecting the base. - - Typical usage is to call read() for non-mutating - operations. - - For mutating operations the sequence is as follows: - - // Add a new value - v.insert(sle); - - // Check out a value for modification - sle = v.peek(k); - - // Indicate that changes were made - v.update(sle) - - // Or, erase the value - v.erase(sle) - - The invariant is that insert, update, and erase may not - be called with any SLE which belongs to different view. -*/ +/** + * Writeable view to a ledger, for applying a transaction. + * + * This refinement of ReadView provides an interface where + * the SLE can be "checked out" for modifications and put + * back in an updated or removed state. Also added is an + * interface to provide contextual information necessary + * to calculate the results of transaction processing, + * including the metadata if the view is later applied to + * the parent (using an interface in the derived class). + * The context info also includes values from the base + * ledger such as sequence number and the network time. + * + * This allows implementations to journal changes made to + * the state items in a ledger, with the option to apply + * those changes to the base or discard the changes without + * affecting the base. + * + * Typical usage is to call read() for non-mutating + * operations. + * + * For mutating operations the sequence is as follows: + * + * // Add a new value + * v.insert(sle); + * + * // Check out a value for modification + * sle = v.peek(k); + * + * // Indicate that changes were made + * v.update(sle) + * + * // Or, erase the value + * v.erase(sle) + * + * The invariant is that insert, update, and erase may not + * be called with any SLE which belongs to different view. + */ class ApplyView : public ReadView { private: - /** Add an entry to a directory using the specified insert strategy */ + /** + * Add an entry to a directory using the specified insert strategy + */ std::optional dirAdd( bool preserveOrder, @@ -143,84 +146,89 @@ private: public: ApplyView() = default; - /** Returns the tx apply flags. - - Flags can affect the outcome of transaction - processing. For example, transactions applied - to an open ledger generate "local" failures, - while transactions applied to the consensus - ledger produce hard failures (and claim a fee). - */ + /** + * Returns the tx apply flags. + * + * Flags can affect the outcome of transaction + * processing. For example, transactions applied + * to an open ledger generate "local" failures, + * while transactions applied to the consensus + * ledger produce hard failures (and claim a fee). + */ [[nodiscard]] virtual ApplyFlags flags() const = 0; - /** Prepare to modify the SLE associated with key. - - Effects: - - Gives the caller ownership of a modifiable - SLE associated with the specified key. - - The returned SLE may be used in a subsequent - call to erase or update. - - The SLE must not be passed to any other ApplyView. - - @return `nullptr` if the key is not present - */ + /** + * Prepare to modify the SLE associated with key. + * + * Effects: + * + * Gives the caller ownership of a modifiable + * SLE associated with the specified key. + * + * The returned SLE may be used in a subsequent + * call to erase or update. + * + * The SLE must not be passed to any other ApplyView. + * + * @return `nullptr` if the key is not present + */ virtual SLE::pointer peek(Keylet const& k) = 0; - /** Remove a peeked SLE. - - Requirements: - - `sle` was obtained from prior call to peek() - on this instance of the RawView. - - Effects: - - The key is no longer associated with the SLE. - */ + /** + * Remove a peeked SLE. + * + * Requirements: + * + * `sle` was obtained from prior call to peek() + * on this instance of the RawView. + * + * Effects: + * + * The key is no longer associated with the SLE. + */ virtual void erase(SLE::ref sle) = 0; - /** Insert a new state SLE - - Requirements: - - `sle` was not obtained from any calls to - peek() on any instances of RawView. - - The SLE's key must not already exist. - - Effects: - - The key in the state map is associated - with the SLE. - - The RawView acquires ownership of the shared_ptr. - - @note The key is taken from the SLE - */ + /** + * Insert a new state SLE + * + * Requirements: + * + * `sle` was not obtained from any calls to + * peek() on any instances of RawView. + * + * The SLE's key must not already exist. + * + * Effects: + * + * The key in the state map is associated + * with the SLE. + * + * The RawView acquires ownership of the shared_ptr. + * + * @note The key is taken from the SLE + */ virtual void insert(SLE::ref sle) = 0; - /** Indicate changes to a peeked SLE - - Requirements: - - The SLE's key must exist. - - `sle` was obtained from prior call to peek() - on this instance of the RawView. - - Effects: - - The SLE is updated - - @note The key is taken from the SLE - */ + /** + * Indicate changes to a peeked SLE + * + * Requirements: + * + * The SLE's key must exist. + * + * `sle` was obtained from prior call to peek() + * on this instance of the RawView. + * + * Effects: + * + * The SLE is updated + * + * @note The key is taken from the SLE + */ /** @{ */ virtual void update(SLE::ref sle) = 0; @@ -250,7 +258,8 @@ public: XRPL_ASSERT(amount.holds(), "creditHookMPT: amount is for MPTIssue"); } - /** Facilitate tracking of MPT sold by an issuer owning MPT sell offer. + /** + * Facilitate tracking of MPT sold by an issuer owning MPT sell offer. * Unlike IOU, MPT doesn't have bi-directional relationship with an issuer, * where a trustline limits an amount that can be issued to a holder. * Consequently, the credit step (last MPTEndpointStep or @@ -294,23 +303,24 @@ public: { } - /** Append an entry to a directory - - Entries in the directory will be stored in order of insertion, i.e. new - entries will always be added at the tail end of the last page. - - @param directory the base of the directory - @param key the entry to insert - @param describe callback to add required entries to a new page - - @return a \c std::optional which, if insertion was successful, - will contain the page number in which the item was stored. - - @note this function may create a page (including a root page), if no - page with space is available. This function will only fail if the - page counter exceeds the protocol-defined maximum number of - allowable pages. - */ + /** + * Append an entry to a directory + * + * Entries in the directory will be stored in order of insertion, i.e. new + * entries will always be added at the tail end of the last page. + * + * @param directory the base of the directory + * @param key the entry to insert + * @param describe callback to add required entries to a new page + * + * @return a @c std::optional which, if insertion was successful, + * will contain the page number in which the item was stored. + * + * @note this function may create a page (including a root page), if no + * page with space is available. This function will only fail if the + * page counter exceeds the protocol-defined maximum number of + * allowable pages. + */ /** @{ */ std::optional dirAppend( @@ -333,23 +343,24 @@ public: } /** @} */ - /** Insert an entry to a directory - - Entries in the directory will be stored in a semi-random order, but - each page will be maintained in sorted order. - - @param directory the base of the directory - @param key the entry to insert - @param describe callback to add required entries to a new page - - @return a \c std::optional which, if insertion was successful, - will contain the page number in which the item was stored. - - @note this function may create a page (including a root page), if no - page with space is available.this function will only fail if the - page counter exceeds the protocol-defined maximum number of - allowable pages. - */ + /** + * Insert an entry to a directory + * + * Entries in the directory will be stored in a semi-random order, but + * each page will be maintained in sorted order. + * + * @param directory the base of the directory + * @param key the entry to insert + * @param describe callback to add required entries to a new page + * + * @return a @c std::optional which, if insertion was successful, + * will contain the page number in which the item was stored. + * + * @note this function may create a page (including a root page), if no + * page with space is available.this function will only fail if the + * page counter exceeds the protocol-defined maximum number of + * allowable pages. + */ /** @{ */ std::optional dirInsert( @@ -370,21 +381,22 @@ public: } /** @} */ - /** Remove an entry from a directory - - @param directory the base of the directory - @param page the page number for this page - @param key the entry to remove - @param keepRoot if deleting the last entry, don't - delete the root page (i.e. the directory itself). - - @return \c true if the entry was found and deleted and - \c false otherwise. - - @note This function will remove zero or more pages from the directory; - the root page will not be deleted even if it is empty, unless - \p keepRoot is not set and the directory is empty. - */ + /** + * Remove an entry from a directory + * + * @param directory the base of the directory + * @param page the page number for this page + * @param key the entry to remove + * @param keepRoot if deleting the last entry, don't + * delete the root page (i.e. the directory itself). + * + * @return @c true if the entry was found and deleted and + * @c false otherwise. + * + * @note This function will remove zero or more pages from the directory; + * the root page will not be deleted even if it is empty, unless + * \p keepRoot is not set and the directory is empty. + */ /** @{ */ bool dirRemove(Keylet const& directory, std::uint64_t page, uint256 const& key, bool keepRoot); @@ -396,34 +408,38 @@ public: } /** @} */ - /** Remove the specified directory, invoking the callback for every node. */ + /** + * Remove the specified directory, invoking the callback for every node. + */ bool dirDelete(Keylet const& directory, std::function const&); - /** Remove the specified directory, if it is empty. - - @param directory the identifier of the directory node to be deleted - @return \c true if the directory was found and was successfully deleted - \c false otherwise. - - @note The function should only be called with the root entry (i.e. with - the first page) of a directory. - */ + /** + * Remove the specified directory, if it is empty. + * + * @param directory the identifier of the directory node to be deleted + * @return @c true if the directory was found and was successfully deleted + * @c false otherwise. + * + * @note The function should only be called with the root entry (i.e. with + * the first page) of a directory. + */ bool emptyDirDelete(Keylet const& directory); }; -/** Bundles the mutable ledger view and the transaction being applied. - - Passed together to avoid threading two separate parameters through every - helper that needs both the view (for state reads/writes) and the - transaction (for field inspection and metadata). - - Both members are non-owning references; the caller is responsible for - ensuring that the referenced objects outlive the ApplyViewContext. - - TODO: replace with ApplyContext after it's untangled with xrpl/tx -*/ +/** + * Bundles the mutable ledger view and the transaction being applied. + * + * Passed together to avoid threading two separate parameters through every + * helper that needs both the view (for state reads/writes) and the + * transaction (for field inspection and metadata). + * + * Both members are non-owning references; the caller is responsible for + * ensuring that the referenced objects outlive the ApplyViewContext. + * + * TODO: replace with ApplyContext after it's untangled with xrpl/tx + */ struct ApplyViewContext { ApplyView& view; @@ -431,11 +447,12 @@ struct ApplyViewContext }; namespace directory { -/** Helper functions for managing low-level directory operations. - These are not part of the ApplyView interface. - - Don't use them unless you really, really know what you're doing. - Instead use dirAdd, dirInsert, etc. +/** + * Helper functions for managing low-level directory operations. + * These are not part of the ApplyView interface. + * + * Don't use them unless you really, really know what you're doing. + * Instead use dirAdd, dirInsert, etc. */ std::uint64_t diff --git a/include/xrpl/ledger/ApplyViewImpl.h b/include/xrpl/ledger/ApplyViewImpl.h index 9a3734a8ca..630153f90a 100644 --- a/include/xrpl/ledger/ApplyViewImpl.h +++ b/include/xrpl/ledger/ApplyViewImpl.h @@ -18,12 +18,13 @@ namespace xrpl { -/** Editable, discardable view that can build metadata for one tx. - - Iteration of the tx map is delegated to the base. - - @note Presented as ApplyView to clients. -*/ +/** + * Editable, discardable view that can build metadata for one tx. + * + * Iteration of the tx map is delegated to the base. + * + * @note Presented as ApplyView to clients. + */ class ApplyViewImpl final : public detail::ApplyViewBase { public: @@ -37,12 +38,13 @@ public: ApplyViewImpl(ApplyViewImpl&&) = default; ApplyViewImpl(ReadView const* base, ApplyFlags flags); - /** Apply the transaction. - - After a call to `apply`, the only valid - operation on this object is to call the - destructor. - */ + /** + * Apply the transaction. + * + * After a call to `apply`, the only valid + * operation on this object is to call the + * destructor. + */ std::optional apply( OpenView& to, @@ -52,25 +54,28 @@ public: bool isDryRun, beast::Journal j); - /** Set the amount of currency delivered. - - This value is used when generating metadata - for payments, to set the DeliveredAmount field. - If the amount is not specified, the field is - excluded from the resulting metadata. - */ + /** + * Set the amount of currency delivered. + * + * This value is used when generating metadata + * for payments, to set the DeliveredAmount field. + * If the amount is not specified, the field is + * excluded from the resulting metadata. + */ void deliver(STAmount const& amount) { deliver_ = amount; } - /** Get the number of modified entries + /** + * Get the number of modified entries */ std::size_t size(); - /** Visit modified entries + /** + * Visit modified entries */ void visit( diff --git a/include/xrpl/ledger/CachedView.h b/include/xrpl/ledger/CachedView.h index 1da3a67563..b9e2cf8d66 100644 --- a/include/xrpl/ledger/CachedView.h +++ b/include/xrpl/ledger/CachedView.h @@ -133,10 +133,11 @@ public: } // namespace detail -/** Wraps a DigestAwareReadView to provide caching. - - @tparam Base A subclass of DigestAwareReadView -*/ +/** + * Wraps a DigestAwareReadView to provide caching. + * + * @tparam Base A subclass of DigestAwareReadView + */ template class CachedView : public detail::CachedViewImpl { @@ -158,10 +159,11 @@ public: { } - /** Returns the base type. - - @note This breaks encapsulation and bypasses the cache. - */ + /** + * Returns the base type. + * + * @note This breaks encapsulation and bypasses the cache. + */ std::shared_ptr const& base() const { diff --git a/include/xrpl/ledger/CanonicalTXSet.h b/include/xrpl/ledger/CanonicalTXSet.h index f8349dfab6..11aadf4e92 100644 --- a/include/xrpl/ledger/CanonicalTXSet.h +++ b/include/xrpl/ledger/CanonicalTXSet.h @@ -13,13 +13,13 @@ namespace xrpl { -/** Holds transactions which were deferred to the next pass of consensus. - - "Canonical" refers to the order in which transactions are applied. - - - Puts transactions from the same account in SeqProxy order - -*/ +/** + * Holds transactions which were deferred to the next pass of consensus. + * + * "Canonical" refers to the order in which transactions are applied. + * + * - Puts transactions from the same account in SeqProxy order + */ // VFALCO TODO rename to SortedTxSet class CanonicalTXSet : public CountedObject { diff --git a/include/xrpl/ledger/Dir.h b/include/xrpl/ledger/Dir.h index 05df887d8b..233719cdeb 100644 --- a/include/xrpl/ledger/Dir.h +++ b/include/xrpl/ledger/Dir.h @@ -13,18 +13,19 @@ namespace xrpl { -/** A class that simplifies iterating ledger directory pages - - The Dir class provides a forward iterator for walking through - the uint256 values contained in ledger directories. - - The Dir class also allows accelerated directory walking by - stepping directly from one page to the next using the next_page() - member function. - - As of July 2024, the Dir class is only being used with NFTokenOffer - directories and for unit tests. -*/ +/** + * A class that simplifies iterating ledger directory pages + * + * The Dir class provides a forward iterator for walking through + * the uint256 values contained in ledger directories. + * + * The Dir class also allows accelerated directory walking by + * stepping directly from one page to the next using the next_page() + * member function. + * + * As of July 2024, the Dir class is only being used with NFTokenOffer + * directories and for unit tests. + */ class Dir { private: diff --git a/include/xrpl/ledger/Ledger.h b/include/xrpl/ledger/Ledger.h index 3453389a5e..e1dd2c422e 100644 --- a/include/xrpl/ledger/Ledger.h +++ b/include/xrpl/ledger/Ledger.h @@ -42,32 +42,33 @@ struct CreateGenesisT }; extern CreateGenesisT const kCreateGenesis; -/** Holds a ledger. - - The ledger is composed of two SHAMaps. The state map holds all of the - ledger entries such as account roots and order books. The tx map holds - all of the transactions and associated metadata that made it into that - particular ledger. Most of the operations on a ledger are concerned - with the state map. - - This can hold just the header, a partial set of data, or the entire set - of data. It all depends on what is in the corresponding SHAMap entry. - Various functions are provided to populate or depopulate the caches that - the object holds references to. - - Ledgers are constructed as either mutable or immutable. - - 1) If you are the sole owner of a mutable ledger, you can do whatever you - want with no need for locks. - - 2) If you have an immutable ledger, you cannot ever change it, so no need - for locks. - - 3) Mutable ledgers cannot be shared. - - @note Presented to clients as ReadView - @note Calls virtuals in the constructor, so marked as final -*/ +/** + * Holds a ledger. + * + * The ledger is composed of two SHAMaps. The state map holds all of the + * ledger entries such as account roots and order books. The tx map holds + * all of the transactions and associated metadata that made it into that + * particular ledger. Most of the operations on a ledger are concerned + * with the state map. + * + * This can hold just the header, a partial set of data, or the entire set + * of data. It all depends on what is in the corresponding SHAMap entry. + * Various functions are provided to populate or depopulate the caches that + * the object holds references to. + * + * Ledgers are constructed as either mutable or immutable. + * + * 1) If you are the sole owner of a mutable ledger, you can do whatever you + * want with no need for locks. + * + * 2) If you have an immutable ledger, you cannot ever change it, so no need + * for locks. + * + * 3) Mutable ledgers cannot be shared. + * + * @note Presented to clients as ReadView + * @note Calls virtuals in the constructor, so marked as final + */ class Ledger final : public std::enable_shared_from_this, public DigestAwareReadView, public TxsRawView, @@ -82,20 +83,21 @@ public: Ledger& operator=(Ledger&&) = delete; - /** Create the Genesis ledger. - - The Genesis ledger contains a single account whose - AccountID is generated with a Generator using the seed - computed from the string "masterpassphrase" and ordinal - zero. - - The account has an XRP balance equal to the total amount - of XRP in the system. No more XRP than the amount which - starts in this account can ever exist, with amounts - used to pay fees being destroyed. - - Amendments specified are enabled in the genesis ledger - */ + /** + * Create the Genesis ledger. + * + * The Genesis ledger contains a single account whose + * AccountID is generated with a Generator using the seed + * computed from the string "masterpassphrase" and ordinal + * zero. + * + * The account has an XRP balance equal to the total amount + * of XRP in the system. No more XRP than the amount which + * starts in this account can ever exist, with amounts + * used to pay fees being destroyed. + * + * Amendments specified are enabled in the genesis ledger + */ Ledger( CreateGenesisT, Rules rules, @@ -105,13 +107,14 @@ public: Ledger(LedgerHeader const& info, Rules rules, Family& family); - /** Used for ledgers loaded from JSON files - - @param acquire If true, acquires the ledger if not found locally - - @note The fees parameter provides default values, but setup() may - override them from the ledger state if fee-related SLEs exist. - */ + /** + * Used for ledgers loaded from JSON files + * + * @param acquire If true, acquires the ledger if not found locally + * + * @note The fees parameter provides default values, but setup() may + * override them from the ledger state if fee-related SLEs exist. + */ Ledger( LedgerHeader const& info, bool& loaded, @@ -121,12 +124,13 @@ public: Family& family, beast::Journal j); - /** Create a new ledger following a previous ledger - - The ledger will have the sequence number that - follows previous, and have - parentCloseTime == previous.closeTime. - */ + /** + * Create a new ledger following a previous ledger + * + * The ledger will have the sequence number that + * follows previous, and have + * parentCloseTime == previous.closeTime. + */ Ledger(Ledger const& previous, NetClock::time_point closeTime); // used for database ledgers @@ -369,11 +373,15 @@ public: void updateNegativeUNL(); - /** Returns true if the ledger is a flag ledger */ + /** + * Returns true if the ledger is a flag ledger + */ bool isFlagLedger() const; - /** Returns true if the ledger directly precedes a flag ledger */ + /** + * Returns true if the ledger directly precedes a flag ledger + */ bool isVotingLedger() const; @@ -387,23 +395,25 @@ private: bool setup(); - /** @brief Deserialize a SHAMapItem containing a single STTx. + /** + * @brief Deserialize a SHAMapItem containing a single STTx. * * @param item The SHAMapItem to deserialize. * @return A shared pointer to the deserialized transaction. - * @throw May throw on deserialization error. + * @throws May throw on deserialization error. */ static std::shared_ptr deserializeTx(SHAMapItem const& item); - /** @brief Deserialize a SHAMapItem containing STTx + STObject metadata. + /** + * @brief Deserialize a SHAMapItem containing STTx + STObject metadata. * * The SHAMapItem must contain two variable length serialization objects. * * @param item The SHAMapItem to deserialize. * @return A pair containing shared pointers to the deserialized transaction * and metadata. - * @throw May throw on deserialization error. + * @throws May throw on deserialization error. */ static std::pair, std::shared_ptr> deserializeTxPlusMeta(SHAMapItem const& item); @@ -425,7 +435,9 @@ private: beast::Journal j_; }; -/** A ledger wrapped in a CachedView. */ +/** + * A ledger wrapped in a CachedView. + */ using CachedLedger = CachedView; } // namespace xrpl diff --git a/include/xrpl/ledger/LedgerTiming.h b/include/xrpl/ledger/LedgerTiming.h index a97e229046..77254a434b 100644 --- a/include/xrpl/ledger/LedgerTiming.h +++ b/include/xrpl/ledger/LedgerTiming.h @@ -8,11 +8,12 @@ namespace xrpl { -/** Possible ledger close time resolutions. - - Values should not be duplicated. - @see getNextLedgerTimeResolution -*/ +/** + * Possible ledger close time resolutions. + * + * Values should not be duplicated. + * @see getNextLedgerTimeResolution + */ constexpr std::chrono::seconds kLedgerPossibleTimeResolutions[] = { std::chrono::seconds{10}, std::chrono::seconds{20}, @@ -21,41 +22,50 @@ constexpr std::chrono::seconds kLedgerPossibleTimeResolutions[] = { std::chrono::seconds{90}, std::chrono::seconds{120}}; -//! Initial resolution of ledger close time. +/** + * Initial resolution of ledger close time. + */ constexpr auto kLedgerDefaultTimeResolution = kLedgerPossibleTimeResolutions[2]; -//! Close time resolution in genesis ledger +/** + * Close time resolution in genesis ledger + */ constexpr auto kLedgerGenesisTimeResolution = kLedgerPossibleTimeResolutions[0]; -//! How often we increase the close time resolution (in numbers of ledgers) +/** + * How often we increase the close time resolution (in numbers of ledgers) + */ constexpr auto kIncreaseLedgerTimeResolutionEvery = 8; -//! How often we decrease the close time resolution (in numbers of ledgers) +/** + * How often we decrease the close time resolution (in numbers of ledgers) + */ constexpr auto kDecreaseLedgerTimeResolutionEvery = 1; -/** Calculates the close time resolution for the specified ledger. - - The XRPL protocol uses binning to represent time intervals using only one - timestamp. This allows servers to derive a common time for the next ledger, - without the need for perfectly synchronized clocks. - The time resolution (i.e. the size of the intervals) is adjusted dynamically - based on what happened in the last ledger, to try to avoid disagreements. - - @param previousResolution the resolution used for the prior ledger - @param previousAgree whether consensus agreed on the close time of the prior - ledger - @param ledgerSeq the sequence number of the new ledger - - @pre previousResolution must be a valid bin - from @ref kLedgerPossibleTimeResolutions - - @tparam Rep Type representing number of ticks in std::chrono::duration - @tparam Period An std::ratio representing tick period in - std::chrono::duration - @tparam Seq Unsigned integer-like type corresponding to the ledger sequence - number. It should be comparable to 0 and support modular - division. Built-in and tagged_integers are supported. -*/ +/** + * Calculates the close time resolution for the specified ledger. + * + * The XRPL protocol uses binning to represent time intervals using only one + * timestamp. This allows servers to derive a common time for the next ledger, + * without the need for perfectly synchronized clocks. + * The time resolution (i.e. the size of the intervals) is adjusted dynamically + * based on what happened in the last ledger, to try to avoid disagreements. + * + * @tparam Rep Type representing number of ticks in std::chrono::duration + * @tparam Period An std::ratio representing tick period in + * std::chrono::duration + * @tparam Seq Unsigned integer-like type corresponding to the ledger sequence + * number. It should be comparable to 0 and support modular + * division. Built-in and tagged_integers are supported. + * + * @param previousResolution the resolution used for the prior ledger + * @param previousAgree whether consensus agreed on the close time of the prior + * ledger + * @param ledgerSeq the sequence number of the new ledger + * + * @pre previousResolution must be a valid bin + * from @ref kLedgerPossibleTimeResolutions + */ template std::chrono::duration getNextLedgerTimeResolution( @@ -98,13 +108,14 @@ getNextLedgerTimeResolution( return previousResolution; } -/** Calculates the close time for a ledger, given a close time resolution. - - @param closeTime The time to be rounded - @param closeResolution The resolution - @return @b closeTime rounded to the nearest multiple of @b closeResolution. - Rounds up if @b closeTime is midway between multiples of @b closeResolution. -*/ +/** + * Calculates the close time for a ledger, given a close time resolution. + * + * @param closeTime The time to be rounded + * @param closeResolution The resolution + * @return @b closeTime rounded to the nearest multiple of @b closeResolution. + * Rounds up if @b closeTime is midway between multiples of @b closeResolution. + */ template std::chrono::time_point roundCloseTime( @@ -119,15 +130,16 @@ roundCloseTime( return closeTime - (closeTime.time_since_epoch() % closeResolution); } -/** Calculate the effective ledger close time - - After adjusting the ledger close time based on the current resolution, also - ensure it is sufficiently separated from the prior close time. - - @param closeTime The raw ledger close time - @param resolution The current close time resolution - @param priorCloseTime The close time of the prior ledger -*/ +/** + * Calculate the effective ledger close time + * + * After adjusting the ledger close time based on the current resolution, also + * ensure it is sufficiently separated from the prior close time. + * + * @param closeTime The raw ledger close time + * @param resolution The current close time resolution + * @param priorCloseTime The close time of the prior ledger + */ template std::chrono::time_point effCloseTime( diff --git a/include/xrpl/ledger/OpenView.h b/include/xrpl/ledger/OpenView.h index 875909715c..3f8e950b02 100644 --- a/include/xrpl/ledger/OpenView.h +++ b/include/xrpl/ledger/OpenView.h @@ -25,21 +25,23 @@ namespace xrpl { -/** Open ledger construction tag. - - Views constructed with this tag will have the - rules of open ledgers applied during transaction - processing. +/** + * Open ledger construction tag. + * + * Views constructed with this tag will have the + * rules of open ledgers applied during transaction + * processing. */ inline constexpr struct OpenLedgerT { explicit constexpr OpenLedgerT() = default; } kOpenLedger{}; -/** Batch view construction tag. - - Views constructed with this tag are part of a stack of views - used during batch transaction application. +/** + * Batch view construction tag. + * + * Views constructed with this tag are part of a stack of views + * used during batch transaction application. */ inline constexpr struct BatchViewT { @@ -48,10 +50,11 @@ inline constexpr struct BatchViewT //------------------------------------------------------------------------------ -/** Writable ledger view that accumulates state and tx changes. - - @note Presented as ReadView to clients. -*/ +/** + * Writable ledger view that accumulates state and tx changes. + * + * @note Presented as ReadView to clients. + */ class OpenView final : public ReadView, public TxsRawView { private: @@ -95,7 +98,9 @@ private: detail::RawStateTable items_; std::shared_ptr hold_; - /// In batch mode, the number of transactions already executed. + /** + * In batch mode, the number of transactions already executed. + */ std::size_t baseTxCount_ = 0; bool open_ = true; @@ -109,40 +114,42 @@ public: OpenView(OpenView&&) = default; - /** Construct a shallow copy. - - Effects: - - Creates a new object with a copy of - the modification state table. - - The objects managed by shared pointers are - not duplicated but shared between instances. - Since the SLEs are immutable, calls on the - RawView interface cannot break invariants. - */ + /** + * Construct a shallow copy. + * + * Effects: + * + * Creates a new object with a copy of + * the modification state table. + * + * The objects managed by shared pointers are + * not duplicated but shared between instances. + * Since the SLEs are immutable, calls on the + * RawView interface cannot break invariants. + */ OpenView(OpenView const&); - /** Construct an open ledger view. - - Effects: - - The sequence number is set to the - sequence number of parent plus one. - - The parentCloseTime is set to the - closeTime of parent. - - If `hold` is not nullptr, retains - ownership of a copy of `hold` until - the MetaView is destroyed. - - Calls to rules() will return the - rules provided on construction. - - The tx list starts empty and will contain - all newly inserted tx. - */ + /** + * Construct an open ledger view. + * + * Effects: + * + * The sequence number is set to the + * sequence number of parent plus one. + * + * The parentCloseTime is set to the + * closeTime of parent. + * + * If `hold` is not nullptr, retains + * ownership of a copy of `hold` until + * the MetaView is destroyed. + * + * Calls to rules() will return the + * rules provided on construction. + * + * The tx list starts empty and will contain + * all newly inserted tx. + */ OpenView( OpenLedgerT, ReadView const* base, @@ -159,35 +166,41 @@ public: baseTxCount_ = base.txCount(); } - /** Construct a new last closed ledger. - - Effects: - - The LedgerHeader is copied from the base. - - The rules are inherited from the base. - - The tx list starts empty and will contain - all newly inserted tx. - */ + /** + * Construct a new last closed ledger. + * + * Effects: + * + * The LedgerHeader is copied from the base. + * + * The rules are inherited from the base. + * + * The tx list starts empty and will contain + * all newly inserted tx. + */ OpenView(ReadView const* base, std::shared_ptr hold = nullptr); - /** Returns true if this reflects an open ledger. */ + /** + * Returns true if this reflects an open ledger. + */ bool open() const override { return open_; } - /** Return the number of tx inserted since creation. - - This is used to set the "apply ordinal" - when calculating transaction metadata. - */ + /** + * Return the number of tx inserted since creation. + * + * This is used to set the "apply ordinal" + * when calculating transaction metadata. + */ std::size_t txCount() const; - /** Apply changes. */ + /** + * Apply changes. + */ void apply(TxsRawView& to) const; diff --git a/include/xrpl/ledger/OrderBookDB.h b/include/xrpl/ledger/OrderBookDB.h index a44183900c..96dc94b1f4 100644 --- a/include/xrpl/ledger/OrderBookDB.h +++ b/include/xrpl/ledger/OrderBookDB.h @@ -14,85 +14,92 @@ namespace xrpl { -/** Tracks order books in the ledger. - - This interface provides access to order book information, including: - - Which order books exist in the ledger - - Querying order books by issue - - Managing order book subscriptions - - The order book database is updated as ledgers are accepted and provides - efficient lookup of order book information for pathfinding and client - subscriptions. -*/ +/** + * Tracks order books in the ledger. + * + * This interface provides access to order book information, including: + * - Which order books exist in the ledger + * - Querying order books by issue + * - Managing order book subscriptions + * + * The order book database is updated as ledgers are accepted and provides + * efficient lookup of order book information for pathfinding and client + * subscriptions. + */ class OrderBookDB { public: virtual ~OrderBookDB() = default; - /** Initialize or update the order book database with a new ledger. - - This method should be called when a new ledger is accepted to update - the order book database with the current state of all order books. - - @param ledger The ledger to scan for order books - */ + /** + * Initialize or update the order book database with a new ledger. + * + * This method should be called when a new ledger is accepted to update + * the order book database with the current state of all order books. + * + * @param ledger The ledger to scan for order books + */ virtual void setup(std::shared_ptr const& ledger) = 0; - /** Add an order book to track. - - @param book The order book to add - */ + /** + * Add an order book to track. + * + * @param book The order book to add + */ virtual void addOrderBook(Book const& book) = 0; - /** Get all order books that want a specific issue. - - Returns a list of all order books where the taker pays the specified - issue. This is useful for pathfinding to find all possible next hops - from a given currency. - - @param asset The asset to search for - @param domain Optional domain restriction for the order book - @return Vector of books that want this issue - */ + /** + * Get all order books that want a specific issue. + * + * Returns a list of all order books where the taker pays the specified + * issue. This is useful for pathfinding to find all possible next hops + * from a given currency. + * + * @param asset The asset to search for + * @param domain Optional domain restriction for the order book + * @return Vector of books that want this issue + */ virtual std::vector getBooksByTakerPays(Asset const& asset, std::optional const& domain = std::nullopt) = 0; - /** Get the count of order books that want a specific issue. - - @param asset The asset to search for - @param domain Optional domain restriction for the order book - @return Number of books that want this issue - */ + /** + * Get the count of order books that want a specific issue. + * + * @param asset The asset to search for + * @param domain Optional domain restriction for the order book + * @return Number of books that want this issue + */ virtual int getBookSize(Asset const& asset, std::optional const& domain = std::nullopt) = 0; - /** Check if an order book to XRP exists for the given issue. - - @param asset The asset to check - @param domain Optional domain restriction for the order book - @return true if a book from this issue to XRP exists - */ + /** + * Check if an order book to XRP exists for the given issue. + * + * @param asset The asset to check + * @param domain Optional domain restriction for the order book + * @return true if a book from this issue to XRP exists + */ virtual bool isBookToXRP(Asset const& asset, std::optional const& domain = std::nullopt) = 0; }; -/** Extract the set of books affected by a transaction. +/** + * Extract the set of books affected by a transaction. * - * Walks the transaction's metadata nodes and collects every order book - * whose offers were created, modified, or deleted. Used by NetworkOPs to - * fan transaction notifications out to book subscribers. + * Walks the transaction's metadata nodes and collects every order book + * whose offers were created, modified, or deleted. Used by NetworkOPs to + * fan transaction notifications out to book subscribers. * - * @param alTx The accepted ledger transaction to inspect. - * @param j Journal used to log per-node parsing failures. Inspecting an - * offer node can throw if a required field is missing; in that - * case the bad node is skipped and a warn-level message is - * emitted via @p j. Other affected books in the same transaction - * are still returned. - * @return The set of books whose offers were created, modified, or - * deleted. May be empty for non-offer transactions. + * @param alTx The accepted ledger transaction to inspect. + * @param j Journal used to log per-node parsing failures. Inspecting an + * offer node can throw if a required field is missing; in that + * case the bad node is skipped and a warn-level message is + * emitted via @p j. Other affected books in the same transaction + * are still returned. + * @return The set of books whose offers were created, modified, or + * deleted. May be empty for non-offer transactions. */ hash_set affectedBooks(AcceptedLedgerTx const& alTx, beast::Journal const& j); diff --git a/include/xrpl/ledger/PaymentSandbox.h b/include/xrpl/ledger/PaymentSandbox.h index 8afe21b397..e725bdd556 100644 --- a/include/xrpl/ledger/PaymentSandbox.h +++ b/include/xrpl/ledger/PaymentSandbox.h @@ -132,18 +132,19 @@ private: //------------------------------------------------------------------------------ -/** A wrapper which makes credits unavailable to balances. - - This is used for payments and pathfinding, so that consuming - liquidity from a path never causes portions of that path or - other paths to gain liquidity. - - The behavior of certain free functions in the ApplyView API - will change via the balanceHook and creditHook overrides - of PaymentSandbox. - - @note Presented as ApplyView to clients -*/ +/** + * A wrapper which makes credits unavailable to balances. + * + * This is used for payments and pathfinding, so that consuming + * liquidity from a path never causes portions of that path or + * other paths to gain liquidity. + * + * The behavior of certain free functions in the ApplyView API + * will change via the balanceHook and creditHook overrides + * of PaymentSandbox. + * + * @note Presented as ApplyView to clients + */ class PaymentSandbox final : public detail::ApplyViewBase { public: @@ -164,16 +165,17 @@ public: { } - /** Construct on top of existing PaymentSandbox. - - The changes are pushed to the parent when - apply() is called. - - @param parent A non-null pointer to the parent. - - @note A pointer is used to prevent confusion - with copy construction. - */ + /** + * Construct on top of existing PaymentSandbox. + * + * The changes are pushed to the parent when + * apply() is called. + * + * @param parent A non-null pointer to the parent. + * + * @note A pointer is used to prevent confusion + * with copy construction. + */ // VFALCO If we are constructing on top of a PaymentSandbox, // or a PaymentSandbox-derived class, we MUST go through // one of these constructors or invariants will be broken. @@ -225,12 +227,13 @@ public: [[nodiscard]] OwnerCounts ownerCountHook(AccountID const& account, OwnerCounts const& count) const override; - /** Apply changes to base view. - - `to` must contain contents identical to the parent - view passed upon construction, else undefined - behavior will result. - */ + /** + * Apply changes to base view. + * + * `to` must contain contents identical to the parent + * view passed upon construction, else undefined + * behavior will result. + */ /** @{ */ void apply(RawView& to); diff --git a/include/xrpl/ledger/PendingSaves.h b/include/xrpl/ledger/PendingSaves.h index a18292df68..723ae1aef1 100644 --- a/include/xrpl/ledger/PendingSaves.h +++ b/include/xrpl/ledger/PendingSaves.h @@ -8,12 +8,13 @@ namespace xrpl { -/** Keeps track of which ledgers haven't been fully saved. - - During the ledger building process this collection will keep - track of those ledgers that are being built but have not yet - been completely written. -*/ +/** + * Keeps track of which ledgers haven't been fully saved. + * + * During the ledger building process this collection will keep + * track of those ledgers that are being built but have not yet + * been completely written. + */ class PendingSaves { private: @@ -22,12 +23,13 @@ private: std::condition_variable await_; public: - /** Start working on a ledger - - This is called prior to updating the SQLite indexes. - - @return 'true' if work should be done - */ + /** + * Start working on a ledger + * + * This is called prior to updating the SQLite indexes. + * + * @return 'true' if work should be done + */ bool startWork(LedgerIndex seq) { @@ -45,12 +47,13 @@ public: return true; } - /** Finish working on a ledger - - This is called after updating the SQLite indexes. - The tracking of the work in progress is removed and - threads awaiting completion are notified. - */ + /** + * Finish working on a ledger + * + * This is called after updating the SQLite indexes. + * The tracking of the work in progress is removed and + * threads awaiting completion are notified. + */ void finishWork(LedgerIndex seq) { @@ -60,7 +63,9 @@ public: await_.notify_all(); } - /** Return `true` if a ledger is in the progress of being saved. */ + /** + * Return `true` if a ledger is in the progress of being saved. + */ bool pending(LedgerIndex seq) { @@ -68,14 +73,15 @@ public: return map_.contains(seq); } - /** Check if a ledger should be dispatched - - Called to determine whether work should be done or - dispatched. If work is already in progress and the - call is synchronous, wait for work to be completed. - - @return 'true' if work should be done or dispatched - */ + /** + * Check if a ledger should be dispatched + * + * Called to determine whether work should be done or + * dispatched. If work is already in progress and the + * call is synchronous, wait for work to be completed. + * + * @return 'true' if work should be done or dispatched + */ bool shouldWork(LedgerIndex seq, bool isSynchronous) { @@ -108,12 +114,13 @@ public: } while (true); } - /** Get a snapshot of the pending saves - - Each entry in the returned map corresponds to a ledger - that is in progress or dispatched. The boolean indicates - whether work is currently in progress. - */ + /** + * Get a snapshot of the pending saves + * + * Each entry in the returned map corresponds to a ledger + * that is in progress or dispatched. The boolean indicates + * whether work is currently in progress. + */ std::map getSnapshot() const { diff --git a/include/xrpl/ledger/RawView.h b/include/xrpl/ledger/RawView.h index ac2674226f..b94a7aab27 100644 --- a/include/xrpl/ledger/RawView.h +++ b/include/xrpl/ledger/RawView.h @@ -9,10 +9,11 @@ namespace xrpl { -/** Interface for ledger entry changes. - - Subclasses allow raw modification of ledger entries. -*/ +/** + * Interface for ledger entry changes. + * + * Subclasses allow raw modification of ledger entries. + */ class RawView { public: @@ -22,66 +23,72 @@ public: RawView& operator=(RawView const&) = delete; - /** Delete an existing state item. - - The SLE is provided so the implementation - can calculate metadata. - */ + /** + * Delete an existing state item. + * + * The SLE is provided so the implementation + * can calculate metadata. + */ virtual void rawErase(SLE::ref sle) = 0; - /** Unconditionally insert a state item. - - Requirements: - The key must not already exist. - - Effects: - - The key is associated with the SLE. - - @note The key is taken from the SLE - */ + /** + * Unconditionally insert a state item. + * + * Requirements: + * The key must not already exist. + * + * Effects: + * + * The key is associated with the SLE. + * + * @note The key is taken from the SLE + */ virtual void rawInsert(SLE::ref sle) = 0; - /** Unconditionally replace a state item. - - Requirements: - - The key must exist. - - Effects: - - The key is associated with the SLE. - - @note The key is taken from the SLE - */ + /** + * Unconditionally replace a state item. + * + * Requirements: + * + * The key must exist. + * + * Effects: + * + * The key is associated with the SLE. + * + * @note The key is taken from the SLE + */ virtual void rawReplace(SLE::ref sle) = 0; - /** Destroy XRP. - - This is used to pay for transaction fees. - */ + /** + * Destroy XRP. + * + * This is used to pay for transaction fees. + */ virtual void rawDestroyXRP(XRPAmount const& fee) = 0; }; //------------------------------------------------------------------------------ -/** Interface for changing ledger entries with transactions. - - Allows raw modification of ledger entries and insertion - of transactions into the transaction map. -*/ +/** + * Interface for changing ledger entries with transactions. + * + * Allows raw modification of ledger entries and insertion + * of transactions into the transaction map. + */ class TxsRawView : public RawView { public: - /** Add a transaction to the tx map. - - Closed ledgers must have metadata, - while open ledgers omit metadata. - */ + /** + * Add a transaction to the tx map. + * + * Closed ledgers must have metadata, + * while open ledgers omit metadata. + */ virtual void rawTxInsert( ReadView::key_type const& key, diff --git a/include/xrpl/ledger/ReadView.h b/include/xrpl/ledger/ReadView.h index da92e0b510..d0010b6030 100644 --- a/include/xrpl/ledger/ReadView.h +++ b/include/xrpl/ledger/ReadView.h @@ -30,12 +30,13 @@ namespace xrpl { //------------------------------------------------------------------------------ -/** A view into a ledger. - - This interface provides read access to state - and transaction items. There is no checkpointing - or calculation of metadata. -*/ +/** + * A view into a ledger. + * + * This interface provides read access to state + * and transaction items. There is no checkpointing + * or calculation of metadata. + */ class ReadView { public: @@ -86,72 +87,87 @@ public: { } - /** Returns information about the ledger. */ + /** + * Returns information about the ledger. + */ [[nodiscard]] virtual LedgerHeader const& header() const = 0; - /** Returns true if this reflects an open ledger. */ + /** + * Returns true if this reflects an open ledger. + */ [[nodiscard]] virtual bool open() const = 0; - /** Returns the close time of the previous ledger. */ + /** + * Returns the close time of the previous ledger. + */ [[nodiscard]] NetClock::time_point parentCloseTime() const { return header().parentCloseTime; } - /** Returns the sequence number of the base ledger. */ + /** + * Returns the sequence number of the base ledger. + */ [[nodiscard]] LedgerIndex seq() const { return header().seq; } - /** Returns the fees for the base ledger. */ + /** + * Returns the fees for the base ledger. + */ [[nodiscard]] virtual Fees const& fees() const = 0; - /** Returns the tx processing rules. */ + /** + * Returns the tx processing rules. + */ [[nodiscard]] virtual Rules const& rules() const = 0; - /** Determine if a state item exists. - - @note This can be more efficient than calling read. - - @return `true` if a SLE is associated with the - specified key. - */ + /** + * Determine if a state item exists. + * + * @note This can be more efficient than calling read. + * + * @return `true` if a SLE is associated with the + * specified key. + */ [[nodiscard]] virtual bool exists(Keylet const& k) const = 0; - /** Return the key of the next state item. - - This returns the key of the first state item - whose key is greater than the specified key. If - no such key is present, std::nullopt is returned. - - If `last` is engaged, returns std::nullopt when - the key returned would be outside the open - interval (key, last). - */ + /** + * Return the key of the next state item. + * + * This returns the key of the first state item + * whose key is greater than the specified key. If + * no such key is present, std::nullopt is returned. + * + * If `last` is engaged, returns std::nullopt when + * the key returned would be outside the open + * interval (key, last). + */ [[nodiscard]] virtual std::optional succ(key_type const& key, std::optional const& last = std::nullopt) const = 0; - /** Return the state item associated with a key. - - Effects: - If the key exists, gives the caller ownership - of the non-modifiable corresponding SLE. - - @note While the returned SLE is `const` from the - perspective of the caller, it can be changed - by other callers through raw operations. - - @return `nullptr` if the key is not present or - if the type does not match. - */ + /** + * Return the state item associated with a key. + * + * Effects: + * If the key exists, gives the caller ownership + * of the non-modifiable corresponding SLE. + * + * @note While the returned SLE is `const` from the + * perspective of the caller, it can be changed + * by other callers through raw operations. + * + * @return `nullptr` if the key is not present or + * if the type does not match. + */ [[nodiscard]] virtual SLE::const_pointer read(Keylet const& k) const = 0; @@ -217,22 +233,24 @@ public: [[nodiscard]] virtual std::unique_ptr txsEnd() const = 0; - /** Returns `true` if a tx exists in the tx map. - - A tx exists in the map if it is part of the - base ledger, or if it is a newly inserted tx. - */ + /** + * Returns `true` if a tx exists in the tx map. + * + * A tx exists in the map if it is part of the + * base ledger, or if it is a newly inserted tx. + */ [[nodiscard]] virtual bool txExists(key_type const& key) const = 0; - /** Read a transaction from the tx map. - - If the view represents an open ledger, - the metadata object will be empty. - - @return A pair of nullptr if the - key is not found in the tx map. - */ + /** + * Read a transaction from the tx map. + * + * If the view represents an open ledger, + * the metadata object will be empty. + * + * @return A pair of nullptr if the + * key is not found in the tx map. + */ [[nodiscard]] virtual tx_type txRead(key_type const& key) const = 0; @@ -240,11 +258,12 @@ public: // Memberspaces // - /** Iterable range of ledger state items. - - @note Visiting each state entry in the ledger can - become quite expensive as the ledger grows. - */ + /** + * Iterable range of ledger state items. + * + * @note Visiting each state entry in the ledger can + * become quite expensive as the ledger grows. + */ SlesType sles; // The range of transactions @@ -253,7 +272,9 @@ public: //------------------------------------------------------------------------------ -/** ReadView that associates keys with digests. */ +/** + * ReadView that associates keys with digests. + */ class DigestAwareReadView : public ReadView { public: @@ -262,10 +283,11 @@ public: DigestAwareReadView() = default; DigestAwareReadView(DigestAwareReadView const&) = default; - /** Return the digest associated with the key. - - @return std::nullopt if the item does not exist. - */ + /** + * Return the digest associated with the key. + * + * @return std::nullopt if the item does not exist. + */ [[nodiscard]] virtual std::optional digest(key_type const& key) const = 0; }; diff --git a/include/xrpl/ledger/Sandbox.h b/include/xrpl/ledger/Sandbox.h index ca8838631f..fd48e339eb 100644 --- a/include/xrpl/ledger/Sandbox.h +++ b/include/xrpl/ledger/Sandbox.h @@ -7,12 +7,13 @@ namespace xrpl { -/** Discardable, editable view to a ledger. - - The sandbox inherits the flags of the base. - - @note Presented as ApplyView to clients. -*/ +/** + * Discardable, editable view to a ledger. + * + * The sandbox inherits the flags of the base. + * + * @note Presented as ApplyView to clients. + */ class Sandbox : public detail::ApplyViewBase { public: diff --git a/include/xrpl/ledger/View.h b/include/xrpl/ledger/View.h index 7fad61d407..768e518008 100644 --- a/include/xrpl/ledger/View.h +++ b/include/xrpl/ledger/View.h @@ -35,26 +35,27 @@ enum class SkipEntry : bool { No = false, Yes }; // //------------------------------------------------------------------------------ -/** Determines whether the given expiration time has passed. - - In the XRP Ledger, expiration times are defined as the number of whole - seconds after the "XRPL epoch" which, for historical reasons, is set - to January 1, 2000 (00:00 UTC). - - This is like the way the Unix epoch works, except the XRPL epoch is - precisely 946,684,800 seconds after the Unix Epoch. - - See https://xrpl.org/basic-data-types.html#specifying-time - - Expiration is defined in terms of the close time of the parent ledger, - because we definitively know the time that it closed (since consensus - agrees on time) but we do not know the closing time of the ledger that - is under construction. - - @param view The ledger whose parent time is used as the clock. - @param exp The optional expiration time we want to check. - - @returns `true` if `exp` is in the past; `false` otherwise. +/** + * Determines whether the given expiration time has passed. + * + * In the XRP Ledger, expiration times are defined as the number of whole + * seconds after the "XRPL epoch" which, for historical reasons, is set + * to January 1, 2000 (00:00 UTC). + * + * This is like the way the Unix epoch works, except the XRPL epoch is + * precisely 946,684,800 seconds after the Unix Epoch. + * + * See https://xrpl.org/basic-data-types.html#specifying-time + * + * Expiration is defined in terms of the close time of the parent ledger, + * because we definitively know the time that it closed (since consensus + * agrees on time) but we do not know the closing time of the ledger that + * is under construction. + * + * @param view The ledger whose parent time is used as the clock. + * @param exp The optional expiration time we want to check. + * + * @return `true` if `exp` is in the past; `false` otherwise. */ [[nodiscard]] bool hasExpired(ReadView const& view, std::optional const& exp); @@ -83,41 +84,44 @@ using majorityAmendments_t = std::map; [[nodiscard]] majorityAmendments_t getMajorityAmendments(ReadView const& view); -/** Return the hash of a ledger by sequence. - The hash is retrieved by looking up the "skip list" - in the passed ledger. As the skip list is limited - in size, if the requested ledger sequence number is - out of the range of ledgers represented in the skip - list, then std::nullopt is returned. - @return The hash of the ledger with the - given sequence number or std::nullopt. -*/ +/** + * Return the hash of a ledger by sequence. + * The hash is retrieved by looking up the "skip list" + * in the passed ledger. As the skip list is limited + * in size, if the requested ledger sequence number is + * out of the range of ledgers represented in the skip + * list, then std::nullopt is returned. + * @return The hash of the ledger with the + * given sequence number or std::nullopt. + */ [[nodiscard]] std::optional hashOfSeq(ReadView const& ledger, LedgerIndex seq, beast::Journal journal); -/** Find a ledger index from which we could easily get the requested ledger - - The index that we return should meet two requirements: - 1) It must be the index of a ledger that has the hash of the ledger - we are looking for. This means that its sequence must be equal to - greater than the sequence that we want but not more than 256 greater - since each ledger contains the hashes of the 256 previous ledgers. - - 2) Its hash must be easy for us to find. This means it must be 0 mod 256 - because every such ledger is permanently enshrined in a LedgerHashes - page which we can easily retrieve via the skip list. -*/ +/** + * Find a ledger index from which we could easily get the requested ledger + * + * The index that we return should meet two requirements: + * 1) It must be the index of a ledger that has the hash of the ledger + * we are looking for. This means that its sequence must be equal to + * greater than the sequence that we want but not more than 256 greater + * since each ledger contains the hashes of the 256 previous ledgers. + * + * 2) Its hash must be easy for us to find. This means it must be 0 mod 256 + * because every such ledger is permanently enshrined in a LedgerHashes + * page which we can easily retrieve via the skip list. + */ inline LedgerIndex getCandidateLedger(LedgerIndex requested) { return (requested + 255) & (~255); } -/** Return false if the test ledger is provably incompatible - with the valid ledger, that is, they could not possibly - both be valid. Use the first form if you have both ledgers, - use the second form if you have not acquired the valid ledger yet -*/ +/** + * Return false if the test ledger is provably incompatible + * with the valid ledger, that is, they could not possibly + * both be valid. Use the first form if you have both ledgers, + * use the second form if you have not acquired the valid ledger yet + */ [[nodiscard]] bool areCompatible( ReadView const& validLedger, @@ -146,7 +150,8 @@ dirLink( SLE::pointer& object, SF_UINT64 const& node = sfOwnerNode); -/** Checks that can withdraw funds from an object to itself or a destination. +/** + * Checks that can withdraw funds from an object to itself or a destination. * * The receiver may be either the submitting account (sfAccount) or a different * destination account (sfDestination). @@ -169,7 +174,8 @@ canWithdraw( STAmount const& amount, bool hasDestinationTag); -/** Checks that can withdraw funds from an object to itself or a destination. +/** + * Checks that can withdraw funds from an object to itself or a destination. * * The receiver may be either the submitting account (sfAccount) or a different * destination account (sfDestination). @@ -191,7 +197,8 @@ canWithdraw( STAmount const& amount, bool hasDestinationTag); -/** Checks that can withdraw funds from an object to itself or a destination. +/** + * Checks that can withdraw funds from an object to itself or a destination. * * The receiver may be either the submitting account (sfAccount) or a different * destination account (sfDestination). @@ -218,13 +225,15 @@ doWithdraw( STAmount const& amount, beast::Journal j); -/** Deleter function prototype. Returns the status of the entry deletion +/** + * Deleter function prototype. Returns the status of the entry deletion * (if should not be skipped) and if the entry should be skipped. The status * is always tesSUCCESS if the entry should be skipped. */ using EntryDeleter = std::function(LedgerEntryType, uint256 const&, SLE::pointer&)>; -/** Cleanup owner directory entries on account delete. +/** + * Cleanup owner directory entries on account delete. * Used for a regular and AMM accounts deletion. The caller * has to provide the deleter function, which handles details of * specific account-owned object deletion. @@ -239,12 +248,13 @@ cleanupOnAccountDelete( beast::Journal j, std::optional maxNodesToDelete = std::nullopt); -/** Has the specified time passed? - - @param now the current time - @param mark the cutoff point - @return true if \a now refers to a time strictly after \a mark, else false. -*/ +/** + * Has the specified time passed? + * + * @param now the current time + * @param mark the cutoff point + * @return true if \a now refers to a time strictly after \a mark, else false. + */ bool after(NetClock::time_point now, std::uint32_t mark); diff --git a/include/xrpl/ledger/helpers/AMMHelpers.h b/include/xrpl/ledger/helpers/AMMHelpers.h index c6a2053010..7d41bfce81 100644 --- a/include/xrpl/ledger/helpers/AMMHelpers.h +++ b/include/xrpl/ledger/helpers/AMMHelpers.h @@ -53,7 +53,8 @@ enum class IsDeposit : bool { No = false, Yes = true }; inline Number const kAMMInvariantRelativeTolerance{1, -11}; -/** Calculate LP Tokens given AMM pool reserves. +/** + * Calculate LP Tokens given AMM pool reserves. * @param asset1 AMM one side of the pool reserve * @param asset2 AMM another side of the pool reserve * @return LP Tokens as IOU @@ -61,7 +62,8 @@ inline Number const kAMMInvariantRelativeTolerance{1, -11}; STAmount ammLPTokens(STAmount const& asset1, STAmount const& asset2, Asset const& lptIssue); -/** Calculate LP Tokens given asset's deposit amount. +/** + * Calculate LP Tokens given asset's deposit amount. * @param asset1Balance current AMM asset1 balance * @param asset1Deposit requested asset1 deposit amount * @param lptAMMBalance AMM LPT balance @@ -75,10 +77,11 @@ lpTokensOut( STAmount const& lptAMMBalance, std::uint16_t tfee); -/** Calculate asset deposit given LP Tokens. +/** + * Calculate asset deposit given LP Tokens. * @param asset1Balance current AMM asset1 balance - * @param lpTokens LP Tokens * @param lptAMMBalance AMM LPT balance + * @param lpTokens LP Tokens * @param tfee trading fee in basis points * @return */ @@ -89,7 +92,8 @@ ammAssetIn( STAmount const& lpTokens, std::uint16_t tfee); -/** Calculate LP Tokens given asset's withdraw amount. Return 0 +/** + * Calculate LP Tokens given asset's withdraw amount. Return 0 * if can't calculate. * @param asset1Balance current AMM asset1 balance * @param asset1Withdraw requested asset1 withdraw amount @@ -104,7 +108,8 @@ lpTokensIn( STAmount const& lptAMMBalance, std::uint16_t tfee); -/** Calculate asset withdrawal by tokens +/** + * Calculate asset withdrawal by tokens * @param assetBalance balance of the asset being withdrawn * @param lptAMMBalance total AMM Tokens balance * @param lpTokens LP Tokens balance @@ -118,7 +123,8 @@ ammAssetOut( STAmount const& lpTokens, std::uint16_t tfee); -/** Check if the relative distance between the qualities +/** + * Check if the relative distance between the qualities * is within the requested distance. * @param calcQuality calculated quality * @param reqQuality requested quality @@ -137,7 +143,8 @@ withinRelativeDistance(Quality const& calcQuality, Quality const& reqQuality, Nu return ((min.rate() - max.rate()) / min.rate()) < dist; } -/** Check if the relative distance between the amounts +/** + * Check if the relative distance between the amounts * is within the requested distance. * @param calc calculated amount * @param req requested amount @@ -158,13 +165,15 @@ withinRelativeDistance(Amt const& calc, Amt const& req, Number const& dist) return ((max - min) / max) < dist; } -/** Solve quadratic equation to find takerGets or takerPays. Round +/** + * Solve quadratic equation to find takerGets or takerPays. Round * to minimize the amount in order to maximize the quality. */ std::optional solveQuadraticEqSmallest(Number const& a, Number const& b, Number const& c); -/** Generate AMM offer starting with takerGets when AMM pool +/** + * Generate AMM offer starting with takerGets when AMM pool * from the payment perspective is IOU(in)/XRP(out) * Equations: * Spot Price Quality after the offer is consumed: @@ -231,7 +240,8 @@ getAMMOfferStartWithTakerGets( return amounts; } -/** Generate AMM offer starting with takerPays when AMM pool +/** + * Generate AMM offer starting with takerPays when AMM pool * from the payment perspective is XRP(in)/IOU(out) or IOU(in)/IOU(out). * Equations: * Spot Price Quality after the offer is consumed: @@ -298,7 +308,8 @@ getAMMOfferStartWithTakerPays( return amounts; } -/** Generate AMM offer so that either updated Spot Price Quality (SPQ) +/** + * Generate AMM offer so that either updated Spot Price Quality (SPQ) * is equal to LOB quality (in this case AMM offer quality is * better than LOB quality) or AMM offer is equal to LOB quality * (in this case SPQ is better than LOB quality). @@ -415,7 +426,8 @@ changeSpotPriceQuality( return amounts; } -/** AMM pool invariant - the product (A * B) after swap in/out has to remain +/** + * AMM pool invariant - the product (A * B) after swap in/out has to remain * at least the same: (A + in) * (B - out) >= A * B * XRP round-off may result in a smaller product after swap in/out. * To address this: @@ -427,7 +439,8 @@ changeSpotPriceQuality( * value is increased. */ -/** Swap assetIn into the pool and swap out a proportional amount +/** + * Swap assetIn into the pool and swap out a proportional amount * of the other asset. Implements AMM Swap in. * @see [XLS30d:AMM * Swap](https://github.com/XRPLF/XRPL-Standards/discussions/78) @@ -493,7 +506,8 @@ swapAssetIn(TAmounts const& pool, TIn const& assetIn, std::uint16_t t Number::RoundingMode::Downward); } -/** Swap assetOut out of the pool and swap in a proportional amount +/** + * Swap assetOut out of the pool and swap in a proportional amount * of the other asset. Implements AMM Swap out. * @see [XLS30d:AMM * Swap](https://github.com/XRPLF/XRPL-Standards/discussions/78) @@ -559,12 +573,14 @@ swapAssetOut(TAmounts const& pool, TOut const& assetOut, std::uint16_ Number::RoundingMode::Upward); } -/** Return square of n. +/** + * Return square of n. */ Number square(Number const& n); -/** Adjust LP tokens to deposit/withdraw. +/** + * Adjust LP tokens to deposit/withdraw. * Amount type keeps 16 digits. Maintaining the LP balance by adding * deposited tokens or subtracting withdrawn LP tokens from LP balance * results in losing precision in LP balance. I.e. the resulting LP balance @@ -578,7 +594,8 @@ square(Number const& n); STAmount adjustLPTokens(STAmount const& lptAMMBalance, STAmount const& lpTokens, IsDeposit isDeposit); -/** Calls adjustLPTokens() and adjusts deposit or withdraw amounts if +/** + * Calls adjustLPTokens() and adjusts deposit or withdraw amounts if * the adjusted LP tokens are less than the provided LP tokens. * @param amountBalance asset1 pool balance * @param amount asset1 to deposit or withdraw @@ -599,7 +616,8 @@ adjustAmountsByLPTokens( std::uint16_t tfee, IsDeposit isDeposit); -/** Positive solution for quadratic equation: +/** + * Positive solution for quadratic equation: * x = (-b + sqrt(b**2 + 4*a*c))/(2*a) */ Number @@ -630,7 +648,8 @@ getAssetRounding(IsDeposit isDeposit) } // namespace detail -/** Round AMM equal deposit/withdrawal amount. Deposit/withdrawal formulas +/** + * Round AMM equal deposit/withdrawal amount. Deposit/withdrawal formulas * calculate the amount as a fractional value of the pool balance. The rounding * takes place on the last step of multiplying the balance by the fraction if * AMMv1_3 is enabled. @@ -654,7 +673,8 @@ getRoundedAsset(Rules const& rules, STAmount const& balance, A const& frac, IsDe return multiply(balance, frac, rm); } -/** Round AMM single deposit/withdrawal amount. +/** + * Round AMM single deposit/withdrawal amount. * The lambda's are used to delay evaluation until the function * is executed so that the calculation is not done twice. noRoundCb() is * called if AMMv1_3 is disabled. Otherwise, the rounding is set and @@ -671,7 +691,8 @@ getRoundedAsset( std::function const& productCb, IsDeposit isDeposit); -/** Round AMM deposit/withdrawal LPToken amount. Deposit/withdrawal formulas +/** + * Round AMM deposit/withdrawal LPToken amount. Deposit/withdrawal formulas * calculate the lptokens as a fractional value of the AMM total lptokens. * The rounding takes place on the last step of multiplying the balance by * the fraction if AMMv1_3 is enabled. The tokens are then @@ -685,7 +706,8 @@ getRoundedLPTokens( Number const& frac, IsDeposit isDeposit); -/** Round AMM single deposit/withdrawal LPToken amount. +/** + * Round AMM single deposit/withdrawal LPToken amount. * The lambda's are used to delay evaluation until the function is executed * so that the calculations are not done twice. * noRoundCb() is called if AMMv1_3 is disabled. Otherwise, the rounding is set @@ -732,7 +754,8 @@ adjustAssetOutByTokens( STAmount const& tokens, std::uint16_t tfee); -/** Find a fraction of tokens after the tokens are adjusted. The fraction +/** + * Find a fraction of tokens after the tokens are adjusted. The fraction * is used to adjust equal deposit/withdraw amount. */ Number @@ -742,7 +765,8 @@ adjustFracByTokens( STAmount const& tokens, Number const& frac); -/** Get AMM pool balances. +/** + * Get AMM pool balances. */ std::pair ammPoolHolds( @@ -754,7 +778,8 @@ ammPoolHolds( AuthHandling authHandling, beast::Journal const j); -/** Check AMM pool product invariant after an AMM operation that changes LP tokens +/** + * Check AMM pool product invariant after an AMM operation that changes LP tokens * (deposit/withdraw/clawback) from an already calculated pool product mean. * Returns tecPRECISION_LOSS if poolProductMean < newLPTokenBalance beyond the * invariant tolerance, @@ -763,7 +788,8 @@ ammPoolHolds( TER checkAMMPrecisionLoss(Number const& poolProductMean, STAmount const& newLPTokenBalance); -/** Check AMM pool product invariant after an AMM operation that changes LP tokens +/** + * Check AMM pool product invariant after an AMM operation that changes LP tokens * (deposit/withdraw/clawback). * Returns tecPRECISION_LOSS if sqrt(asset1 * asset2) < newLPTokenBalance beyond * the invariant tolerance, @@ -778,7 +804,8 @@ checkAMMPrecisionLoss( STAmount const& newLPTokenBalance, beast::Journal const j); -/** Get AMM pool and LP token balances. If both optIssue are +/** + * Get AMM pool and LP token balances. If both optIssue are * provided then they are used as the AMM token pair issues. * Otherwise the missing issues are fetched from ammSle. */ @@ -792,7 +819,8 @@ ammHolds( AuthHandling authHandling, beast::Journal const j); -/** Get the balance of LP tokens. +/** + * Get the balance of LP tokens. */ STAmount ammLPHolds( @@ -810,25 +838,29 @@ ammLPHolds( AccountID const& lpAccount, beast::Journal const j); -/** Get AMM trading fee for the given account. The fee is discounted +/** + * Get AMM trading fee for the given account. The fee is discounted * if the account is the auction slot owner or one of the slot's authorized * accounts. */ std::uint16_t getTradingFee(ReadView const& view, SLE const& ammSle, AccountID const& account); -/** Returns total amount held by AMM for the given token. +/** + * Returns total amount held by AMM for the given token. */ STAmount ammAccountHolds(ReadView const& view, AccountID const& ammAccountID, Asset const& asset); -/** Delete trustlines to AMM. If all trustlines are deleted then +/** + * Delete trustlines to AMM. If all trustlines are deleted then * AMM object and account are deleted. Otherwise tecINCOMPLETE is returned. */ TER deleteAMMAccount(Sandbox& view, Asset const& asset, Asset const& asset2, beast::Journal j); -/** Initialize Auction and Voting slots and set the trading/discounted fee. +/** + * Initialize Auction and Voting slots and set the trading/discounted fee. */ void initializeFeeAuctionVote( @@ -838,14 +870,16 @@ initializeFeeAuctionVote( Asset const& lptAsset, std::uint16_t tfee); -/** Return true if the Liquidity Provider is the only AMM provider, false +/** + * Return true if the Liquidity Provider is the only AMM provider, false * otherwise. Return tecINTERNAL if encountered an unexpected condition, * for instance Liquidity Provider has more than one LPToken trustline. */ std::expected isOnlyLiquidityProvider(ReadView const& view, Issue const& ammIssue, AccountID const& lpAccount); -/** Due to rounding, the LPTokenBalance of the last LP might +/** + * Due to rounding, the LPTokenBalance of the last LP might * not match the LP's trustline balance. If it's within the tolerance, * update LPTokenBalance to match the LP's trustline balance. */ diff --git a/include/xrpl/ledger/helpers/AccountRootHelpers.h b/include/xrpl/ledger/helpers/AccountRootHelpers.h index ae8cf53f2f..350fc6ca85 100644 --- a/include/xrpl/ledger/helpers/AccountRootHelpers.h +++ b/include/xrpl/ledger/helpers/AccountRootHelpers.h @@ -20,27 +20,29 @@ namespace xrpl { -/** Check if the issuer has the global freeze flag set. - @param issuer The account to check - @return true if the account has global freeze set -*/ +/** + * Check if the issuer has the global freeze flag set. + * @param issuer The account to check + * @return true if the account has global freeze set + */ [[nodiscard]] bool isGlobalFrozen(ReadView const& view, AccountID const& issuer); -/** Calculate liquid XRP balance for an account. +/** + * Calculate liquid XRP balance for an account. * - * This function may be used to calculate the amount of XRP that - * the holder is able to freely spend. It subtracts reserve requirements. + * This function may be used to calculate the amount of XRP that + * the holder is able to freely spend. It subtracts reserve requirements. * - * ownerCountAdj adjusts the owner count in case the caller calculates - * before ledger entries are added or removed. Positive to add, negative - * to subtract. + * ownerCountAdj adjusts the owner count in case the caller calculates + * before ledger entries are added or removed. Positive to add, negative + * to subtract. * - * @param view The ledger view to read from - * @param id The account ID to check - * @param ownerCountAdj Positive to add to count, negative to reduce count - * @param j Journal for logging - * @return The liquid XRP amount available to the account + * @param view The ledger view to read from + * @param id The account ID to check + * @param ownerCountAdj Positive to add to count, negative to reduce count + * @param j Journal for logging + * @return The liquid XRP amount available to the account */ [[nodiscard]] XRPAmount xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, beast::Journal j); @@ -51,32 +53,34 @@ struct Adjustment std::int32_t accountCountDelta = 0; }; -/** Returns the account reserve, in drops. +/** + * Returns the account reserve, in drops. * - * Actual owner count can be adjusted by delta in ownerCountAdj - * Actual reserve count can be adjusted by delta in accountCountAdj - * The reserve is calculated as: - * (ownerCount + "sponsoring object count" - "sponsored object count" + additionalOwnerCount) * - * increment + (1 if not sponsored account + sponsoringAccountCount) * "reserve base" + * Actual owner count can be adjusted by delta in ownerCountAdj + * Actual reserve count can be adjusted by delta in accountCountAdj + * The reserve is calculated as: + * (ownerCount + "sponsoring object count" - "sponsored object count" + additionalOwnerCount) * + * increment + (1 if not sponsored account + sponsoringAccountCount) * "reserve base" * - * @param view The ledger view to read from - * @param sle The ledger entry for the account - * @param j Journal for logging - * @param adj Adjustment to the owner/account count (default: 0/0). Positive to add, negative to + * @param view The ledger view to read from + * @param sle The ledger entry for the account + * @param j Journal for logging + * @param adj Adjustment to the owner/account count (default: 0/0). Positive to add, negative to * subtract. - * @return The account reserve amount in drops + * @return The account reserve amount in drops */ [[nodiscard]] XRPAmount accountReserve(ReadView const& view, SLE::const_ref sle, beast::Journal j, Adjustment adj = {}); -/** Convenience overload that accepts AccountID instead of SLE. +/** + * Convenience overload that accepts AccountID instead of SLE. * - * @param view The ledger view to read from - * @param id The account ID - * @param j Journal for logging - * @param adj Adjustment to the owner/account count (default: 0/0). Positive to add, negative to + * @param view The ledger view to read from + * @param id The account ID + * @param j Journal for logging + * @param adj Adjustment to the owner/account count (default: 0/0). Positive to add, negative to * subtract. - * @return The account reserve amount in drops + * @return The account reserve amount in drops */ [[nodiscard]] inline XRPAmount accountReserve(ReadView const& view, AccountID const& id, beast::Journal j, Adjustment adj = {}) @@ -84,19 +88,20 @@ accountReserve(ReadView const& view, AccountID const& id, beast::Journal j, Adju return accountReserve(view, view.read(keylet::account(id)), j, adj); } -/** Check if an account has sufficient reserve. +/** + * Check if an account has sufficient reserve. * - * @param view The ledger view to read from - * @param tx The transaction being processed - * @param accSle The account's ledger entry - * @param accBalance The account's balance - * @param sponsorSle The sponsor's ledger entry (if applicable) - * @param adj Adjustment to the owner/account count (default: 0/0). Positive to add, negative to + * @param view The ledger view to read from + * @param tx The transaction being processed + * @param accSle The account's ledger entry + * @param accBalance The account's balance + * @param sponsorSle The sponsor's ledger entry (if applicable) + * @param adj Adjustment to the owner/account count (default: 0/0). Positive to add, negative to * subtract. - * @param j Journal for logging (default: null sink) - * @param insufReserveCode The transaction result code to return if the reserve is insufficient - * (default: tecINSUFFICIENT_RESERVE). - * @return Transaction result code + * @param j Journal for logging (default: null sink) + * @param insufReserveCode The transaction result code to return if the reserve is insufficient + * (default: tecINSUFFICIENT_RESERVE). + * @return Transaction result code */ [[nodiscard]] TER checkReserve( @@ -108,21 +113,22 @@ checkReserve( beast::Journal j, TER insufReserveCode = tecINSUFFICIENT_RESERVE); -/** Check if an account has sufficient reserve, deriving the sponsor internally. +/** + * Check if an account has sufficient reserve, deriving the sponsor internally. * - * Equivalent to the overload above, but resolves the sponsor via - * getEffectiveTxReserveSponsor(ctx, accSle) instead of taking it explicitly. Use this - * in the common case where the sponsor is simply the transaction's reserve - * sponsor for accSle. Callers that must force the account's-own-reserve branch - * (passing a null sponsor) or supply a different sponsor should use the - * explicit overload above. + * Equivalent to the overload above, but resolves the sponsor via + * getEffectiveTxReserveSponsor(ctx, accSle) instead of taking it explicitly. Use this + * in the common case where the sponsor is simply the transaction's reserve + * sponsor for accSle. Callers that must force the account's-own-reserve branch + * (passing a null sponsor) or supply a different sponsor should use the + * explicit overload above. * - * @param ctx The apply-view context (view + tx) - * @param accSle The account's ledger entry - * @param accBalance The account's balance - * @param adj Reserve adjustments (owner/account count deltas) - * @param j Journal for logging (default: null sink) - * @return Transaction result code + * @param ctx The apply-view context (view + tx) + * @param accSle The account's ledger entry + * @param accBalance The account's balance + * @param adj Reserve adjustments (owner/account count deltas) + * @param j Journal for logging (default: null sink) + * @return Transaction result code */ [[nodiscard]] TER checkReserve( @@ -132,29 +138,31 @@ checkReserve( Adjustment adj, beast::Journal j = beast::Journal{beast::Journal::getNullSink()}); -/** Return number of the objects which reserve is covered by the account(sle) (so called "owner - * count"). Actual owner count can be adjusted by delta in ownerCountAdj. +/** + * Return number of the objects which reserve is covered by the account(sle) (so called "owner + * count"). Actual owner count can be adjusted by delta in ownerCountAdj. * - * @param sle The account's ledger entry - * @param j Journal for logging - * @param ownerCountAdj Adjustment to the owner count (default: 0) - * @return The adjusted owner count + * @param sle The account's ledger entry + * @param j Journal for logging + * @param ownerCountAdj Adjustment to the owner count (default: 0) + * @return The adjusted owner count */ std::uint32_t ownerCount(SLE::const_ref sle, beast::Journal j, std::int32_t ownerCountAdj = 0); -/** Increase owner-count fields when the caller supplies the sponsor. +/** + * Increase owner-count fields when the caller supplies the sponsor. * - * This helper does not create a ledger object. It updates reserve accounting - * after the caller has created/updated an object. - * If sponsorSle is provided, this also adjusts the account's sponsored count - * and the sponsor's sponsoring count. + * This helper does not create a ledger object. It updates reserve accounting + * after the caller has created/updated an object. + * If sponsorSle is provided, this also adjusts the account's sponsored count + * and the sponsor's sponsoring count. * - * @param view The apply view for making changes - * @param accountSle The account's ledger entry - * @param sponsorSle The sponsor's ledger entry (if applicable) - * @param count Amount to add to the owner count - * @param j Journal for logging + * @param view The apply view for making changes + * @param accountSle The account's ledger entry + * @param sponsorSle The sponsor's ledger entry (if applicable) + * @param count Amount to add to the owner count + * @param j Journal for logging */ void increaseOwnerCount( @@ -164,18 +172,19 @@ increaseOwnerCount( std::uint32_t count, beast::Journal j); -/** Increase owner-count fields, deriving the tx reserve sponsor internally. +/** + * Increase owner-count fields, deriving the tx reserve sponsor internally. * - * Equivalent to the overload above, but resolves the sponsor via - * getEffectiveTxReserveSponsor(ctx, accountSle) instead of taking it explicitly. Use - * this when the sponsor is the transaction's reserve sponsor for accountSle - * (the common create path). Deletion paths, which derive the sponsor from an - * object's sfSponsor field, should keep using the explicit overload. + * Equivalent to the overload above, but resolves the sponsor via + * getEffectiveTxReserveSponsor(ctx, accountSle) instead of taking it explicitly. Use + * this when the sponsor is the transaction's reserve sponsor for accountSle + * (the common create path). Deletion paths, which derive the sponsor from an + * object's sfSponsor field, should keep using the explicit overload. * - * @param ctx The apply-view context (view + tx) - * @param accountSle The account's ledger entry - * @param count Amount to add to the owner count - * @param j Journal for logging + * @param ctx The apply-view context (view + tx) + * @param accountSle The account's ledger entry + * @param count Amount to add to the owner count + * @param j Journal for logging */ void increaseOwnerCount( @@ -184,13 +193,14 @@ increaseOwnerCount( std::uint32_t count, beast::Journal j); -/** Convenience overload that accepts AccountID instead of SLE references. +/** + * Convenience overload that accepts AccountID instead of SLE references. * - * @param view The apply view for making changes - * @param account The account ID - * @param sponsor The optional sponsor account ID - * @param count Amount to add to the owner count - * @param j Journal for logging + * @param view The apply view for making changes + * @param account The account ID + * @param sponsor The optional sponsor account ID + * @param count Amount to add to the owner count + * @param j Journal for logging */ inline void increaseOwnerCount( @@ -208,18 +218,19 @@ increaseOwnerCount( j); } -/** Decrease owner-count fields when the caller supplies the sponsor. +/** + * Decrease owner-count fields when the caller supplies the sponsor. * - * This helper does not delete a ledger object. It updates reserve accounting - * after the caller has removed an owner-counted reserve, or for special - * owner-count changes whose sponsor cannot be derived from an object's - * sfSponsor field. + * This helper does not delete a ledger object. It updates reserve accounting + * after the caller has removed an owner-counted reserve, or for special + * owner-count changes whose sponsor cannot be derived from an object's + * sfSponsor field. * - * @param view The apply view for making changes - * @param accountSle The account's ledger entry - * @param sponsorSle The sponsor's ledger entry (if applicable) - * @param count Amount to remove from the owner count - * @param j Journal for logging + * @param view The apply view for making changes + * @param accountSle The account's ledger entry + * @param sponsorSle The sponsor's ledger entry (if applicable) + * @param count Amount to remove from the owner count + * @param j Journal for logging */ void decreaseOwnerCount( @@ -229,13 +240,14 @@ decreaseOwnerCount( std::uint32_t count, beast::Journal j); -/** Convenience overload that accepts AccountID instead of SLE references. +/** + * Convenience overload that accepts AccountID instead of SLE references. * - * @param view The apply view for making changes - * @param account The account ID - * @param sponsor The optional sponsor account ID - * @param count Amount to remove from the owner count - * @param j Journal for logging + * @param view The apply view for making changes + * @param account The account ID + * @param sponsor The optional sponsor account ID + * @param count Amount to remove from the owner count + * @param j Journal for logging */ inline void decreaseOwnerCount( @@ -253,18 +265,19 @@ decreaseOwnerCount( j); } -/** Decrease owner-count fields for an existing ledger object. +/** + * Decrease owner-count fields for an existing ledger object. * - * This helper derives the reserve sponsor from objectSle's sfSponsor field, - * then updates the same owner-count fields as decreaseOwnerCount. Use this - * when removing an existing object whose reserve sponsor is stored on that - * object. + * This helper derives the reserve sponsor from objectSle's sfSponsor field, + * then updates the same owner-count fields as decreaseOwnerCount. Use this + * when removing an existing object whose reserve sponsor is stored on that + * object. * - * @param view The apply view for making changes - * @param accountSle The account's ledger entry - * @param objectSle The object's ledger entry - * @param count Amount to remove from the owner count - * @param j Journal for logging + * @param view The apply view for making changes + * @param accountSle The account's ledger entry + * @param objectSle The object's ledger entry + * @param count Amount to remove from the owner count + * @param j Journal for logging */ void decreaseOwnerCountForObject( @@ -274,13 +287,14 @@ decreaseOwnerCountForObject( std::uint32_t count, beast::Journal j); -/** Convenience overload that accepts AccountID instead of account SLE reference. +/** + * Convenience overload that accepts AccountID instead of account SLE reference. * - * @param view The apply view for making changes - * @param account The account ID - * @param objectSle The object's ledger entry - * @param count Amount to remove from the owner count - * @param j Journal for logging + * @param view The apply view for making changes + * @param account The account ID + * @param objectSle The object's ledger entry + * @param count Amount to remove from the owner count + * @param j Journal for logging */ inline void decreaseOwnerCountForObject( @@ -294,19 +308,20 @@ decreaseOwnerCountForObject( decreaseOwnerCountForObject(view, accountSle, objectSle, count, j); } -/** Adjust a LoanBroker's owner count. +/** + * Adjust a LoanBroker's owner count. * - * A LoanBroker's sfOwnerCount tracks the number of outstanding loans on - * that broker; it is not a reserve-backed owner count and is distinct - * from the broker's pseudo-account's owner count. Loans can never carry a - * reserve sponsor (LoanSet rejects reserve sponsorship at preflight), so - * this never involves sponsor accounting and never invokes the - * ownerCountHook used for ACCOUNT_ROOT reserve tracking. + * A LoanBroker's sfOwnerCount tracks the number of outstanding loans on + * that broker; it is not a reserve-backed owner count and is distinct + * from the broker's pseudo-account's owner count. Loans can never carry a + * reserve sponsor (LoanSet rejects reserve sponsorship at preflight), so + * this never involves sponsor accounting and never invokes the + * ownerCountHook used for ACCOUNT_ROOT reserve tracking. * - * @param view The apply view for making changes - * @param brokerSle The LoanBroker's ledger entry - * @param delta Amount to add (positive) or remove (negative) from the count - * @param j Journal for logging + * @param view The apply view for making changes + * @param brokerSle The LoanBroker's ledger entry + * @param delta Amount to add (positive) or remove (negative) from the count + * @param j Journal for logging */ void adjustLoanBrokerOwnerCount( @@ -315,7 +330,8 @@ adjustLoanBrokerOwnerCount( std::int32_t delta, beast::Journal j); -/** Returns IOU issuer transfer fee as Rate. Rate specifies +/** + * Returns IOU issuer transfer fee as Rate. Rate specifies * the fee as fractions of 1 billion. For example, 1% transfer rate * is represented as 1,010,000,000. * @param issuer The IOU issuer @@ -323,35 +339,40 @@ adjustLoanBrokerOwnerCount( [[nodiscard]] Rate transferRate(ReadView const& view, AccountID const& issuer); -/** Generate a pseudo-account address from a pseudo owner key. - @param pseudoOwnerKey The key to generate the address from - @return The generated account ID -*/ +/** + * Generate a pseudo-account address from a pseudo owner key. + * @param pseudoOwnerKey The key to generate the address from + * @return The generated account ID + */ AccountID pseudoAccountAddress(ReadView const& view, uint256 const& pseudoOwnerKey); -/** Returns the list of fields that define an ACCOUNT_ROOT as a pseudo-account - if set. - - The list is constructed during initialization and is const after that. - Pseudo-account designator fields MUST be maintained by including the - SField::sMD_PseudoAccount flag in the SField definition. -*/ +/** + * Returns the list of fields that define an ACCOUNT_ROOT as a pseudo-account + * if set. + * + * The list is constructed during initialization and is const after that. + * Pseudo-account designator fields MUST be maintained by including the + * SField::sMD_PseudoAccount flag in the SField definition. + */ [[nodiscard]] std::vector const& getPseudoAccountFields(); -/** Returns true if and only if sleAcct is a pseudo-account or specific - pseudo-accounts in pseudoFieldFilter. - - Returns false if sleAcct is: - - NOT a pseudo-account OR - - NOT a ltACCOUNT_ROOT OR - - null pointer -*/ +/** + * Returns true if and only if sleAcct is a pseudo-account or specific + * pseudo-accounts in pseudoFieldFilter. + * + * Returns false if sleAcct is: + * - NOT a pseudo-account OR + * - NOT a ltACCOUNT_ROOT OR + * - null pointer + */ [[nodiscard]] bool isPseudoAccount(SLE::const_pointer sleAcct, std::set const& pseudoFieldFilter = {}); -/** Convenience overload that reads the account from the view. */ +/** + * Convenience overload that reads the account from the view. + */ [[nodiscard]] inline bool isPseudoAccount( ReadView const& view, @@ -372,11 +393,12 @@ isPseudoAccount( [[nodiscard]] std::expected createPseudoAccount(ApplyView& view, uint256 const& pseudoOwnerKey, SField const& ownerField); -/** Checks the destination and tag. - - - Checks that the SLE is not null. - - If the SLE requires a destination tag, checks that there is a tag. -*/ +/** + * Checks the destination and tag. + * + * - Checks that the SLE is not null. + * - If the SLE requires a destination tag, checks that there is a tag. + */ [[nodiscard]] TER checkDestinationAndTag(SLE::const_ref toSle, bool hasDestinationTag); diff --git a/include/xrpl/ledger/helpers/DirectoryHelpers.h b/include/xrpl/ledger/helpers/DirectoryHelpers.h index a95b9bc95a..25085c4252 100644 --- a/include/xrpl/ledger/helpers/DirectoryHelpers.h +++ b/include/xrpl/ledger/helpers/DirectoryHelpers.h @@ -95,19 +95,20 @@ internalDirFirst( } // namespace detail /** @{ */ -/** Returns the first entry in the directory, advancing the index - - @deprecated These are legacy function that are considered deprecated - and will soon be replaced with an iterator-based model - that is easier to use. You should not use them in new code. - - @param view The view against which to operate - @param root The root (i.e. first page) of the directory to iterate - @param page The current page - @param index The index inside the current page - @param entry The entry at the current index - - @return true if the directory isn't empty; false otherwise +/** + * Returns the first entry in the directory, advancing the index + * + * @deprecated These are legacy function that are considered deprecated + * and will soon be replaced with an iterator-based model + * that is easier to use. You should not use them in new code. + * + * @param view The view against which to operate + * @param root The root (i.e. first page) of the directory to iterate + * @param page The current page + * @param index The index inside the current page + * @param entry The entry at the current index + * + * @return true if the directory isn't empty; false otherwise */ bool cdirFirst( @@ -127,19 +128,20 @@ dirFirst( /** @} */ /** @{ */ -/** Returns the next entry in the directory, advancing the index - - @deprecated These are legacy function that are considered deprecated - and will soon be replaced with an iterator-based model - that is easier to use. You should not use them in new code. - - @param view The view against which to operate - @param root The root (i.e. first page) of the directory to iterate - @param page The current page - @param index The index inside the current page - @param entry The entry at the current index - - @return true if the directory isn't empty; false otherwise +/** + * Returns the next entry in the directory, advancing the index + * + * @deprecated These are legacy function that are considered deprecated + * and will soon be replaced with an iterator-based model + * that is easier to use. You should not use them in new code. + * + * @param view The view against which to operate + * @param root The root (i.e. first page) of the directory to iterate + * @param page The current page + * @param index The index inside the current page + * @param entry The entry at the current index + * + * @return true if the directory isn't empty; false otherwise */ bool cdirNext( @@ -158,16 +160,19 @@ dirNext( uint256& entry); /** @} */ -/** Iterate all items in the given directory. */ +/** + * Iterate all items in the given directory. + */ void forEachItem(ReadView const& view, Keylet const& root, std::function const& f); -/** Iterate all items after an item in the given directory. - @param after The key of the item to start after - @param hint The directory page containing `after` - @param limit The maximum number of items to return - @return `false` if the iteration failed -*/ +/** + * Iterate all items after an item in the given directory. + * @param after The key of the item to start after + * @param hint The directory page containing `after` + * @param limit The maximum number of items to return + * @return `false` if the iteration failed + */ bool forEachItemAfter( ReadView const& view, @@ -177,19 +182,22 @@ forEachItemAfter( unsigned int limit, std::function const& f); -/** Iterate all items in an account's owner directory. */ +/** + * Iterate all items in an account's owner directory. + */ inline void forEachItem(ReadView const& view, AccountID const& id, std::function const& f) { forEachItem(view, keylet::ownerDir(id), f); } -/** Iterate all items after an item in an owner directory. - @param after The key of the item to start after - @param hint The directory page containing `after` - @param limit The maximum number of items to return - @return `false` if the iteration failed -*/ +/** + * Iterate all items after an item in an owner directory. + * @param after The key of the item to start after + * @param hint The directory page containing `after` + * @param limit The maximum number of items to return + * @return `false` if the iteration failed + */ inline bool forEachItemAfter( ReadView const& view, @@ -202,13 +210,16 @@ forEachItemAfter( return forEachItemAfter(view, keylet::ownerDir(id), after, hint, limit, f); } -/** Returns `true` if the directory is empty - @param key The key of the directory -*/ +/** + * Returns `true` if the directory is empty + * @param key The key of the directory + */ [[nodiscard]] bool dirIsEmpty(ReadView const& view, Keylet const& k); -/** Returns a function that sets the owner on a directory SLE */ +/** + * Returns a function that sets the owner on a directory SLE + */ [[nodiscard]] std::function describeOwnerDir(AccountID const& account); diff --git a/include/xrpl/ledger/helpers/LendingHelpers.h b/include/xrpl/ledger/helpers/LendingHelpers.h index 873abae272..e2605e9ab7 100644 --- a/include/xrpl/ledger/helpers/LendingHelpers.h +++ b/include/xrpl/ledger/helpers/LendingHelpers.h @@ -63,7 +63,9 @@ static constexpr std::uint32_t kSecondsInYear = 365 * 24 * 60 * 60; Number loanPeriodicRate(TenthBips32 interestRate, std::uint32_t paymentInterval); -/// Ensure the periodic payment is always rounded consistently +/** + * Ensure the periodic payment is always rounded consistently + */ inline Number roundPeriodicPayment(Asset const& asset, Number const& periodicPayment, std::int32_t scale) { @@ -127,7 +129,8 @@ struct LoanPaymentParts operator==(LoanPaymentParts const& other) const; }; -/** This structure captures the parts of a loan state. +/** + * This structure captures the parts of a loan state. * * Whether the values are theoretical (unrounded) or rounded will depend on how * it was computed. @@ -324,12 +327,14 @@ struct PaymentComponents // - extra: An additional payment beyond the regular schedule (overpayment) PaymentSpecialCase specialCase = PaymentSpecialCase::None; - // Calculates the tracked interest portion of this payment. - // This is derived from the other components as: - // trackedValueDelta - trackedPrincipalDelta - trackedManagementFeeDelta - // - // @return The amount of tracked interest included in this payment that - // will be paid to the vault. + /** + * Calculates the tracked interest portion of this payment. + * This is derived from the other components as: + * trackedValueDelta - trackedPrincipalDelta - trackedManagementFeeDelta + * + * @return The amount of tracked interest included in this payment that + * will be paid to the vault. + */ [[nodiscard]] Number trackedInterestPart() const; }; @@ -401,7 +406,8 @@ struct LoanStateDeltas // The difference in management fee outstanding between two loan states. Number managementFee; - /* Calculates the total change across all components. + /** + * Calculates the total change across all components. * @return The sum of principal, interest, and management fee deltas. */ [[nodiscard]] Number diff --git a/include/xrpl/ledger/helpers/MPTokenHelpers.h b/include/xrpl/ledger/helpers/MPTokenHelpers.h index 8a2a4a5b84..5418e5b26a 100644 --- a/include/xrpl/ledger/helpers/MPTokenHelpers.h +++ b/include/xrpl/ledger/helpers/MPTokenHelpers.h @@ -29,15 +29,17 @@ namespace xrpl { [[nodiscard]] bool isGlobalFrozen(ReadView const& view, MPTIssue const& mptIssue); -/** Returns true if @p account's MPToken for @p mptIssue carries the - * individual-lock flag (lsfMPTLocked). +/** + * Returns true if @p account's MPToken for @p mptIssue carries the + * individual-lock flag (lsfMPTLocked). * - * @warning This checks only the raw per-holder lock bit. It does **not** - * perform the transitive vault pseudo-account check: if @p mptIssue is a - * vault share whose underlying asset is frozen, this function returns false. - * Call @ref isFrozen instead when determining whether an account may send or - * receive tokens — it combines isIndividualFrozen, isGlobalFrozen, and - * isVaultPseudoAccountFrozen into a single complete check. */ + * @warning This checks only the raw per-holder lock bit. It does **not** + * perform the transitive vault pseudo-account check: if @p mptIssue is a + * vault share whose underlying asset is frozen, this function returns false. + * Call @ref isFrozen instead when determining whether an account may send or + * receive tokens — it combines isIndividualFrozen, isGlobalFrozen, and + * isVaultPseudoAccountFrozen into a single complete check. + */ [[nodiscard]] bool isIndividualFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue); @@ -61,7 +63,8 @@ isAnyFrozen( // //------------------------------------------------------------------------------ -/** Returns MPT transfer fee as Rate. Rate specifies +/** + * Returns MPT transfer fee as Rate. Rate specifies * the fee as fractions of 1 billion. For example, 1% transfer rate * is represented as 1,010,000,000. * @param issuanceID MPTokenIssuanceID of MPTTokenIssuance object @@ -94,7 +97,8 @@ authorizeMPToken( std::uint32_t flags = 0, std::optional holderID = std::nullopt); -/** Check if the account lacks required authorization for MPT. +/** + * Check if the account lacks required authorization for MPT. * * requireAuth check is recursive for MPT shares in a vault, descending to * assets in the vault, up to maxAssetCheckDepth recursion depth. This is @@ -109,7 +113,8 @@ requireAuth( AuthType authType = AuthType::Legacy, std::uint8_t depth = 0); -/** Enforce account has MPToken to match its authorization. +/** + * Enforce account has MPToken to match its authorization. * * Called from doApply - it will check for expired (and delete if found any) * credentials matching DomainID set in MPTokenIssuance. Must be called if @@ -123,44 +128,46 @@ enforceMPTokenAuthorization( XRPAmount const& priorBalance, beast::Journal j); -/** Resolve the underlying asset of a vault share. +/** + * Resolve the underlying asset of a vault share. * - * Reads sfReferenceHolding from @p sleShareIssuance to determine which - * asset the vault wraps. @p sleHolding must be the SLE that - * sfReferenceHolding points to — either an ltMPTOKEN (returns its - * MPTIssue) or an ltRIPPLE_STATE (returns its low/high Issue). + * Reads sfReferenceHolding from @p sleShareIssuance to determine which + * asset the vault wraps. @p sleHolding must be the SLE that + * sfReferenceHolding points to — either an ltMPTOKEN (returns its + * MPTIssue) or an ltRIPPLE_STATE (returns its low/high Issue). * - * @pre Both SLEs must exist and @p sleHolding must be of type ltMPTOKEN - * or ltRIPPLE_STATE. Passing any other type is undefined behaviour. - * @param sleShareIssuance MPTokenIssuance SLE for the vault share token. - * @param sleHolding SLE referenced by sfReferenceHolding. - * @return The underlying Asset (MPTIssue or Issue). + * @pre Both SLEs must exist and @p sleHolding must be of type ltMPTOKEN + * or ltRIPPLE_STATE. Passing any other type is undefined behaviour. + * @param sleShareIssuance MPTokenIssuance SLE for the vault share token. + * @param sleHolding SLE referenced by sfReferenceHolding. + * @return The underlying Asset (MPTIssue or Issue). */ [[nodiscard]] Asset assetOfHolding(SLE const& sleShareIssuance, SLE const& sleHolding); -/** Check whether @p to may receive the given MPT from @p from. +/** + * Check whether @p to may receive the given MPT from @p from. * - * The check passes when any of the following is true: - * - @p waive is WaiveMPTCanTransfer::Yes (recovery-path exemption), or - * - @p from or @p to is the issuer, or - * - lsfMPTCanTransfer is set on the MPTokenIssuance. + * The check passes when any of the following is true: + * - @p waive is WaiveMPTCanTransfer::Yes (recovery-path exemption), or + * - @p from or @p to is the issuer, or + * - lsfMPTCanTransfer is set on the MPTokenIssuance. * - * For vault shares (MPTokenIssuances that carry sfReferenceHolding) the - * check recurses into the underlying asset's transferability. This - * recursion is defensive; vault-of-vault-shares is rejected at vault - * creation, so in practice depth never exceeds 1. + * For vault shares (MPTokenIssuances that carry sfReferenceHolding) the + * check recurses into the underlying asset's transferability. This + * recursion is defensive; vault-of-vault-shares is rejected at vault + * creation, so in practice depth never exceeds 1. * - * @param view Ledger state to read from. - * @param mptIssue The MPT issuance being transferred. - * @param from Sending account. - * @param to Receiving account. - * @param waive WaiveMPTCanTransfer::Yes skips the lsfMPTCanTransfer - * check. Use for recovery paths (e.g. unwinding SAV or - * Lending Protocol positions after an issuer revokes - * transferability). - * @param depth Recursion depth; bounded at kMaxAssetCheckDepth. - * @return tesSUCCESS if the transfer is allowed, tecNO_AUTH otherwise. + * @param view Ledger state to read from. + * @param mptIssue The MPT issuance being transferred. + * @param from Sending account. + * @param to Receiving account. + * @param waive WaiveMPTCanTransfer::Yes skips the lsfMPTCanTransfer + * check. Use for recovery paths (e.g. unwinding SAV or + * Lending Protocol positions after an issuer revokes + * transferability). + * @param depth Recursion depth; bounded at kMaxAssetCheckDepth. + * @return tesSUCCESS if the transfer is allowed, tecNO_AUTH otherwise. */ [[nodiscard]] TER canTransfer( @@ -171,22 +178,24 @@ canTransfer( WaiveMPTCanTransfer waive = WaiveMPTCanTransfer::No, std::uint8_t depth = 0); -/** Check whether @p asset may be traded on the DEX. +/** + * Check whether @p asset may be traded on the DEX. * - * For IOU assets the check delegates to the existing offer/AMM freeze - * logic. For MPT assets it checks lsfMPTCanTrade on the MPTokenIssuance. - * Vault shares recurse into the underlying asset's tradability via - * sfReferenceHolding; depth is bounded at kMaxAssetCheckDepth. + * For IOU assets the check delegates to the existing offer/AMM freeze + * logic. For MPT assets it checks lsfMPTCanTrade on the MPTokenIssuance. + * Vault shares recurse into the underlying asset's tradability via + * sfReferenceHolding; depth is bounded at kMaxAssetCheckDepth. * - * @param view Ledger state to read from. - * @param asset The asset to check. - * @param depth Recursion depth; bounded at kMaxAssetCheckDepth. - * @return tesSUCCESS if trading is allowed, tecNO_PERMISSION otherwise. + * @param view Ledger state to read from. + * @param asset The asset to check. + * @param depth Recursion depth; bounded at kMaxAssetCheckDepth. + * @return tesSUCCESS if trading is allowed, tecNO_PERMISSION otherwise. */ [[nodiscard]] TER canTrade(ReadView const& view, Asset const& asset, std::uint8_t depth = 0); -/** Convenience to combine canTrade/Transfer. Returns tesSUCCESS if Asset is Issue. +/** + * Convenience to combine canTrade/Transfer. Returns tesSUCCESS if Asset is Issue. */ [[nodiscard]] TER canMPTTradeAndTransfer( @@ -272,7 +281,8 @@ availableMPTAmount(SLE const& sleIssuance); std::int64_t availableMPTAmount(ReadView const& view, MPTID const& mptID); -/** Checks for two types of OutstandingAmount overflow during a send operation. +/** + * Checks for two types of OutstandingAmount overflow during a send operation. * 1. **Direct directSendNoFee (Overflow: No):** A true overflow check when * `OutstandingAmount > MaximumAmount`. This threshold is used for direct * directSendNoFee transactions that bypass the payment engine. @@ -297,7 +307,8 @@ isMPTOverflow( [[nodiscard]] STAmount issuerFundsToSelfIssue(ReadView const& view, MPTIssue const& issue); -/** Facilitate tracking of MPT sold by an issuer owning MPT sell offer. +/** + * Facilitate tracking of MPT sold by an issuer owning MPT sell offer. * See ApplyView::issuerSelfDebitHookMPT(). */ void diff --git a/include/xrpl/ledger/helpers/NFTokenHelpers.h b/include/xrpl/ledger/helpers/NFTokenHelpers.h index 1c4d395fbe..d9d195c559 100644 --- a/include/xrpl/ledger/helpers/NFTokenHelpers.h +++ b/include/xrpl/ledger/helpers/NFTokenHelpers.h @@ -24,19 +24,25 @@ namespace xrpl::nft { -/** Delete up to a specified number of offers from the specified token offer - * directory. */ +/** + * Delete up to a specified number of offers from the specified token offer + * directory. + */ std::size_t removeTokenOffersWithLimit( ApplyView& view, Keylet const& directory, std::size_t maxDeletableOffers); -/** Finds the specified token in the owner's token directory. */ +/** + * Finds the specified token in the owner's token directory. + */ std::optional findToken(ReadView const& view, AccountID const& owner, uint256 const& nftokenID); -/** Finds the token in the owner's token directory. Returns token and page. */ +/** + * Finds the token in the owner's token directory. Returns token and page. + */ struct TokenAndPage { STObject token; @@ -49,33 +55,39 @@ struct TokenAndPage std::optional findTokenAndPage(ApplyView& view, AccountID const& owner, uint256 const& nftokenID); -/** Insert the token in the owner's token directory. */ +/** + * Insert the token in the owner's token directory. + */ TER insertToken(ApplyView& view, AccountID owner, STObject&& nft); -/** Remove the token from the owner's token directory. */ +/** + * Remove the token from the owner's token directory. + */ TER removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID); TER removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID, SLE::ref page); -/** Deletes the given token offer. - - An offer is tracked in two separate places: - - The token's 'buy' directory, if it's a buy offer; or - - The token's 'sell' directory, if it's a sell offer; and - - The owner directory of the account that placed the offer. - - The offer also consumes one incremental reserve. +/** + * Deletes the given token offer. + * + * An offer is tracked in two separate places: + * - The token's 'buy' directory, if it's a buy offer; or + * - The token's 'sell' directory, if it's a sell offer; and + * - The owner directory of the account that placed the offer. + * + * The offer also consumes one incremental reserve. */ bool deleteTokenOffer(ApplyView& view, SLE::ref offer); -/** Repairs the links in an NFTokenPage directory. - - Returns true if a repair took place, otherwise false. -*/ +/** + * Repairs the links in an NFTokenPage directory. + * + * Returns true if a repair took place, otherwise false. + */ bool repairNFTokenDirectoryLinks(ApplyView& view, AccountID const& owner); @@ -89,7 +101,9 @@ changeTokenURI( uint256 const& nftokenID, std::optional const& uri); -/** Preflight checks shared by NFTokenCreateOffer and NFTokenMint */ +/** + * Preflight checks shared by NFTokenCreateOffer and NFTokenMint + */ NotTEC tokenOfferCreatePreflight( AccountID const& acctID, @@ -101,7 +115,9 @@ tokenOfferCreatePreflight( std::optional const& owner = std::nullopt, std::uint32_t txFlags = tfSellNFToken); -/** Preclaim checks shared by NFTokenCreateOffer and NFTokenMint */ +/** + * Preclaim checks shared by NFTokenCreateOffer and NFTokenMint + */ TER tokenOfferCreatePreclaim( ReadView const& view, @@ -115,7 +131,9 @@ tokenOfferCreatePreclaim( std::optional const& owner = std::nullopt, std::uint32_t txFlags = tfSellNFToken); -/** doApply implementation shared by NFTokenCreateOffer and NFTokenMint */ +/** + * doApply implementation shared by NFTokenCreateOffer and NFTokenMint + */ TER tokenOfferCreateApply( ApplyView& view, diff --git a/include/xrpl/ledger/helpers/OfferHelpers.h b/include/xrpl/ledger/helpers/OfferHelpers.h index fc863dff0a..524288ea33 100644 --- a/include/xrpl/ledger/helpers/OfferHelpers.h +++ b/include/xrpl/ledger/helpers/OfferHelpers.h @@ -7,18 +7,19 @@ namespace xrpl { -/** Delete an offer. - - Requirements: - The offer must exist. - The caller must have already checked permissions. - - @param view The ApplyView to modify. - @param sle The offer to delete. - @param j Journal for logging. - - @return tesSUCCESS on success, otherwise an error code. -*/ +/** + * Delete an offer. + * + * Requirements: + * The offer must exist. + * The caller must have already checked permissions. + * + * @param view The ApplyView to modify. + * @param sle The offer to delete. + * @param j Journal for logging. + * + * @return tesSUCCESS on success, otherwise an error code. + */ // [[nodiscard]] // nodiscard commented out so Flow, BookTip and others compile. TER offerDelete(ApplyView& view, SLE::ref sle, beast::Journal j); diff --git a/include/xrpl/ledger/helpers/PaymentChannelHelpers.h b/include/xrpl/ledger/helpers/PaymentChannelHelpers.h index 6e8cd17f7f..5e1f590c58 100644 --- a/include/xrpl/ledger/helpers/PaymentChannelHelpers.h +++ b/include/xrpl/ledger/helpers/PaymentChannelHelpers.h @@ -12,37 +12,40 @@ namespace xrpl { -/** Close a payment channel and return its remaining funds to the channel owner. +/** + * Close a payment channel and return its remaining funds to the channel owner. * - * @param slep The SLE for the PayChannel object to close. - * @param view The apply view in which ledger state modifications are made. - * @param key The ledger key identifying the PayChannel entry. - * @param j Journal used for fatal-level diagnostic messages. - * @return tesSUCCESS on success; tefBAD_LEDGER if a directory removal - * fails; tefINTERNAL if the source account SLE cannot be found. + * @param slep The SLE for the PayChannel object to close. + * @param view The apply view in which ledger state modifications are made. + * @param key The ledger key identifying the PayChannel entry. + * @param j Journal used for fatal-level diagnostic messages. + * @return tesSUCCESS on success; tefBAD_LEDGER if a directory removal + * fails; tefINTERNAL if the source account SLE cannot be found. */ TER closeChannel(SLE::ref slep, ApplyView& view, uint256 const& key, beast::Journal j); -/** Add two uint32_t values with saturation at UINT32_MAX. +/** + * Add two uint32_t values with saturation at UINT32_MAX. * - * @param rules The current ledger rules used to check amendment status. - * @param lhs Left-hand operand. - * @param rhs Right-hand operand. - * @return @p lhs + @p rhs, saturated at UINT32_MAX when the amendment - * is active. + * @param rules The current ledger rules used to check amendment status. + * @param lhs Left-hand operand. + * @param rhs Right-hand operand. + * @return @p lhs + @p rhs, saturated at UINT32_MAX when the amendment + * is active. */ uint32_t saturatingAdd(Rules const& rules, uint32_t const lhs, uint32_t const rhs); -/** Determine whether a payment channel time field represents an expired time. +/** + * Determine whether a payment channel time field represents an expired time. * - * @param view The apply view providing the parent close time and rules. - * @param timeField The optional expiry timestamp (seconds since the XRP - * Ledger epoch). If empty, the function returns false. - * @return @c true if @p timeField is set and the indicated time is - * in the past relative to the view's parent close time; - * @c false otherwise. + * @param view The apply view providing the parent close time and rules. + * @param timeField The optional expiry timestamp (seconds since the XRP + * Ledger epoch). If empty, the function returns false. + * @return @c true if @p timeField is set and the indicated time is + * in the past relative to the view's parent close time; + * @c false otherwise. */ bool isChannelExpired(ApplyView const& view, std::optional timeField); diff --git a/include/xrpl/ledger/helpers/RippleStateHelpers.h b/include/xrpl/ledger/helpers/RippleStateHelpers.h index 398ec86bb7..a0508d074f 100644 --- a/include/xrpl/ledger/helpers/RippleStateHelpers.h +++ b/include/xrpl/ledger/helpers/RippleStateHelpers.h @@ -31,13 +31,14 @@ namespace xrpl { // //------------------------------------------------------------------------------ -/** Calculate the maximum amount of IOUs that an account can hold - @param view the ledger to check against. - @param account the account of interest. - @param issuer the issuer of the IOU. - @param currency the IOU to check. - @return The maximum amount that can be held. -*/ +/** + * Calculate the maximum amount of IOUs that an account can hold + * @param view the ledger to check against. + * @param account the account of interest. + * @param issuer the issuer of the IOU. + * @param currency the IOU to check. + * @return The maximum amount that can be held. + */ /** @{ */ STAmount creditLimit( @@ -50,12 +51,13 @@ IOUAmount creditLimit2(ReadView const& v, AccountID const& acc, AccountID const& iss, Currency const& cur); /** @} */ -/** Returns the amount of IOUs issued by issuer that are held by an account - @param view the ledger to check against. - @param account the account of interest. - @param issuer the issuer of the IOU. - @param currency the IOU to check. -*/ +/** + * Returns the amount of IOUs issued by issuer that are held by an account + * @param view the ledger to check against. + * @param account the account of interest. + * @param issuer the issuer of the IOU. + * @param currency the IOU to check. + */ /** @{ */ STAmount creditBalance( @@ -134,10 +136,11 @@ checkDeepFrozen(ReadView const& view, AccountID const& account, Issue const& iss // //------------------------------------------------------------------------------ -/** Create a trust line - - This can set an initial balance. -*/ +/** + * Create a trust line + * + * This can set an initial balance. + */ [[nodiscard]] TER trustCreate( ApplyView& view, @@ -196,7 +199,8 @@ redeemIOU( // //------------------------------------------------------------------------------ -/** Check if the account lacks required authorization. +/** + * Check if the account lacks required authorization. * * Return tecNO_AUTH or tecNO_LINE if it does * and tesSUCCESS otherwise. @@ -220,7 +224,8 @@ requireAuth( AccountID const& account, AuthType authType = AuthType::Legacy); -/** Check if the destination account is allowed +/** + * Check if the destination account is allowed * to receive IOU. Return terNO_RIPPLE if rippling is * disabled on both sides and tesSUCCESS otherwise. */ @@ -233,8 +238,10 @@ canTransfer(ReadView const& view, Issue const& issue, AccountID const& from, Acc // //------------------------------------------------------------------------------ -/// Any transactors that call addEmptyHolding() in doApply must call -/// canAddHolding() in preflight with the same View and Asset +/** + * Any transactors that call addEmptyHolding() in doApply must call + * canAddHolding() in preflight with the same View and Asset + */ [[nodiscard]] TER addEmptyHolding( ApplyViewContext ctx, @@ -250,7 +257,8 @@ removeEmptyHolding( Issue const& issue, beast::Journal journal); -/** Delete trustline to AMM. The passed `sle` must be obtained from a prior +/** + * Delete trustline to AMM. The passed `sle` must be obtained from a prior * call to view.peek(). Fail if neither side of the trustline is AMM or * if ammAccountID is seated and is not one of the trustline's side. */ @@ -261,7 +269,8 @@ deleteAMMTrustLine( std::optional const& ammAccountID, beast::Journal j); -/** Delete AMMs MPToken. The passed `sle` must be obtained from a prior +/** + * Delete AMMs MPToken. The passed `sle` must be obtained from a prior * call to view.peek(). */ [[nodiscard]] TER diff --git a/include/xrpl/ledger/helpers/SponsorHelpers.h b/include/xrpl/ledger/helpers/SponsorHelpers.h index fd2315b024..98bf419140 100644 --- a/include/xrpl/ledger/helpers/SponsorHelpers.h +++ b/include/xrpl/ledger/helpers/SponsorHelpers.h @@ -16,22 +16,27 @@ namespace xrpl { -/** Whether the given transaction type may use reserve sponsorship (v1). +/** + * Whether the given transaction type may use reserve sponsorship (v1). * - * Reserve sponsorship is restricted to an explicit allow-list of transaction - * types; all others reject spfSponsorReserve at preflight. + * Reserve sponsorship is restricted to an explicit allow-list of transaction + * types; all others reject spfSponsorReserve at preflight. */ bool isReserveSponsorAllowed(TxType txType); -/** Whether the transaction's fee is sponsored (sfSponsor present + spfSponsorFee set). */ +/** + * Whether the transaction's fee is sponsored (sfSponsor present + spfSponsorFee set). + */ inline bool isFeeSponsored(STTx const& tx) { return tx.isFieldPresent(sfSponsor) && ((tx.getFieldU32(sfSponsorFlags) & spfSponsorFee) != 0u); } -/** Whether the transaction's reserve is sponsored (sfSponsor present + spfSponsorReserve set). */ +/** + * Whether the transaction's reserve is sponsored (sfSponsor present + spfSponsorReserve set). + */ inline bool isReserveSponsored(STTx const& tx) { @@ -39,61 +44,68 @@ isReserveSponsored(STTx const& tx) ((tx.getFieldU32(sfSponsorFlags) & spfSponsorReserve) != 0u); } -/** Return the AccountID of the transaction's reserve sponsor, or nullopt if unsponsored. */ +/** + * Return the AccountID of the transaction's reserve sponsor, or nullopt if unsponsored. + */ std::optional getTxReserveSponsorID(STTx const& tx); -/** Return a mutable SLE for the transaction's reserve sponsor account. +/** + * Return a mutable SLE for the transaction's reserve sponsor account. * - * @param ctx The apply-view context (view + tx) - * @return The sponsor account SLE, a null pointer if the tx is not - * reserve-sponsored, or tecINTERNAL if the sponsor account cannot - * be loaded (an already-checked invariant). + * @param ctx The apply-view context (view + tx) + * @return The sponsor account SLE, a null pointer if the tx is not + * reserve-sponsored, or tecINTERNAL if the sponsor account cannot + * be loaded (an already-checked invariant). */ std::expected getTxReserveSponsor(ApplyViewContext ctx); -/** Return a read-only SLE for the transaction's reserve sponsor account. +/** + * Return a read-only SLE for the transaction's reserve sponsor account. * - * @param view The ledger read view - * @param tx The transaction to inspect - * @return The sponsor account SLE, a null pointer if the tx is not - * reserve-sponsored, or tecINTERNAL if the sponsor account cannot - * be loaded (an already-checked invariant). + * @param view The ledger read view + * @param tx The transaction to inspect + * @return The sponsor account SLE, a null pointer if the tx is not + * reserve-sponsored, or tecINTERNAL if the sponsor account cannot + * be loaded (an already-checked invariant). */ std::expected getTxReserveSponsor(ReadView const& view, STTx const& tx); -/** The transaction's reserve sponsor for the given account, if applicable. +/** + * The transaction's reserve sponsor for the given account, if applicable. * - * A reserve sponsor only covers the transaction submitter's own objects, so - * this returns the tx reserve sponsor SLE only when accountSle is the tx's own - * (non-pseudo) account; otherwise it returns a null sponsor pointer. This is - * the single source of truth for the "sponsor applies to tx.Account only" rule - * that the sponsor-deriving helper overloads in AccountRootHelpers rely on. + * A reserve sponsor only covers the transaction submitter's own objects, so + * this returns the tx reserve sponsor SLE only when accountSle is the tx's own + * (non-pseudo) account; otherwise it returns a null sponsor pointer. This is + * the single source of truth for the "sponsor applies to tx.Account only" rule + * that the sponsor-deriving helper overloads in AccountRootHelpers rely on. * - * @param ctx The apply-view context (view + tx) - * @param accountSle The account whose sponsor is being resolved - * @return The sponsor SLE (nullptr if unsponsored), or tecINTERNAL if the - * sponsor account cannot be loaded (an already-checked invariant) + * @param ctx The apply-view context (view + tx) + * @param accountSle The account whose sponsor is being resolved + * @return The sponsor SLE (nullptr if unsponsored), or tecINTERNAL if the + * sponsor account cannot be loaded (an already-checked invariant) */ [[nodiscard]] std::expected getEffectiveTxReserveSponsor(ApplyViewContext ctx, SLE::const_ref accountSle); -/** Return the AccountID stored in the given sponsor field of a ledger entry, or nullopt if absent. +/** + * Return the AccountID stored in the given sponsor field of a ledger entry, or nullopt if absent. */ std::optional getLedgerEntryReserveSponsorID(SLE::const_ref sle, SF_ACCOUNT const& field = sfSponsor); -/** Return a mutable SLE for the reserve sponsor recorded on a ledger entry. +/** + * Return a mutable SLE for the reserve sponsor recorded on a ledger entry. * - * Reads the sponsor AccountID from @p field on @p sle and peeks the - * corresponding account root in @p view. + * Reads the sponsor AccountID from @p field on @p sle and peeks the + * corresponding account root in @p view. * - * @param view The mutable apply view - * @param sle The ledger entry whose sponsor field is inspected - * @param field The field that holds the sponsor AccountID (defaults to sfSponsor) - * @return The sponsor account SLE, or a null pointer if the entry is unsponsored. + * @param view The mutable apply view + * @param sle The ledger entry whose sponsor field is inspected + * @param field The field that holds the sponsor AccountID (defaults to sfSponsor) + * @return The sponsor account SLE, or a null pointer if the entry is unsponsored. */ SLE::pointer getLedgerEntryReserveSponsor( @@ -101,16 +113,17 @@ getLedgerEntryReserveSponsor( SLE::const_ref sle, SF_ACCOUNT const& field = sfSponsor); -/** Stamp a reserve sponsor onto a ledger entry using an explicit sponsor SLE. +/** + * Stamp a reserve sponsor onto a ledger entry using an explicit sponsor SLE. * - * Sets @p field on @p sle to the AccountID from @p sponsorSle. A no-op when - * @p sponsorSle is null (unsponsored). For RippleState entries the field must - * be sfHighSponsor or sfLowSponsor; for all other entry types it must be - * sfSponsor. + * Sets @p field on @p sle to the AccountID from @p sponsorSle. A no-op when + * @p sponsorSle is null (unsponsored). For RippleState entries the field must + * be sfHighSponsor or sfLowSponsor; for all other entry types it must be + * sfSponsor. * - * @param sle The ledger entry to stamp - * @param sponsorSle The sponsor's account root SLE (null → no-op) - * @param field The sponsor field to set (defaults to sfSponsor) + * @param sle The ledger entry to stamp + * @param sponsorSle The sponsor's account root SLE (null → no-op) + * @param field The sponsor field to set (defaults to sfSponsor) */ void addSponsorToLedgerEntry( @@ -118,65 +131,72 @@ addSponsorToLedgerEntry( SLE::const_ref sponsorSle, SF_ACCOUNT const& field = sfSponsor); -/** Stamp the transaction's reserve sponsor onto a newly-created ledger entry. +/** + * Stamp the transaction's reserve sponsor onto a newly-created ledger entry. * - * Equivalent to the overload above, but resolves the sponsor via - * getTxReserveSponsor(ctx) instead of taking it explicitly. A no-op when the - * transaction is not reserve-sponsored. The entry is assumed to be owned by - * the transaction submitter, which is the only account a tx reserve sponsor - * can cover. + * Equivalent to the overload above, but resolves the sponsor via + * getTxReserveSponsor(ctx) instead of taking it explicitly. A no-op when the + * transaction is not reserve-sponsored. The entry is assumed to be owned by + * the transaction submitter, which is the only account a tx reserve sponsor + * can cover. */ void addSponsorToLedgerEntry(ApplyViewContext ctx, SLE::ref sle, SF_ACCOUNT const& field = sfSponsor); -/** Remove the reserve sponsor field from a ledger entry. +/** + * Remove the reserve sponsor field from a ledger entry. * - * A no-op when @p field is not present on @p sle. For RippleState entries - * the field must be sfHighSponsor or sfLowSponsor; for all other entry types - * it must be sfSponsor. + * A no-op when @p field is not present on @p sle. For RippleState entries + * the field must be sfHighSponsor or sfLowSponsor; for all other entry types + * it must be sfSponsor. * - * @param sle The ledger entry to modify - * @param field The sponsor field to clear (defaults to sfSponsor) + * @param sle The ledger entry to modify + * @param field The sponsor field to clear (defaults to sfSponsor) */ void removeSponsorFromLedgerEntry(SLE::ref sle, SF_ACCOUNT const& field = sfSponsor); -/** Whether @p account is the owner of a ledger entry for sponsorship purposes. +/** + * Whether @p account is the owner of a ledger entry for sponsorship purposes. * - * Ownership rules vary by entry type. For RippleState entries the owner is - * whichever side of the trust line holds the reserve. For credentials, the - * owner is the subject once accepted and the issuer before acceptance. + * Ownership rules vary by entry type. For RippleState entries the owner is + * whichever side of the trust line holds the reserve. For credentials, the + * owner is the subject once accepted and the issuer before acceptance. * - * @param view The ledger read view (used for SignerList lookup) - * @param sle The ledger entry whose owner is checked - * @param account The candidate account to match against - * @return true if @p account owns @p sle, false otherwise. + * @param view The ledger read view (used for SignerList lookup) + * @param sle The ledger entry whose owner is checked + * @param account The candidate account to match against + * @return true if @p account owns @p sle, false otherwise. */ bool isLedgerEntryOwner(ReadView const& view, SLE const& sle, AccountID const& account); -/** Whether this ledger entry type can have a reserve sponsor attached to it. */ +/** + * Whether this ledger entry type can have a reserve sponsor attached to it. + */ bool isLedgerEntrySupportedBySponsorship(SLE const& sle); -/** Return the number of owner-count units the ledger entry consumes. +/** + * Return the number of owner-count units the ledger entry consumes. * - * Most entries cost 1. Exceptions: Oracles scale with their price-data series - * size, Vaults cost 2 (vault + pseudo-account), and legacy SignerList entries - * (pre-MultiSignReserve) cost 2 + signer count. + * Most entries cost 1. Exceptions: Oracles scale with their price-data series + * size, Vaults cost 2 (vault + pseudo-account), and legacy SignerList entries + * (pre-MultiSignReserve) cost 2 + signer count. */ std::uint32_t getLedgerEntryOwnerCount(SLE const& sle); -/** Return the SField used to store the reserve sponsor for @p owner on @p sle. +/** + * Return the SField used to store the reserve sponsor for @p owner on @p sle. * - * For most entry types this is sfSponsor. RippleState entries use - * sfHighSponsor or sfLowSponsor depending on which side of the trust line - * @p owner holds. + * For most entry types this is sfSponsor. RippleState entries use + * sfHighSponsor or sfLowSponsor depending on which side of the trust line + * @p owner holds. * - * @param sle The ledger entry - * @param owner The account whose sponsor field is needed - * @return sfHighSponsor, sfLowSponsor, or sfSponsor as appropriate. + * @param sle The ledger entry + * @param owner The account whose sponsor field is needed + * @return sfHighSponsor, sfLowSponsor, or sfSponsor as appropriate. */ SF_ACCOUNT const& getLedgerEntrySponsorField(SLE const& sle, AccountID const& owner); diff --git a/include/xrpl/ledger/helpers/TokenHelpers.h b/include/xrpl/ledger/helpers/TokenHelpers.h index 33d9761d84..501101136a 100644 --- a/include/xrpl/ledger/helpers/TokenHelpers.h +++ b/include/xrpl/ledger/helpers/TokenHelpers.h @@ -28,21 +28,30 @@ namespace xrpl { // //------------------------------------------------------------------------------ -/** Controls the treatment of frozen account balances */ +/** + * Controls the treatment of frozen account balances + */ enum class FreezeHandling { IgnoreFreeze, ZeroIfFrozen }; -/** Controls the treatment of unauthorized MPT balances */ +/** + * Controls the treatment of unauthorized MPT balances + */ enum class AuthHandling { IgnoreAuth, ZeroIfUnauthorized }; -/** Controls whether to include the account's full spendable balance */ +/** + * Controls whether to include the account's full spendable balance + */ enum class SpendableHandling { SimpleBalance, FullBalance }; enum class WaiveTransferFee : bool { No = false, Yes }; -/** Controls whether accountSend is allowed to overflow OutstandingAmount **/ +/** + * Controls whether accountSend is allowed to overflow OutstandingAmount * + */ enum class AllowMPTOverflow : bool { No = false, Yes }; -/** Controls whether canTransfer enforces lsfMPTCanTransfer on MPTs. +/** + * Controls whether canTransfer enforces lsfMPTCanTransfer on MPTs. * * Default is No (enforce). Use Yes at call sites that must remain available * even when an MPT issuer has cleared lsfMPTCanTransfer - for example, @@ -81,9 +90,9 @@ isIndividualFrozen(ReadView const& view, AccountID const& account, Asset const& checkIndividualFrozen(ReadView const& view, AccountID const& account, Asset const& asset); /** - * isFrozen check is recursive for MPT shares in a vault, descending to - * assets in the vault, up to maxAssetCheckDepth recursion depth. This is - * purely defensive, as we currently do not allow such vaults to be created. + * isFrozen check is recursive for MPT shares in a vault, descending to + * assets in the vault, up to maxAssetCheckDepth recursion depth. This is + * purely defensive, as we currently do not allow such vaults to be created. */ [[nodiscard]] bool isFrozen( @@ -122,9 +131,9 @@ isDeepFrozen( std::uint8_t depth = 0); /** - * isFrozen check is recursive for MPT shares in a vault, descending to - * assets in the vault, up to maxAssetCheckDepth recursion depth. This is - * purely defensive, as we currently do not allow such vaults to be created. + * isFrozen check is recursive for MPT shares in a vault, descending to + * assets in the vault, up to maxAssetCheckDepth recursion depth. This is + * purely defensive, as we currently do not allow such vaults to be created. */ [[nodiscard]] bool isDeepFrozen( @@ -285,7 +294,8 @@ accountFunds( AuthHandling authHandling, beast::Journal j); -/** Returns the transfer fee as Rate based on the type of token +/** + * Returns the transfer fee as Rate based on the type of token * @param view The ledger view * @param amount The amount to transfer */ @@ -350,7 +360,8 @@ canTransfer( // --> bCheckIssuer : normally require issuer to be involved. // [[nodiscard]] // nodiscard commented out so DirectStep.cpp compiles. -/** Calls static directSendNoFeeIOU if saAmount represents Issue. +/** + * Calls static directSendNoFeeIOU if saAmount represents Issue. * Calls static directSendNoFeeMPT if saAmount represents MPTIssue. */ TER @@ -362,7 +373,8 @@ directSendNoFee( bool bCheckIssuer, beast::Journal j); -/** Calls static accountSendIOU if saAmount represents Issue. +/** + * Calls static accountSendIOU if saAmount represents Issue. * Calls static accountSendMPT if saAmount represents MPTIssue. */ [[nodiscard]] TER @@ -377,7 +389,8 @@ accountSend( AllowMPTOverflow allowOverflow = AllowMPTOverflow::No); using MultiplePaymentDestinations = std::vector>; -/** Like accountSend, except one account is sending multiple payments (with the +/** + * Like accountSend, except one account is sending multiple payments (with the * same asset!) simultaneously * * Calls static accountSendMultiIOU if saAmount represents Issue. diff --git a/include/xrpl/ledger/helpers/VaultHelpers.h b/include/xrpl/ledger/helpers/VaultHelpers.h index 2344b4de77..1bd1663314 100644 --- a/include/xrpl/ledger/helpers/VaultHelpers.h +++ b/include/xrpl/ledger/helpers/VaultHelpers.h @@ -9,57 +9,63 @@ namespace xrpl { -/** From the perspective of a vault, return the number of shares to give - depositor when they offer a fixed amount of assets. Note, since shares are - MPT, this number is integral and always truncated in this calculation. - - @param vault The vault SLE. - @param issuance The MPTokenIssuance SLE for the vault's shares. - @param assets The amount of assets to convert. - - @return The number of shares, or nullopt on error. -*/ +/** + * From the perspective of a vault, return the number of shares to give + * depositor when they offer a fixed amount of assets. Note, since shares are + * MPT, this number is integral and always truncated in this calculation. + * + * @param vault The vault SLE. + * @param issuance The MPTokenIssuance SLE for the vault's shares. + * @param assets The amount of assets to convert. + * + * @return The number of shares, or nullopt on error. + */ [[nodiscard]] std::optional assetsToSharesDeposit(SLE::const_ref vault, SLE::const_ref issuance, STAmount const& assets); -/** From the perspective of a vault, return the number of assets to take from - depositor when they receive a fixed amount of shares. Note, since shares are - MPT, they are always an integral number. - - @param vault The vault SLE. - @param issuance The MPTokenIssuance SLE for the vault's shares. - @param shares The amount of shares to convert. - - @return The number of assets, or nullopt on error. -*/ +/** + * From the perspective of a vault, return the number of assets to take from + * depositor when they receive a fixed amount of shares. Note, since shares are + * MPT, they are always an integral number. + * + * @param vault The vault SLE. + * @param issuance The MPTokenIssuance SLE for the vault's shares. + * @param shares The amount of shares to convert. + * + * @return The number of assets, or nullopt on error. + */ [[nodiscard]] std::optional sharesToAssetsDeposit(SLE::const_ref vault, SLE::const_ref issuance, STAmount const& shares); -/** Controls whether to truncate shares instead of rounding. */ +/** + * Controls whether to truncate shares instead of rounding. + */ enum class TruncateShares : bool { No = false, Yes = true }; -/** Controls whether the withdraw conversion helpers - (assetsToSharesWithdraw and sharesToAssetsWithdraw) subtract - sfLossUnrealized from sfAssetsTotal before computing the exchange rate. - The default (No) applies the standard discounted rate; Yes is used when - the redeemer is the sole remaining shareholder. -*/ +/** + * Controls whether the withdraw conversion helpers + * (assetsToSharesWithdraw and sharesToAssetsWithdraw) subtract + * sfLossUnrealized from sfAssetsTotal before computing the exchange rate. + * The default (No) applies the standard discounted rate; Yes is used when + * the redeemer is the sole remaining shareholder. + */ enum class WaiveUnrealizedLoss : bool { No = false, Yes = true }; -/** From the perspective of a vault, return the number of shares to demand from - the depositor when they ask to withdraw a fixed amount of assets. Since - shares are MPT this number is integral, and it will be rounded to nearest - unless explicitly requested to be truncated instead. - - @param vault The vault SLE. - @param issuance The MPTokenIssuance SLE for the vault's shares. - @param assets The amount of assets to convert. - @param truncate Whether to truncate instead of rounding. - @param waive Whether to waive the unrealized-loss discount when computing - the exchange rate. - - @return The number of shares, or nullopt on error. -*/ +/** + * From the perspective of a vault, return the number of shares to demand from + * the depositor when they ask to withdraw a fixed amount of assets. Since + * shares are MPT this number is integral, and it will be rounded to nearest + * unless explicitly requested to be truncated instead. + * + * @param vault The vault SLE. + * @param issuance The MPTokenIssuance SLE for the vault's shares. + * @param assets The amount of assets to convert. + * @param truncate Whether to truncate instead of rounding. + * @param waive Whether to waive the unrealized-loss discount when computing + * the exchange rate. + * + * @return The number of shares, or nullopt on error. + */ [[nodiscard]] std::optional assetsToSharesWithdraw( SLE::const_ref vault, @@ -68,18 +74,19 @@ assetsToSharesWithdraw( TruncateShares truncate = TruncateShares::No, WaiveUnrealizedLoss waive = WaiveUnrealizedLoss::No); -/** From the perspective of a vault, return the number of assets to give the - depositor when they redeem a fixed amount of shares. Note, since shares are - MPT, they are always an integral number. - - @param vault The vault SLE. - @param issuance The MPTokenIssuance SLE for the vault's shares. - @param shares The amount of shares to convert. - @param waive Whether to waive (i.e. not subtract) the vault's unrealized - loss when computing the exchange rate. - - @return The number of assets, or nullopt on error. -*/ +/** + * From the perspective of a vault, return the number of assets to give the + * depositor when they redeem a fixed amount of shares. Note, since shares are + * MPT, they are always an integral number. + * + * @param vault The vault SLE. + * @param issuance The MPTokenIssuance SLE for the vault's shares. + * @param shares The amount of shares to convert. + * @param waive Whether to waive (i.e. not subtract) the vault's unrealized + * loss when computing the exchange rate. + * + * @return The number of assets, or nullopt on error. + */ [[nodiscard]] std::optional sharesToAssetsWithdraw( SLE::const_ref vault, @@ -87,15 +94,16 @@ sharesToAssetsWithdraw( STAmount const& shares, WaiveUnrealizedLoss waive = WaiveUnrealizedLoss::No); -/** Returns true iff `account` holds all of the vault's outstanding shares — - i.e. is the sole remaining shareholder. Returns false if the account - holds no shares or fewer than the total outstanding. - - @param view The ledger view. - @param account The candidate sole shareholder. - @param issuance The MPTokenIssuance SLE for the vault's shares; provides - both the share MPTID and the outstanding-amount total. -*/ +/** + * Returns true iff `account` holds all of the vault's outstanding shares — + * i.e. is the sole remaining shareholder. Returns false if the account + * holds no shares or fewer than the total outstanding. + * + * @param view The ledger view. + * @param account The candidate sole shareholder. + * @param issuance The MPTokenIssuance SLE for the vault's shares; provides + * both the share MPTID and the outstanding-amount total. + */ [[nodiscard]] bool isSoleShareholder(ReadView const& view, AccountID const& account, SLE::const_ref issuance); diff --git a/include/xrpl/net/HTTPClient.h b/include/xrpl/net/HTTPClient.h index 7ed9b35b9b..752afac9c4 100644 --- a/include/xrpl/net/HTTPClient.h +++ b/include/xrpl/net/HTTPClient.h @@ -14,7 +14,8 @@ namespace xrpl { -/** Provides an asynchronous HTTP client implementation with optional SSL. +/** + * Provides an asynchronous HTTP client implementation with optional SSL. */ class HTTPClient { @@ -30,14 +31,15 @@ public: bool sslVerify, beast::Journal j); - /** Destroys the global SSL context created by initializeSSLContext(). + /** + * Destroys the global SSL context created by initializeSSLContext(). * - * This releases the underlying boost::asio::ssl::context and any - * associated OpenSSL resources. Must not be called while any - * HTTPClient requests are in flight. + * This releases the underlying boost::asio::ssl::context and any + * associated OpenSSL resources. Must not be called while any + * HTTPClient requests are in flight. * - * @note Currently only called from tests during teardown. In production, - * the SSL context lives for the lifetime of the process. + * @note Currently only called from tests during teardown. In production, + * the SSL context lives for the lifetime of the process. */ static void cleanupSSLContext(); diff --git a/include/xrpl/net/RegisterSSLCerts.h b/include/xrpl/net/RegisterSSLCerts.h index 5cc9934638..004f893515 100644 --- a/include/xrpl/net/RegisterSSLCerts.h +++ b/include/xrpl/net/RegisterSSLCerts.h @@ -5,13 +5,14 @@ #include namespace xrpl { -/** Register default SSL certificates. - - Register the system default SSL root certificates. On linux/mac, - this just calls asio's `set_default_verify_paths` to look in standard - operating system locations. On windows, it uses the OS certificate - store accessible via CryptoAPI. -*/ +/** + * Register default SSL certificates. + * + * Register the system default SSL root certificates. On linux/mac, + * this just calls asio's `set_default_verify_paths` to look in standard + * operating system locations. On windows, it uses the OS certificate + * store accessible via CryptoAPI. + */ void registerSSLCerts(boost::asio::ssl::context&, boost::system::error_code&, beast::Journal j); diff --git a/include/xrpl/nodestore/Backend.h b/include/xrpl/nodestore/Backend.h index 29c4a8b526..564a874c5e 100644 --- a/include/xrpl/nodestore/Backend.h +++ b/include/xrpl/nodestore/Backend.h @@ -15,33 +15,37 @@ namespace xrpl::NodeStore { -/** A backend used for the NodeStore. - - The NodeStore uses a swappable backend so that other database systems - can be tried. Different databases may offer various features such - as improved performance, fault tolerant or distributed storage, or - all in-memory operation. - - A given instance of a backend is fixed to a particular key size. -*/ +/** + * A backend used for the NodeStore. + * + * The NodeStore uses a swappable backend so that other database systems + * can be tried. Different databases may offer various features such + * as improved performance, fault tolerant or distributed storage, or + * all in-memory operation. + * + * A given instance of a backend is fixed to a particular key size. + */ class Backend { public: - /** Destroy the backend. - - All open files are closed and flushed. If there are batched writes - or other tasks scheduled, they will be completed before this call - returns. - */ + /** + * Destroy the backend. + * + * All open files are closed and flushed. If there are batched writes + * or other tasks scheduled, they will be completed before this call + * returns. + */ virtual ~Backend() = default; - /** Get the human-readable name of this backend. - This is used for diagnostic output. - */ + /** + * Get the human-readable name of this backend. + * This is used for diagnostic output. + */ virtual std::string getName() = 0; - /** Get the block size for backends that support it + /** + * Get the block size for backends that support it */ [[nodiscard]] virtual std::optional getBlockSize() const @@ -49,25 +53,28 @@ public: return std::nullopt; } - /** Open the backend. - @param createIfMissing Create the database files if necessary. - This allows the caller to catch exceptions. - */ + /** + * Open the backend. + * @param createIfMissing Create the database files if necessary. + * This allows the caller to catch exceptions. + */ virtual void open(bool createIfMissing = true) = 0; - /** Returns true is the database is open. + /** + * Returns true is the database is open. */ virtual bool isOpen() = 0; - /** Open the backend. - @param createIfMissing Create the database files if necessary. - @param appType Deterministic appType used to create a backend. - @param uid Deterministic uid used to create a backend. - @param salt Deterministic salt used to create a backend. - @throws std::runtime_error is function is called not for NuDB backend. - */ + /** + * Open the backend. + * @param createIfMissing Create the database files if necessary. + * @param appType Deterministic appType used to create a backend. + * @param uid Deterministic uid used to create a backend. + * @param salt Deterministic salt used to create a backend. + * @throws std::runtime_error is function is called not for NuDB backend. + */ virtual void open(bool createIfMissing, uint64_t appType, uint64_t uid, uint64_t salt) { @@ -75,60 +82,70 @@ public: "Deterministic appType/uid/salt not supported by backend " + getName()); } - /** Close the backend. - This allows the caller to catch exceptions. - */ + /** + * Close the backend. + * This allows the caller to catch exceptions. + */ virtual void close() = 0; - /** Fetch a single object. - If the object is not found or an error is encountered, the - result will indicate the condition. - @note This will be called concurrently. - @param hash The hash of the object. - @param pObject [out] The created object if successful. - @return The result of the operation. - */ + /** + * Fetch a single object. + * If the object is not found or an error is encountered, the + * result will indicate the condition. + * @note This will be called concurrently. + * @param hash The hash of the object. + * @param pObject [out] The created object if successful. + * @return The result of the operation. + */ virtual Status fetch(uint256 const& hash, std::shared_ptr* pObject) = 0; - /** Store a single object. - Depending on the implementation this may happen immediately - or deferred using a scheduled task. - @note This will be called concurrently. - @param object The object to store. - */ + /** + * Store a single object. + * Depending on the implementation this may happen immediately + * or deferred using a scheduled task. + * @note This will be called concurrently. + * @param object The object to store. + */ virtual void store(std::shared_ptr const& object) = 0; - /** Store a group of objects. - @note This function will not be called concurrently with - itself or @ref store. - */ + /** + * Store a group of objects. + * @note This function will not be called concurrently with + * itself or @ref store. + */ virtual void storeBatch(Batch const& batch) = 0; virtual void sync() = 0; - /** Visit every object in the database - This is usually called during import. - @note This routine will not be called concurrently with itself - or other methods. - @see import - */ + /** + * Visit every object in the database + * This is usually called during import. + * @note This routine will not be called concurrently with itself + * or other methods. + * @see import + */ virtual void forEach(std::function)> f) = 0; - /** Estimate the number of write operations pending. */ + /** + * Estimate the number of write operations pending. + */ virtual int getWriteLoad() = 0; - /** Remove contents on disk upon destruction. */ + /** + * Remove contents on disk upon destruction. + */ virtual void setDeletePath() = 0; - /** Perform consistency checks on database. + /** + * Perform consistency checks on database. * * This method is implemented only by NuDBBackend. It is not yet called * anywhere, but it might be a good idea to one day call it at startup to @@ -139,7 +156,9 @@ public: { } - /** Returns the number of file descriptors the backend expects to need. */ + /** + * Returns the number of file descriptors the backend expects to need. + */ [[nodiscard]] virtual int fdRequired() const = 0; }; diff --git a/include/xrpl/nodestore/Database.h b/include/xrpl/nodestore/Database.h index 49002ee301..96ba91bd76 100644 --- a/include/xrpl/nodestore/Database.h +++ b/include/xrpl/nodestore/Database.h @@ -27,100 +27,109 @@ class Section; namespace xrpl::NodeStore { -/** Persistency layer for NodeObject - - A Node is a ledger object which is uniquely identified by a key, which is - the 256-bit hash of the body of the node. The payload is a variable length - block of serialized data. - - All ledger data is stored as node objects and as such, needs to be persisted - between launches. Furthermore, since the set of node objects will in - general be larger than the amount of available memory, purged node objects - which are later accessed must be retrieved from the node store. - - @see NodeObject -*/ +/** + * Persistency layer for NodeObject + * + * A Node is a ledger object which is uniquely identified by a key, which is + * the 256-bit hash of the body of the node. The payload is a variable length + * block of serialized data. + * + * All ledger data is stored as node objects and as such, needs to be persisted + * between launches. Furthermore, since the set of node objects will in + * general be larger than the amount of available memory, purged node objects + * which are later accessed must be retrieved from the node store. + * + * @see NodeObject + */ class Database { public: Database() = delete; - /** Construct the node store. - - @param scheduler The scheduler to use for performing asynchronous tasks. - @param readThreads The number of asynchronous read threads to create. - @param config The configuration settings - @param journal Destination for logging output. - */ + /** + * Construct the node store. + * + * @param scheduler The scheduler to use for performing asynchronous tasks. + * @param readThreads The number of asynchronous read threads to create. + * @param config The configuration settings + * @param journal Destination for logging output. + */ Database(Scheduler& scheduler, int readThreads, Section const& config, beast::Journal j); - /** Destroy the node store. - All pending operations are completed, pending writes flushed, - and files closed before this returns. - */ + /** + * Destroy the node store. + * All pending operations are completed, pending writes flushed, + * and files closed before this returns. + */ virtual ~Database(); - /** Retrieve the name associated with this backend. - This is used for diagnostics and may not reflect the actual path - or paths used by the underlying backend. - */ + /** + * Retrieve the name associated with this backend. + * This is used for diagnostics and may not reflect the actual path + * or paths used by the underlying backend. + */ virtual std::string getName() const = 0; - /** Import objects from another database. */ + /** + * Import objects from another database. + */ virtual void importDatabase(Database& source) = 0; - /** Retrieve the estimated number of pending write operations. - This is used for diagnostics. - */ + /** + * Retrieve the estimated number of pending write operations. + * This is used for diagnostics. + */ virtual std::int32_t getWriteLoad() const = 0; - /** Store the object. - - The caller's Blob parameter is overwritten. - - @param type The type of object. - @param data The payload of the object. The caller's - variable is overwritten. - @param hash The 256-bit hash of the payload data. - @param ledgerSeq The sequence of the ledger the object belongs to. - - @return `true` if the object was stored? - */ + /** + * Store the object. + * + * The caller's Blob parameter is overwritten. + * + * @param type The type of object. + * @param data The payload of the object. The caller's + * variable is overwritten. + * @param hash The 256-bit hash of the payload data. + * @param ledgerSeq The sequence of the ledger the object belongs to. + * + * @return `true` if the object was stored? + */ virtual void store(NodeObjectType type, Blob&& data, uint256 const& hash, std::uint32_t ledgerSeq) = 0; - /* Check if two ledgers are in the same database - - If these two sequence numbers map to the same database, - the result of a fetch with either sequence number would - be identical. - - @param s1 The first sequence number - @param s2 The second sequence number - - @return 'true' if both ledgers would be in the same DB - - */ + /** + * Check if two ledgers are in the same database + * + * If these two sequence numbers map to the same database, + * the result of a fetch with either sequence number would + * be identical. + * + * @param s1 The first sequence number + * @param s2 The second sequence number + * + * @return 'true' if both ledgers would be in the same DB + */ virtual bool isSameDB(std::uint32_t s1, std::uint32_t s2) = 0; virtual void sync() = 0; - /** Fetch a node object. - If the object is known to be not in the database, isn't found in the - database during the fetch, or failed to load correctly during the fetch, - `nullptr` is returned. - - @note This can be called concurrently. - @param hash The key of the object to retrieve. - @param ledgerSeq The sequence of the ledger where the object is stored. - @param fetchType the type of fetch, synchronous or asynchronous. - @return The object, or nullptr if it couldn't be retrieved. - */ + /** + * Fetch a node object. + * If the object is known to be not in the database, isn't found in the + * database during the fetch, or failed to load correctly during the fetch, + * `nullptr` is returned. + * + * @note This can be called concurrently. + * @param hash The key of the object to retrieve. + * @param ledgerSeq The sequence of the ledger where the object is stored. + * @param fetchType the type of fetch, synchronous or asynchronous. + * @return The object, or nullptr if it couldn't be retrieved. + */ std::shared_ptr fetchNodeObject( uint256 const& hash, @@ -128,29 +137,33 @@ public: FetchType fetchType = FetchType::Synchronous, bool duplicate = false); - /** Fetch an object without waiting. - If I/O is required to determine whether or not the object is present, - `false` is returned. Otherwise, `true` is returned and `object` is set - to refer to the object, or `nullptr` if the object is not present. - If I/O is required, the I/O is scheduled and `true` is returned - - @note This can be called concurrently. - @param hash The key of the object to retrieve - @param ledgerSeq The sequence of the ledger where the - object is stored. - @param callback Callback function when read completes - */ + /** + * Fetch an object without waiting. + * If I/O is required to determine whether or not the object is present, + * `false` is returned. Otherwise, `true` is returned and `object` is set + * to refer to the object, or `nullptr` if the object is not present. + * If I/O is required, the I/O is scheduled and `true` is returned + * + * @note This can be called concurrently. + * @param hash The key of the object to retrieve + * @param ledgerSeq The sequence of the ledger where the + * object is stored. + * @param callback Callback function when read completes + */ virtual void asyncFetch( uint256 const& hash, std::uint32_t ledgerSeq, std::function const&)>&& callback); - /** Remove expired entries from the positive and negative caches. */ + /** + * Remove expired entries from the positive and negative caches. + */ virtual void sweep() = 0; - /** Gather statistics pertaining to read and write activities. + /** + * Gather statistics pertaining to read and write activities. * * @param obj Json object reference into which to place counters. */ @@ -187,7 +200,9 @@ public: void getCountsJson(json::Value& obj); - /** Returns the number of file descriptors the database expects to need */ + /** + * Returns the number of file descriptors the database expects to need + */ int fdRequired() const { @@ -200,7 +215,8 @@ public: bool isStopping() const; - /** @return The earliest ledger sequence allowed + /** + * @return The earliest ledger sequence allowed */ [[nodiscard]] std::uint32_t earliestLedgerSeq() const noexcept @@ -277,13 +293,14 @@ private: FetchReport& fetchReport, bool duplicate) = 0; - /** Visit every object in the database - This is usually called during import. - - @note This routine will not be called concurrently with itself - or other methods. - @see import - */ + /** + * Visit every object in the database + * This is usually called during import. + * + * @note This routine will not be called concurrently with itself + * or other methods. + * @see import + */ virtual void forEach(std::function)> f) = 0; diff --git a/include/xrpl/nodestore/DatabaseRotating.h b/include/xrpl/nodestore/DatabaseRotating.h index 69eb31261d..1c5bb0efaf 100644 --- a/include/xrpl/nodestore/DatabaseRotating.h +++ b/include/xrpl/nodestore/DatabaseRotating.h @@ -28,13 +28,14 @@ public: { } - /** Rotates the backends. - - @param newBackend New writable backend - @param f A function executed after the rotation outside of lock. The - values passed to f will be the new backend database names _after_ - rotation. - */ + /** + * Rotates the backends. + * + * @param newBackend New writable backend + * @param f A function executed after the rotation outside of lock. The + * values passed to f will be the new backend database names _after_ + * rotation. + */ virtual void rotate( std::unique_ptr&& newBackend, diff --git a/include/xrpl/nodestore/DummyScheduler.h b/include/xrpl/nodestore/DummyScheduler.h index f626115786..49b0d37462 100644 --- a/include/xrpl/nodestore/DummyScheduler.h +++ b/include/xrpl/nodestore/DummyScheduler.h @@ -5,7 +5,9 @@ namespace xrpl::NodeStore { -/** Simple NodeStore Scheduler that just performs the tasks synchronously. */ +/** + * Simple NodeStore Scheduler that just performs the tasks synchronously. + */ class DummyScheduler : public Scheduler { public: diff --git a/include/xrpl/nodestore/Factory.h b/include/xrpl/nodestore/Factory.h index e79ae3e05d..a18023a8a8 100644 --- a/include/xrpl/nodestore/Factory.h +++ b/include/xrpl/nodestore/Factory.h @@ -16,24 +16,29 @@ class Section; namespace xrpl::NodeStore { -/** Base class for backend factories. */ +/** + * Base class for backend factories. + */ class Factory { public: virtual ~Factory() = default; - /** Retrieve the name of this factory. */ + /** + * Retrieve the name of this factory. + */ [[nodiscard]] virtual std::string getName() const = 0; - /** Create an instance of this factory's backend. - - @param keyBytes The fixed number of bytes per key. - @param parameters A set of key/value configuration pairs. - @param burstSize Backend burst size in bytes. - @param scheduler The scheduler to use for running tasks. - @return A pointer to the Backend object. - */ + /** + * Create an instance of this factory's backend. + * + * @param keyBytes The fixed number of bytes per key. + * @param parameters A set of key/value configuration pairs. + * @param burstSize Backend burst size in bytes. + * @param scheduler The scheduler to use for running tasks. + * @return A pointer to the Backend object. + */ virtual std::unique_ptr createInstance( size_t keyBytes, @@ -42,15 +47,16 @@ public: Scheduler& scheduler, beast::Journal journal) = 0; - /** Create an instance of this factory's backend. - - @param keyBytes The fixed number of bytes per key. - @param parameters A set of key/value configuration pairs. - @param burstSize Backend burst size in bytes. - @param scheduler The scheduler to use for running tasks. - @param context The context used by database. - @return A pointer to the Backend object. - */ + /** + * Create an instance of this factory's backend. + * + * @param keyBytes The fixed number of bytes per key. + * @param parameters A set of key/value configuration pairs. + * @param burstSize Backend burst size in bytes. + * @param scheduler The scheduler to use for running tasks. + * @param context The context used by database. + * @return A pointer to the Backend object. + */ virtual std::unique_ptr createInstance( size_t keyBytes, diff --git a/include/xrpl/nodestore/Manager.h b/include/xrpl/nodestore/Manager.h index f813412846..54d99fe94b 100644 --- a/include/xrpl/nodestore/Manager.h +++ b/include/xrpl/nodestore/Manager.h @@ -12,7 +12,9 @@ namespace xrpl::NodeStore { -/** Singleton for managing NodeStore factories and back ends. */ +/** + * Singleton for managing NodeStore factories and back ends. + */ class Manager { public: @@ -22,26 +24,35 @@ public: Manager& operator=(Manager const&) = delete; - /** Returns the instance of the manager singleton. */ + /** + * Returns the instance of the manager singleton. + */ static Manager& instance(); - /** Add a factory. */ + /** + * Add a factory. + */ virtual void insert(Factory& factory) = 0; - /** Remove a factory. */ + /** + * Remove a factory. + */ virtual void erase(Factory& factory) = 0; - /** Return a pointer to the matching factory if it exists. - @param name The name to match, performed case-insensitive. - @return `nullptr` if a match was not found. - */ + /** + * Return a pointer to the matching factory if it exists. + * @param name The name to match, performed case-insensitive. + * @return `nullptr` if a match was not found. + */ virtual Factory* find(std::string const& name) = 0; - /** Create a backend. */ + /** + * Create a backend. + */ virtual std::unique_ptr makeBackend( Section const& parameters, @@ -49,34 +60,35 @@ public: Scheduler& scheduler, beast::Journal journal) = 0; - /** Construct a NodeStore database. - - The parameters are key value pairs passed to the backend. The - 'type' key must exist, it defines the choice of backend. Most - backends also require a 'path' field. - - Some choices for 'type' are: - HyperLevelDB, LevelDBFactory, SQLite, MDB - - If the fastBackendParameter is omitted or empty, no ephemeral database - is used. If the scheduler parameter is omitted or unspecified, a - synchronous scheduler is used which performs all tasks immediately on - the caller's thread. - - @note If the database cannot be opened or created, an exception is - thrown. - - @param name A diagnostic label for the database. - @param burstSize Backend burst size in bytes. - @param scheduler The scheduler to use for performing asynchronous tasks. - @param readThreads The number of async read threads to create - @param backendParameters The parameter string for the persistent - backend. - @param fastBackendParameters [optional] The parameter string for the - ephemeral backend. - - @return The opened database. - */ + /** + * Construct a NodeStore database. + * + * The parameters are key value pairs passed to the backend. The + * 'type' key must exist, it defines the choice of backend. Most + * backends also require a 'path' field. + * + * Some choices for 'type' are: + * HyperLevelDB, LevelDBFactory, SQLite, MDB + * + * If the fastBackendParameter is omitted or empty, no ephemeral database + * is used. If the scheduler parameter is omitted or unspecified, a + * synchronous scheduler is used which performs all tasks immediately on + * the caller's thread. + * + * @note If the database cannot be opened or created, an exception is + * thrown. + * + * @param name A diagnostic label for the database. + * @param burstSize Backend burst size in bytes. + * @param scheduler The scheduler to use for performing asynchronous tasks. + * @param readThreads The number of async read threads to create + * @param backendParameters The parameter string for the persistent + * backend. + * @param fastBackendParameters [optional] The parameter string for the + * ephemeral backend. + * + * @return The opened database. + */ virtual std::unique_ptr makeDatabase( std::size_t burstSize, diff --git a/include/xrpl/nodestore/NodeObject.h b/include/xrpl/nodestore/NodeObject.h index 3f3b75d5f8..b96d65fa12 100644 --- a/include/xrpl/nodestore/NodeObject.h +++ b/include/xrpl/nodestore/NodeObject.h @@ -12,7 +12,9 @@ namespace xrpl { -/** The types of node objects. */ +/** + * The types of node objects. + */ enum class NodeObjectType : std::uint32_t { Unknown = 0, Ledger = 1, @@ -21,15 +23,16 @@ enum class NodeObjectType : std::uint32_t { Dummy = 512 // an invalid or missing object }; -/** A simple object that the Ledger uses to store entries. - NodeObjects are comprised of a type, a hash, and a blob. - They can be uniquely identified by the hash, which is a half-SHA512 of - the blob. The blob is a variable length block of serialized data. The - type identifies what the blob contains. - - @note No checking is performed to make sure the hash matches the data. - @see SHAMap -*/ +/** + * A simple object that the Ledger uses to store entries. + * NodeObjects are comprised of a type, a hash, and a blob. + * They can be uniquely identified by the hash, which is a half-SHA512 of + * the blob. The blob is a variable length block of serialized data. The + * type identifies what the blob contains. + * + * @note No checking is performed to make sure the hash matches the data. + * @see SHAMap + */ class NodeObject : public CountedObject { public: @@ -48,29 +51,36 @@ public: // This constructor is private, use createObject instead. NodeObject(NodeObjectType type, Blob&& data, uint256 const& hash, PrivateAccess); - /** Create an object from fields. - - The caller's variable is modified during this call. The - underlying storage for the Blob is taken over by the NodeObject. - - @param type The type of object. - @param ledgerIndex The ledger in which this object appears. - @param data A buffer containing the payload. The caller's variable - is overwritten. - @param hash The 256-bit hash of the payload data. - */ + /** + * Create an object from fields. + * + * The caller's variable is modified during this call. The + * underlying storage for the Blob is taken over by the NodeObject. + * + * @param type The type of object. + * @param ledgerIndex The ledger in which this object appears. + * @param data A buffer containing the payload. The caller's variable + * is overwritten. + * @param hash The 256-bit hash of the payload data. + */ static std::shared_ptr createObject(NodeObjectType type, Blob&& data, uint256 const& hash); - /** Returns the type of this object. */ + /** + * Returns the type of this object. + */ [[nodiscard]] NodeObjectType getType() const; - /** Returns the hash of the data. */ + /** + * Returns the hash of the data. + */ [[nodiscard]] uint256 const& getHash() const; - /** Returns the underlying data. */ + /** + * Returns the underlying data. + */ [[nodiscard]] Blob const& getData() const; diff --git a/include/xrpl/nodestore/Scheduler.h b/include/xrpl/nodestore/Scheduler.h index 588ff19bdc..5d93a80eaa 100644 --- a/include/xrpl/nodestore/Scheduler.h +++ b/include/xrpl/nodestore/Scheduler.h @@ -8,7 +8,9 @@ namespace xrpl::NodeStore { enum class FetchType { Synchronous, Async }; -/** Contains information about a fetch operation. */ +/** + * Contains information about a fetch operation. + */ struct FetchReport { explicit FetchReport(FetchType fetchType) : fetchType(fetchType) @@ -20,7 +22,9 @@ struct FetchReport bool wasFound = false; }; -/** Contains information about a batch write operation. */ +/** + * Contains information about a batch write operation. + */ struct BatchWriteReport { explicit BatchWriteReport() = default; @@ -29,36 +33,40 @@ struct BatchWriteReport int writeCount; }; -/** Scheduling for asynchronous backend activity - - For improved performance, a backend has the option of performing writes - in batches. These writes can be scheduled using the provided scheduler - object. - - @see BatchWriter -*/ +/** + * Scheduling for asynchronous backend activity + * + * For improved performance, a backend has the option of performing writes + * in batches. These writes can be scheduled using the provided scheduler + * object. + * + * @see BatchWriter + */ class Scheduler { public: virtual ~Scheduler() = default; - /** Schedules a task. - Depending on the implementation, the task may be invoked either on - the current thread of execution, or an unspecified - implementation-defined foreign thread. - */ + /** + * Schedules a task. + * Depending on the implementation, the task may be invoked either on + * the current thread of execution, or an unspecified + * implementation-defined foreign thread. + */ virtual void scheduleTask(Task& task) = 0; - /** Reports completion of a fetch - Allows the scheduler to monitor the node store's performance - */ + /** + * Reports completion of a fetch + * Allows the scheduler to monitor the node store's performance + */ virtual void onFetch(FetchReport const& report) = 0; - /** Reports the completion of a batch write - Allows the scheduler to monitor the node store's performance - */ + /** + * Reports the completion of a batch write + * Allows the scheduler to monitor the node store's performance + */ virtual void onBatchWrite(BatchWriteReport const& report) = 0; }; diff --git a/include/xrpl/nodestore/Task.h b/include/xrpl/nodestore/Task.h index 0695970a68..59fe648476 100644 --- a/include/xrpl/nodestore/Task.h +++ b/include/xrpl/nodestore/Task.h @@ -2,14 +2,17 @@ namespace xrpl::NodeStore { -/** Derived classes perform scheduled tasks. */ +/** + * Derived classes perform scheduled tasks. + */ struct Task { virtual ~Task() = default; - /** Performs the task. - The call may take place on a foreign thread. - */ + /** + * Performs the task. + * The call may take place on a foreign thread. + */ virtual void performScheduledTask() = 0; }; diff --git a/include/xrpl/nodestore/Types.h b/include/xrpl/nodestore/Types.h index eaee82c99e..872d948a36 100644 --- a/include/xrpl/nodestore/Types.h +++ b/include/xrpl/nodestore/Types.h @@ -18,7 +18,9 @@ static constexpr auto kBatchWritePreallocationSize = 256; // static constexpr auto kBatchWriteLimitSize = 65536; -/** Return codes from Backend operations. */ +/** + * Return codes from Backend operations. + */ enum class Status { Ok = 0, NotFound = 1, @@ -29,7 +31,9 @@ enum class Status { CustomCode = 100 }; -/** A batch of NodeObjects to write at once. */ +/** + * A batch of NodeObjects to write at once. + */ using Batch = std::vector>; } // namespace xrpl::NodeStore diff --git a/include/xrpl/nodestore/detail/BatchWriter.h b/include/xrpl/nodestore/detail/BatchWriter.h index 7fa23bcb3e..b89df0da14 100644 --- a/include/xrpl/nodestore/detail/BatchWriter.h +++ b/include/xrpl/nodestore/detail/BatchWriter.h @@ -11,18 +11,21 @@ namespace xrpl::NodeStore { -/** Batch-writing assist logic. - - The batch writes are performed with a scheduled task. Use of the - class it not required. A backend can implement its own write batching, - or skip write batching if doing so yields a performance benefit. - - @see Scheduler -*/ +/** + * Batch-writing assist logic. + * + * The batch writes are performed with a scheduled task. Use of the + * class it not required. A backend can implement its own write batching, + * or skip write batching if doing so yields a performance benefit. + * + * @see Scheduler + */ class BatchWriter : private Task { public: - /** This callback does the actual writing. */ + /** + * This callback does the actual writing. + */ struct Callback { virtual ~Callback() = default; @@ -35,24 +38,30 @@ public: writeBatch(Batch const& batch) = 0; }; - /** Create a batch writer. */ + /** + * Create a batch writer. + */ BatchWriter(Callback& callback, Scheduler& scheduler); - /** Destroy a batch writer. - - Anything pending in the batch is written out before this returns. - */ + /** + * Destroy a batch writer. + * + * Anything pending in the batch is written out before this returns. + */ ~BatchWriter() override; - /** Store the object. - - This will add to the batch and initiate a scheduled task to - write the batch out. - */ + /** + * Store the object. + * + * This will add to the batch and initiate a scheduled task to + * write the batch out. + */ void store(std::shared_ptr const& object); - /** Get an estimate of the amount of writing I/O pending. */ + /** + * Get an estimate of the amount of writing I/O pending. + */ int getWriteLoad(); diff --git a/include/xrpl/nodestore/detail/DecodedBlob.h b/include/xrpl/nodestore/detail/DecodedBlob.h index 02ccd5787a..d0cc5e3404 100644 --- a/include/xrpl/nodestore/detail/DecodedBlob.h +++ b/include/xrpl/nodestore/detail/DecodedBlob.h @@ -6,30 +6,37 @@ namespace xrpl::NodeStore { -/** Parsed key/value blob into NodeObject components. - - This will extract the information required to construct a NodeObject. It - also does consistency checking and returns the result, so it is possible - to determine if the data is corrupted without throwing an exception. Not - all forms of corruption are detected so further analysis will be needed - to eliminate false negatives. - - @note This defines the database format of a NodeObject! -*/ +/** + * Parsed key/value blob into NodeObject components. + * + * This will extract the information required to construct a NodeObject. It + * also does consistency checking and returns the result, so it is possible + * to determine if the data is corrupted without throwing an exception. Not + * all forms of corruption are detected so further analysis will be needed + * to eliminate false negatives. + * + * @note This defines the database format of a NodeObject! + */ class DecodedBlob { public: - /** Construct the decoded blob from raw data. */ + /** + * Construct the decoded blob from raw data. + */ DecodedBlob(void const* key, void const* value, int valueBytes); - /** Determine if the decoding was successful. */ + /** + * Determine if the decoding was successful. + */ [[nodiscard]] bool wasOk() const noexcept { return success_; } - /** Create a NodeObject from this data. */ + /** + * Create a NodeObject from this data. + */ std::shared_ptr createObject(); diff --git a/include/xrpl/nodestore/detail/EncodedBlob.h b/include/xrpl/nodestore/detail/EncodedBlob.h index 3982ab1b95..d668cdccd8 100644 --- a/include/xrpl/nodestore/detail/EncodedBlob.h +++ b/include/xrpl/nodestore/detail/EncodedBlob.h @@ -14,47 +14,54 @@ namespace xrpl::NodeStore { -/** Convert a NodeObject from in-memory to database format. - - The (suboptimal) database format consists of: - - - 8 prefix bytes which will typically be 0, but don't assume that's the - case; earlier versions of the code would use these bytes to store the - ledger index either once or twice. - - A single byte denoting the type of the object. - - The payload. - - @note This class is typically instantiated on the stack, so the size of - the object does not matter as much as it normally would since the - allocation is, effectively, free. - - We leverage that fact to preallocate enough memory to handle most - payloads as part of this object, eliminating the need for dynamic - allocation. As of this writing ~94% of objects require fewer than - 1024 payload bytes. +/** + * Convert a NodeObject from in-memory to database format. + * + * The (suboptimal) database format consists of: + * + * - 8 prefix bytes which will typically be 0, but don't assume that's the + * case; earlier versions of the code would use these bytes to store the + * ledger index either once or twice. + * - A single byte denoting the type of the object. + * - The payload. + * + * @note This class is typically instantiated on the stack, so the size of + * the object does not matter as much as it normally would since the + * allocation is, effectively, free. + * + * We leverage that fact to preallocate enough memory to handle most + * payloads as part of this object, eliminating the need for dynamic + * allocation. As of this writing ~94% of objects require fewer than + * 1024 payload bytes. */ class EncodedBlob { - /** The 32-byte key of the serialized object. */ + /** + * The 32-byte key of the serialized object. + */ std::array key_{}; - /** A pre-allocated buffer for the serialized object. - - The buffer is large enough for the 9 byte prefix and at least - 1024 more bytes. The precise size is calculated automatically - at compile time so as to avoid wasting space on padding bytes. + /** + * A pre-allocated buffer for the serialized object. + * + * The buffer is large enough for the 9 byte prefix and at least + * 1024 more bytes. The precise size is calculated automatically + * at compile time so as to avoid wasting space on padding bytes. */ std::array payload_{}; - /** The size of the serialized data. */ + /** + * The size of the serialized data. + */ std::uint32_t size_; - /** A pointer to the serialized data. - - This may point to the pre-allocated buffer (if it is sufficiently - large) or to a dynamically allocated buffer. + /** + * A pointer to the serialized data. + * + * This may point to the pre-allocated buffer (if it is sufficiently + * large) or to a dynamically allocated buffer. */ std::uint8_t* const ptr_; diff --git a/include/xrpl/protocol/AMMCore.h b/include/xrpl/protocol/AMMCore.h index c4fccd029a..a3666c7960 100644 --- a/include/xrpl/protocol/AMMCore.h +++ b/include/xrpl/protocol/AMMCore.h @@ -33,17 +33,20 @@ class STObject; class STAmount; class Rules; -/** Calculate Liquidity Provider Token (LPT) Currency. +/** + * Calculate Liquidity Provider Token (LPT) Currency. */ Currency ammLPTCurrency(Asset const& asset1, Asset const& asset2); -/** Calculate LPT Issue from AMM asset pair. +/** + * Calculate LPT Issue from AMM asset pair. */ Issue ammLPTIssue(Asset const& asset1, Asset const& asset2, AccountID const& ammAccountID); -/** Validate the amount. +/** + * Validate the amount. * If validZero is false and amount is beast::zero then invalid amount. * Return error code if invalid amount. * If pair then validate amount's issue matches one of the pair's issue. @@ -65,17 +68,20 @@ invalidAMMAssetPair( Asset const& asset2, std::optional> const& pair = std::nullopt); -/** Get time slot of the auction slot. +/** + * Get time slot of the auction slot. */ std::optional ammAuctionTimeSlot(std::uint64_t current, STObject const& auctionSlot); -/** Return true if required AMM amendment is enabled +/** + * Return true if required AMM amendment is enabled */ bool ammEnabled(Rules const&); -/** Convert to the fee from the basis points +/** + * Convert to the fee from the basis points * @param tfee trading fee in {0, 1000} * 1 = 1/10bps or 0.001%, 1000 = 1% */ @@ -85,7 +91,8 @@ getFee(std::uint16_t tfee) return Number{tfee} / kAuctionSlotFeeScaleFactor; } -/** Get fee multiplier (1 - tfee) +/** + * Get fee multiplier (1 - tfee) * @tfee trading fee in basis points */ inline Number @@ -94,7 +101,8 @@ feeMult(std::uint16_t tfee) return 1 - getFee(tfee); } -/** Get fee multiplier (1 - tfee / 2) +/** + * Get fee multiplier (1 - tfee / 2) * @tfee trading fee in basis points */ inline Number diff --git a/include/xrpl/protocol/AccountID.h b/include/xrpl/protocol/AccountID.h index a7d49246ca..ab3c5a996b 100644 --- a/include/xrpl/protocol/AccountID.h +++ b/include/xrpl/protocol/AccountID.h @@ -28,43 +28,53 @@ public: } // namespace detail -/** A 160-bit unsigned that uniquely identifies an account. */ +/** + * A 160-bit unsigned that uniquely identifies an account. + */ using AccountID = BaseUInt<160, detail::AccountIDTag>; -/** Convert AccountID to base58 checked string */ +/** + * Convert AccountID to base58 checked string + */ std::string toBase58(AccountID const& v); -/** Parse AccountID from checked, base58 string. - @return std::nullopt if a parse error occurs -*/ +/** + * Parse AccountID from checked, base58 string. + * @return std::nullopt if a parse error occurs + */ template <> std::optional parseBase58(std::string const& s); -/** Compute AccountID from public key. - - The account ID is computed as the 160-bit hash of the - public key data. This excludes the version byte and - guard bytes included in the base58 representation. - -*/ +/** + * Compute AccountID from public key. + * + * The account ID is computed as the 160-bit hash of the + * public key data. This excludes the version byte and + * guard bytes included in the base58 representation. + */ // VFALCO In PublicKey.h for now // AccountID // calcAccountID (PublicKey const& pk); -/** A special account that's used as the "issuer" for XRP. */ +/** + * A special account that's used as the "issuer" for XRP. + */ AccountID const& xrpAccount(); -/** A placeholder for empty accounts. */ +/** + * A placeholder for empty accounts. + */ AccountID const& noAccount(); -/** Convert hex or base58 string to AccountID. - - @return `true` if the parsing was successful. -*/ +/** + * Convert hex or base58 string to AccountID. + * + * @return `true` if the parsing was successful. + */ // DEPRECATED bool toIssuer(AccountID&, std::string const&); @@ -91,17 +101,18 @@ operator<<(std::ostream& os, AccountID const& x) return os; } -/** Initialize the global cache used to map AccountID to base58 conversions. - - The cache is optional and need not be initialized. But because conversion - is expensive (it requires a SHA-256 operation) in most cases the overhead - of the cache is worth the benefit. - - @param count The number of entries the cache should accommodate. Zero will - disable the cache, releasing any memory associated with it. - - @note The function will only initialize the cache the first time it is - invoked. Subsequent invocations do nothing. +/** + * Initialize the global cache used to map AccountID to base58 conversions. + * + * The cache is optional and need not be initialized. But because conversion + * is expensive (it requires a SHA-256 operation) in most cases the overhead + * of the cache is worth the benefit. + * + * @param count The number of entries the cache should accommodate. Zero will + * disable the cache, releasing any memory associated with it. + * + * @note The function will only initialize the cache the first time it is + * invoked. Subsequent invocations do nothing. */ void initAccountIdCache(std::size_t count); diff --git a/include/xrpl/protocol/Asset.h b/include/xrpl/protocol/Asset.h index 2bf24b19fe..8e9c09eb89 100644 --- a/include/xrpl/protocol/Asset.h +++ b/include/xrpl/protocol/Asset.h @@ -62,7 +62,8 @@ private: public: Asset() = default; - /** Conversions to Asset are implicit and conversions to specific issue + /** + * Conversions to Asset are implicit and conversions to specific issue * type are explicit. This design facilitates the use of Asset. */ Asset(Issue const& issue) : issue_(issue) @@ -149,7 +150,8 @@ public: friend constexpr bool operator==(BadAsset const& lhs, Asset const& rhs); - /** Return true if both assets refer to the same currency (regardless of + /** + * Return true if both assets refer to the same currency (regardless of * issuer) or MPT issuance. Otherwise return false. */ friend constexpr bool diff --git a/include/xrpl/protocol/Book.h b/include/xrpl/protocol/Book.h index 92d1353929..a83eb41b24 100644 --- a/include/xrpl/protocol/Book.h +++ b/include/xrpl/protocol/Book.h @@ -19,10 +19,11 @@ namespace xrpl { -/** Specifies an order book. - The order book is a pair of Issues called in and out. - @see Issue. -*/ +/** + * Specifies an order book. + * The order book is a pair of Issues called in and out. + * @see Issue. + */ class Book final : public CountedObject { public: @@ -60,7 +61,9 @@ hash_append(Hasher& h, Book const& b) Book reversed(Book const& book); -/** Equality comparison. */ +/** + * Equality comparison. + */ /** @{ */ [[nodiscard]] constexpr bool operator==(Book const& lhs, Book const& rhs) @@ -69,7 +72,9 @@ operator==(Book const& lhs, Book const& rhs) } /** @} */ -/** Strict weak ordering. */ +/** + * Strict weak ordering. + */ /** @{ */ [[nodiscard]] constexpr std::weak_ordering operator<=>(Book const& lhs, Book const& rhs) diff --git a/include/xrpl/protocol/BuildInfo.h b/include/xrpl/protocol/BuildInfo.h index a60c37e714..18ba20f23c 100644 --- a/include/xrpl/protocol/BuildInfo.h +++ b/include/xrpl/protocol/BuildInfo.h @@ -4,74 +4,83 @@ #include #include -/** Versioning information for this build. */ +/** + * Versioning information for this build. + */ // VFALCO The namespace is deprecated namespace xrpl::BuildInfo { -/** Server version. - Follows the Semantic Versioning Specification: - http://semver.org/ -*/ +/** + * Server version. + * Follows the Semantic Versioning Specification: + * http://semver.org/ + */ std::string const& getVersionString(); -/** Full server version string. - This includes the name of the server. It is used in the peer - protocol hello message and also the headers of some HTTP replies. -*/ +/** + * Full server version string. + * This includes the name of the server. It is used in the peer + * protocol hello message and also the headers of some HTTP replies. + */ std::string const& getFullVersionString(); -/** Encode an arbitrary server software version in a 64-bit integer. - - The general format is: - - ........-........-........-........-........-........-........-........ - XXXXXXXX-XXXXXXXX-YYYYYYYY-YYYYYYYY-YYYYYYYY-YYYYYYYY-YYYYYYYY-YYYYYYYY - - X: 16 bits identifying the particular implementation - Y: 48 bits of data specific to the implementation - - The xrpld-specific format (implementation ID is: 0x18 0x3B) is: - - 00011000-00111011-MMMMMMMM-mmmmmmmm-pppppppp-TTNNNNNN-00000000-00000000 - - M: 8-bit major version (0-255) - m: 8-bit minor version (0-255) - p: 8-bit patch version (0-255) - T: 11 if neither an RC nor a beta - 10 if an RC - 01 if a beta - N: 6-bit rc/beta number (1-63) - - @param the version string - @return the encoded version in a 64-bit integer -*/ +/** + * Encode an arbitrary server software version in a 64-bit integer. + * + * The general format is: + * + * ........-........-........-........-........-........-........-........ + * XXXXXXXX-XXXXXXXX-YYYYYYYY-YYYYYYYY-YYYYYYYY-YYYYYYYY-YYYYYYYY-YYYYYYYY + * + * X: 16 bits identifying the particular implementation + * Y: 48 bits of data specific to the implementation + * + * The xrpld-specific format (implementation ID is: 0x18 0x3B) is: + * + * 00011000-00111011-MMMMMMMM-mmmmmmmm-pppppppp-TTNNNNNN-00000000-00000000 + * + * M: 8-bit major version (0-255) + * m: 8-bit minor version (0-255) + * p: 8-bit patch version (0-255) + * T: 11 if neither an RC nor a beta + * 10 if an RC + * 01 if a beta + * N: 6-bit rc/beta number (1-63) + * + * @param the version string + * @return the encoded version in a 64-bit integer + */ std::uint64_t encodeSoftwareVersion(std::string_view versionStr); -/** Returns this server's version packed in a 64-bit integer. */ +/** + * Returns this server's version packed in a 64-bit integer. + */ std::uint64_t getEncodedVersion(); -/** Check if the encoded software version is an xrpld software version. - - @param version another node's encoded software version - @return true if the version is an xrpld software version, false otherwise -*/ +/** + * Check if the encoded software version is an xrpld software version. + * + * @param version another node's encoded software version + * @return true if the version is an xrpld software version, false otherwise + */ bool isXrpldVersion(std::uint64_t version); -/** Check if the version is newer than the local node's xrpld software - version. - - @param version another node's encoded software version - @return true if the version is newer than the local node's xrpld software - version, false otherwise. - - @note This function only understands version numbers that are generated by - xrpld. Please see the encodeSoftwareVersion() function for detail. -*/ +/** + * Check if the version is newer than the local node's xrpld software + * version. + * + * @param version another node's encoded software version + * @return true if the version is newer than the local node's xrpld software + * version, false otherwise. + * + * @note This function only understands version numbers that are generated by + * xrpld. Please see the encodeSoftwareVersion() function for detail. + */ bool isNewerVersion(std::uint64_t version); diff --git a/include/xrpl/protocol/ConfidentialTransfer.h b/include/xrpl/protocol/ConfidentialTransfer.h index 325117eed4..ecf7970aba 100644 --- a/include/xrpl/protocol/ConfidentialTransfer.h +++ b/include/xrpl/protocol/ConfidentialTransfer.h @@ -28,7 +28,9 @@ namespace xrpl { */ struct ConfidentialRecipient { - /** @brief The recipient's ElGamal public key (size=xrpl::kEcPubKeyLength). */ + /** + * @brief The recipient's ElGamal public key (size=xrpl::kEcPubKeyLength). + */ Slice publicKey; /** @@ -44,10 +46,14 @@ struct ConfidentialRecipient */ struct EcPair { - /** @brief First ElGamal ciphertext component. */ + /** + * @brief First ElGamal ciphertext component. + */ secp256k1_pubkey c1; - /** @brief Second ElGamal ciphertext component. */ + /** + * @brief Second ElGamal ciphertext component. + */ secp256k1_pubkey c2; }; diff --git a/include/xrpl/protocol/ErrorCodes.h b/include/xrpl/protocol/ErrorCodes.h index 38b8bc6d76..8ac7c8c58f 100644 --- a/include/xrpl/protocol/ErrorCodes.h +++ b/include/xrpl/protocol/ErrorCodes.h @@ -147,10 +147,11 @@ enum ErrorCodeI { RpcLast = RpcUnexpectedLedgerType // rpcLAST should always equal the last code. }; -/** Codes returned in the `warnings` array of certain RPC commands. - - These values need to remain stable. -*/ +/** + * Codes returned in the `warnings` array of certain RPC commands. + * + * These values need to remain stable. + */ // Protocol-wide, 50+ files // NOLINTNEXTLINE(cppcoreguidelines-use-enum-class) enum WarningCodeI { @@ -168,7 +169,9 @@ enum WarningCodeI { namespace RPC { -/** Maps an rpc error code to its token, default message, and HTTP status. */ +/** + * Maps an rpc error code to its token, default message, and HTTP status. + */ struct ErrorInfo { // Default ctor needed to produce an empty std::array during constexpr eval. @@ -193,11 +196,15 @@ struct ErrorInfo int httpStatus; }; -/** Returns an ErrorInfo that reflects the error code. */ +/** + * Returns an ErrorInfo that reflects the error code. + */ ErrorInfo const& getErrorInfo(ErrorCodeI code); -/** Add or update the json update to reflect the error code. */ +/** + * Add or update the json update to reflect the error code. + */ /** @{ */ void injectError(ErrorCodeI code, json::Value& json); @@ -206,7 +213,9 @@ void injectError(ErrorCodeI code, std::string const& message, json::Value& json); /** @} */ -/** Returns a new json object that reflects the error code. */ +/** + * Returns a new json object that reflects the error code. + */ /** @{ */ json::Value makeError(ErrorCodeI code); @@ -214,7 +223,9 @@ json::Value makeError(ErrorCodeI code, std::string const& message); /** @} */ -/** Returns a new json object that indicates invalid parameters. */ +/** + * Returns a new json object that indicates invalid parameters. + */ /** @{ */ inline json::Value makeParamError(std::string const& message) @@ -314,17 +325,23 @@ notValidatorError() /** @} */ -/** Returns `true` if the json contains an rpc error specification. */ +/** + * Returns `true` if the json contains an rpc error specification. + */ bool containsError(json::Value const& json); -/** Returns http status that corresponds to the error code. */ +/** + * Returns http status that corresponds to the error code. + */ int errorCodeHttpStatus(ErrorCodeI code); } // namespace RPC -/** Returns a single string with the contents of an RPC error. */ +/** + * Returns a single string with the contents of an RPC error. + */ std::string rpcErrorString(json::Value const& jv); diff --git a/include/xrpl/protocol/Feature.h b/include/xrpl/protocol/Feature.h index 927fde542a..f15b7e2d3f 100644 --- a/include/xrpl/protocol/Feature.h +++ b/include/xrpl/protocol/Feature.h @@ -112,7 +112,9 @@ validFeatureName(auto fn) -> bool enum class VoteBehavior : int { Obsolete = -1, DefaultNo = 0, DefaultYes = 1 }; enum class AmendmentSupport : int { Retired = -1, Supported = 0, Unsupported = 1 }; -/** All amendments libxrpl knows about. */ +/** + * All amendments libxrpl knows about. + */ std::map const& allAmendments(); @@ -152,23 +154,27 @@ static constexpr std::size_t kNumFeatures = #undef XRPL_FEATURE #pragma pop_macro("XRPL_FEATURE") -/** Amendments that this server supports and the default voting behavior. - Whether they are enabled depends on the Rules defined in the validated - ledger */ +/** + * Amendments that this server supports and the default voting behavior. + * Whether they are enabled depends on the Rules defined in the validated + * ledger + */ std::map const& supportedAmendments(); -/** Amendments that this server won't vote for by default. - - This function is only used in unit tests. -*/ +/** + * Amendments that this server won't vote for by default. + * + * This function is only used in unit tests. + */ std::size_t numDownVotedAmendments(); -/** Amendments that this server will vote for by default. - - This function is only used in unit tests. -*/ +/** + * Amendments that this server will vote for by default. + * + * This function is only used in unit tests. + */ std::size_t numUpVotedAmendments(); diff --git a/include/xrpl/protocol/Fees.h b/include/xrpl/protocol/Fees.h index 2f9159cd9b..fdfadcd8fd 100644 --- a/include/xrpl/protocol/Fees.h +++ b/include/xrpl/protocol/Fees.h @@ -10,20 +10,27 @@ namespace xrpl { // This was the reference fee units used in the old fee calculation. inline constexpr std::uint32_t kFeeUnitsDeprecated = 10; -/** Reflects the fee settings for a particular ledger. - - The fees are always the same for any transactions applied - to a ledger. Changes to fees occur in between ledgers. -*/ +/** + * Reflects the fee settings for a particular ledger. + * + * The fees are always the same for any transactions applied + * to a ledger. Changes to fees occur in between ledgers. + */ struct Fees { - /** @brief Cost of a reference transaction in drops. */ + /** + * @brief Cost of a reference transaction in drops. + */ XRPAmount base{0}; - /** @brief Minimum XRP an account must hold to exist on the ledger. */ + /** + * @brief Minimum XRP an account must hold to exist on the ledger. + */ XRPAmount reserve{0}; - /** @brief Additional XRP reserve required per owned ledger object. */ + /** + * @brief Additional XRP reserve required per owned ledger object. + */ XRPAmount increment{0}; explicit Fees() = default; @@ -36,11 +43,12 @@ struct Fees { } - /** Returns the account reserve given the owner count, in drops. - - The reserve is calculated as the reserve base times the number of accounts plus the reserve - increment times the number of increments. - */ + /** + * Returns the account reserve given the owner count, in drops. + * + * The reserve is calculated as the reserve base times the number of accounts plus the reserve + * increment times the number of increments. + */ [[nodiscard]] XRPAmount accountReserve(std::uint32_t ownerCount, std::uint32_t accountCount) const { diff --git a/include/xrpl/protocol/HashPrefix.h b/include/xrpl/protocol/HashPrefix.h index 1b05d450a1..9d4471d05c 100644 --- a/include/xrpl/protocol/HashPrefix.h +++ b/include/xrpl/protocol/HashPrefix.h @@ -17,55 +17,80 @@ makeHashPrefix(char a, char b, char c) } // namespace detail -/** Prefix for hashing functions. - - These prefixes are inserted before the source material used to generate - various hashes. This is done to put each hash in its own "space." This way, - two different types of objects with the same binary data will produce - different hashes. - - Each prefix is a 4-byte value with the last byte set to zero and the first - three bytes formed from the ASCII equivalent of some arbitrary string. For - example "TXN". - - @note Hash prefixes are part of the protocol; you cannot, arbitrarily, - change the type or the value of any of these without causing breakage. -*/ +/** + * Prefix for hashing functions. + * + * These prefixes are inserted before the source material used to generate + * various hashes. This is done to put each hash in its own "space." This way, + * two different types of objects with the same binary data will produce + * different hashes. + * + * Each prefix is a 4-byte value with the last byte set to zero and the first + * three bytes formed from the ASCII equivalent of some arbitrary string. For + * example "TXN". + * + * @note Hash prefixes are part of the protocol; you cannot, arbitrarily, + * change the type or the value of any of these without causing breakage. + */ enum class HashPrefix : std::uint32_t { - /** transaction plus signature to give transaction ID */ + /** + * transaction plus signature to give transaction ID + */ TransactionId = detail::makeHashPrefix('T', 'X', 'N'), - /** transaction plus metadata */ + /** + * transaction plus metadata + */ TxNode = detail::makeHashPrefix('S', 'N', 'D'), - /** account state */ + /** + * account state + */ LeafNode = detail::makeHashPrefix('M', 'L', 'N'), - /** inner node in V1 tree */ + /** + * inner node in V1 tree + */ InnerNode = detail::makeHashPrefix('M', 'I', 'N'), - /** ledger master data for signing */ + /** + * ledger master data for signing + */ LedgerMaster = detail::makeHashPrefix('L', 'W', 'R'), - /** inner transaction to sign */ + /** + * inner transaction to sign + */ TxSign = detail::makeHashPrefix('S', 'T', 'X'), - /** inner transaction to multi-sign */ + /** + * inner transaction to multi-sign + */ TxMultiSign = detail::makeHashPrefix('S', 'M', 'T'), - /** validation for signing */ + /** + * validation for signing + */ Validation = detail::makeHashPrefix('V', 'A', 'L'), - /** proposal for signing */ + /** + * proposal for signing + */ Proposal = detail::makeHashPrefix('P', 'R', 'P'), - /** Manifest */ + /** + * Manifest + */ Manifest = detail::makeHashPrefix('M', 'A', 'N'), - /** Payment Channel Claim */ + /** + * Payment Channel Claim + */ PaymentChannelClaim = detail::makeHashPrefix('C', 'L', 'M'), - /** Batch */ + /** + * Batch + */ Batch = detail::makeHashPrefix('B', 'C', 'H'), }; diff --git a/include/xrpl/protocol/IOUAmount.h b/include/xrpl/protocol/IOUAmount.h index 186ce054f1..060ad3d828 100644 --- a/include/xrpl/protocol/IOUAmount.h +++ b/include/xrpl/protocol/IOUAmount.h @@ -11,16 +11,17 @@ namespace xrpl { -/** Floating point representation of amounts with high dynamic range - - Amounts are stored as a normalized signed mantissa and an exponent. The - range of the normalized exponent is [-96,80] and the range of the absolute - value of the normalized mantissa is [1000000000000000, 9999999999999999]. - - Arithmetic operations can throw std::overflow_error during normalization - if the amount exceeds the largest representable amount, but underflows - will silently truncate to zero. -*/ +/** + * Floating point representation of amounts with high dynamic range + * + * Amounts are stored as a normalized signed mantissa and an exponent. The + * range of the normalized exponent is [-96,80] and the range of the absolute + * value of the normalized mantissa is [1000000000000000, 9999999999999999]. + * + * Arithmetic operations can throw std::overflow_error during normalization + * if the amount exceeds the largest representable amount, but underflows + * will silently truncate to zero. + */ class IOUAmount : private boost::totally_ordered, private boost::additive { private: @@ -29,12 +30,13 @@ private: mantissa_type mantissa_{}; exponent_type exponent_{}; - /** Adjusts the mantissa and exponent to the proper range. - - This can throw if the amount cannot be normalized, or is larger than - the largest value that can be represented as an IOU amount. Amounts - that are too small to be represented normalize to 0. - */ + /** + * Adjusts the mantissa and exponent to the proper range. + * + * This can throw if the amount cannot be normalized, or is larger than + * the largest value that can be represented as an IOU amount. Amounts + * that are too small to be represented normalize to 0. + */ void normalize(); @@ -66,11 +68,15 @@ public: bool operator<(IOUAmount const& other) const; - /** Returns true if the amount is not zero */ + /** + * Returns true if the amount is not zero + */ explicit operator bool() const noexcept; - /** Return the sign of the amount */ + /** + * Return the sign of the amount + */ [[nodiscard]] int signum() const noexcept; diff --git a/include/xrpl/protocol/Indexes.h b/include/xrpl/protocol/Indexes.h index 79ecd05ad5..5a76119649 100644 --- a/include/xrpl/protocol/Indexes.h +++ b/include/xrpl/protocol/Indexes.h @@ -24,74 +24,90 @@ namespace xrpl { class SeqProxy; -/** Keylet computation functions. - - Entries in the ledger are located using 256-bit locators. The locators are - calculated using a wide range of parameters specific to the entry whose - locator we are calculating (e.g. an account's locator is derived from the - account's address, whereas the locator for an offer is derived from the - account and the offer sequence.) - - To enhance type safety during lookup and make the code more robust, we use - keylets, which contain not only the locator of the object but also the type - of the object being referenced. - - These functions each return a type-specific keylet. -*/ +/** + * Keylet computation functions. + * + * Entries in the ledger are located using 256-bit locators. The locators are + * calculated using a wide range of parameters specific to the entry whose + * locator we are calculating (e.g. an account's locator is derived from the + * account's address, whereas the locator for an offer is derived from the + * account and the offer sequence.) + * + * To enhance type safety during lookup and make the code more robust, we use + * keylets, which contain not only the locator of the object but also the type + * of the object being referenced. + * + * These functions each return a type-specific keylet. + */ namespace keylet { -/** AccountID root */ +/** + * AccountID root + */ Keylet account(AccountID const& id) noexcept; -/** The index of the amendment table */ +/** + * The index of the amendment table + */ Keylet const& amendments() noexcept; -/** Any item that can be in an owner dir. */ +/** + * Any item that can be in an owner dir. + */ Keylet child(uint256 const& key) noexcept; -/** The index of the "short" skip list - - The "short" skip list is a node (at a fixed index) that holds the hashes - of ledgers since the last flag ledger. It will contain, at most, 256 hashes. -*/ +/** + * The index of the "short" skip list + * + * The "short" skip list is a node (at a fixed index) that holds the hashes + * of ledgers since the last flag ledger. It will contain, at most, 256 hashes. + */ Keylet const& skip() noexcept; -/** The index of the long skip for a particular ledger range. - - The "long" skip list is a node that holds the hashes of (up to) 256 flag - ledgers. - - It can be used to efficiently skip back to any ledger using only two hops: - the first hop gets the "long" skip list for the ledger it wants to retrieve - and uses it to get the hash of the flag ledger whose short skip list will - contain the hash of the requested ledger. -*/ +/** + * The index of the long skip for a particular ledger range. + * + * The "long" skip list is a node that holds the hashes of (up to) 256 flag + * ledgers. + * + * It can be used to efficiently skip back to any ledger using only two hops: + * the first hop gets the "long" skip list for the ledger it wants to retrieve + * and uses it to get the hash of the flag ledger whose short skip list will + * contain the hash of the requested ledger. + */ Keylet skip(LedgerIndex ledger) noexcept; -/** The (fixed) index of the object containing the ledger fees. */ +/** + * The (fixed) index of the object containing the ledger fees. + */ Keylet const& feeSettings() noexcept; -/** The (fixed) index of the object containing the ledger negativeUNL. */ +/** + * The (fixed) index of the object containing the ledger negativeUNL. + */ Keylet const& negativeUNL() noexcept; -/** The beginning of an order book */ +/** + * The beginning of an order book + */ Keylet book(Book const& b); -/** The index of a trust line for a given currency - - Note that a trustline is *shared* between two accounts (commonly referred - to as the issuer and the holder); if Alice sets up a trust line to Bob for - BTC, and Bob trusts Alice for BTC, here is only a single BTC trust line - between them. -*/ +/** + * The index of a trust line for a given currency + * + * Note that a trustline is *shared* between two accounts (commonly referred + * to as the issuer and the holder); if Alice sets up a trust line to Bob for + * BTC, and Bob trusts Alice for BTC, here is only a single BTC trust line + * between them. + */ /** @{ */ Keylet trustLine(AccountID const& id0, AccountID const& id1, Currency const& currency) noexcept; @@ -103,7 +119,9 @@ trustLine(AccountID const& id, Issue const& issue) noexcept } /** @} */ -/** An offer from an account */ +/** + * An offer from an account + */ /** @{ */ Keylet offer(AccountID const& id, std::uint32_t seq) noexcept; @@ -115,15 +133,21 @@ offer(uint256 const& key) noexcept } /** @} */ -/** The initial directory page for a specific quality */ +/** + * The initial directory page for a specific quality + */ Keylet quality(Keylet const& k, std::uint64_t q) noexcept; -/** The directory for the next lower quality */ +/** + * The directory for the next lower quality + */ Keylet next(Keylet const& k); -/** A ticket belonging to an account */ +/** + * A ticket belonging to an account + */ /** @{ */ Keylet ticket(AccountID const& id, std::uint32_t ticketSeq); @@ -138,15 +162,21 @@ ticket(uint256 const& key) } /** @} */ -/** A SignerList */ +/** + * A SignerList + */ Keylet signerList(AccountID const& account) noexcept; -/** A Sponsorship */ +/** + * A Sponsorship + */ Keylet sponsorship(AccountID const& sponsor, AccountID const& sponsee) noexcept; -/** A Check */ +/** + * A Check + */ /** @{ */ Keylet check(AccountID const& id, std::uint32_t seq) noexcept; @@ -158,7 +188,9 @@ check(uint256 const& key) noexcept } /** @} */ -/** A DepositPreauth */ +/** + * A DepositPreauth + */ /** @{ */ Keylet depositPreauth(AccountID const& owner, AccountID const& preauthorized) noexcept; @@ -177,15 +209,21 @@ depositPreauth(uint256 const& key) noexcept //------------------------------------------------------------------------------ -/** Any ledger entry */ +/** + * Any ledger entry + */ Keylet unchecked(uint256 const& key) noexcept; -/** The root page of an account's directory */ +/** + * The root page of an account's directory + */ Keylet ownerDir(AccountID const& id) noexcept; -/** A page in a directory */ +/** + * A page in a directory + */ /** @{ */ Keylet page(uint256 const& root, std::uint64_t index = 0) noexcept; @@ -198,27 +236,36 @@ page(Keylet const& root, std::uint64_t index = 0) noexcept } /** @} */ -/** An escrow entry */ +/** + * An escrow entry + */ Keylet escrow(AccountID const& src, std::uint32_t seq) noexcept; -/** A PaymentChannel */ +/** + * A PaymentChannel + */ Keylet payChannel(AccountID const& src, AccountID const& dst, std::uint32_t seq) noexcept; -/** NFT page keylets - - Unlike objects whose ledger identifiers are produced by hashing data, - NFT page identifiers are composite identifiers, consisting of the owner's - 160-bit AccountID, followed by a 96-bit value that determines which NFT - tokens are candidates for that page. +/** + * NFT page keylets + * + * Unlike objects whose ledger identifiers are produced by hashing data, + * NFT page identifiers are composite identifiers, consisting of the owner's + * 160-bit AccountID, followed by a 96-bit value that determines which NFT + * tokens are candidates for that page. */ /** @{ */ -/** A keylet for the owner's first possible NFT page. */ +/** + * A keylet for the owner's first possible NFT page. + */ Keylet nftokenPageMin(AccountID const& owner); -/** A keylet for the owner's last possible NFT page. */ +/** + * A keylet for the owner's last possible NFT page. + */ Keylet nftokenPageMax(AccountID const& owner); @@ -226,7 +273,9 @@ Keylet nftokenPage(Keylet const& k, uint256 const& token); /** @} */ -/** An offer from an account to buy or sell an NFT */ +/** + * An offer from an account to buy or sell an NFT + */ Keylet nftokenOffer(AccountID const& owner, std::uint32_t seq); @@ -236,22 +285,30 @@ nftokenOffer(uint256 const& offer) return {ltNFTOKEN_OFFER, offer}; } -/** The directory of buy offers for the specified NFT */ +/** + * The directory of buy offers for the specified NFT + */ Keylet nftBuys(uint256 const& id) noexcept; -/** The directory of sell offers for the specified NFT */ +/** + * The directory of sell offers for the specified NFT + */ Keylet nftSells(uint256 const& id) noexcept; -/** AMM entry */ +/** + * AMM entry + */ Keylet amm(Asset const& issue1, Asset const& issue2) noexcept; Keylet amm(uint256 const& amm) noexcept; -/** A keylet for Delegate object */ +/** + * A keylet for Delegate object + */ Keylet delegate(AccountID const& account, AccountID const& authorizedAccount) noexcept; diff --git a/include/xrpl/protocol/InnerObjectFormats.h b/include/xrpl/protocol/InnerObjectFormats.h index c8312c3701..7364e83cfd 100644 --- a/include/xrpl/protocol/InnerObjectFormats.h +++ b/include/xrpl/protocol/InnerObjectFormats.h @@ -6,14 +6,16 @@ namespace xrpl { -/** Manages the list of known inner object formats. +/** + * Manages the list of known inner object formats. */ class InnerObjectFormats : public KnownFormats { private: - /** Create the object. - This will load the object with all the known inner object formats. - */ + /** + * Create the object. + * This will load the object with all the known inner object formats. + */ InnerObjectFormats(); public: diff --git a/include/xrpl/protocol/Issue.h b/include/xrpl/protocol/Issue.h index a4980404cd..5cd8731609 100644 --- a/include/xrpl/protocol/Issue.h +++ b/include/xrpl/protocol/Issue.h @@ -10,9 +10,10 @@ namespace xrpl { -/** A currency issued by an account. - @see Currency, AccountID, Issue, Book -*/ +/** + * A currency issued by an account. + * @see Currency, AccountID, Issue, Book + */ class Issue { public: @@ -70,7 +71,9 @@ hash_append(Hasher& h, Issue const& r) hash_append(h, r.currency, r.account); } -/** Equality comparison. */ +/** + * Equality comparison. + */ /** @{ */ [[nodiscard]] constexpr bool operator==(Issue const& lhs, Issue const& rhs) @@ -79,7 +82,9 @@ operator==(Issue const& lhs, Issue const& rhs) } /** @} */ -/** Strict weak ordering. */ +/** + * Strict weak ordering. + */ /** @{ */ [[nodiscard]] constexpr std::weak_ordering operator<=>(Issue const& lhs, Issue const& rhs) @@ -96,7 +101,9 @@ operator<=>(Issue const& lhs, Issue const& rhs) //------------------------------------------------------------------------------ -/** Returns an asset specifier that represents XRP. */ +/** + * Returns an asset specifier that represents XRP. + */ inline Issue const& xrpIssue() { @@ -104,7 +111,9 @@ xrpIssue() return kIssue; } -/** Returns an asset specifier that represents no account and currency. */ +/** + * Returns an asset specifier that represents no account and currency. + */ inline Issue const& noIssue() { diff --git a/include/xrpl/protocol/Keylet.h b/include/xrpl/protocol/Keylet.h index 19704e2a11..48d494f564 100644 --- a/include/xrpl/protocol/Keylet.h +++ b/include/xrpl/protocol/Keylet.h @@ -7,14 +7,15 @@ namespace xrpl { class STLedgerEntry; -/** A pair of SHAMap key and LedgerEntryType. - - A Keylet identifies both a key in the state map - and its ledger entry type. - - @note Keylet is a portmanteau of the words key - and LET, an acronym for LedgerEntryType. -*/ +/** + * A pair of SHAMap key and LedgerEntryType. + * + * A Keylet identifies both a key in the state map + * and its ledger entry type. + * + * @note Keylet is a portmanteau of the words key + * and LET, an acronym for LedgerEntryType. + */ struct Keylet { uint256 key; @@ -24,7 +25,9 @@ struct Keylet { } - /** Returns true if the SLE matches the type */ + /** + * Returns true if the SLE matches the type + */ [[nodiscard]] bool check(STLedgerEntry const&) const; }; diff --git a/include/xrpl/protocol/KnownFormats.h b/include/xrpl/protocol/KnownFormats.h index c31e28c37d..385feb2c27 100644 --- a/include/xrpl/protocol/KnownFormats.h +++ b/include/xrpl/protocol/KnownFormats.h @@ -15,18 +15,20 @@ namespace xrpl { -/** Manages a list of known formats. - - Each format has a name, an associated KeyType (typically an enumeration), - and a predefined @ref SOElement. - - @tparam KeyType The type of key identifying the format. -*/ +/** + * Manages a list of known formats. + * + * Each format has a name, an associated KeyType (typically an enumeration), + * and a predefined @ref SOElement. + * + * @tparam KeyType The type of key identifying the format. + */ template class KnownFormats { public: - /** A known format. + /** + * A known format. */ class Item { @@ -46,7 +48,8 @@ public: "KnownFormats KeyType must be integral or enum."); } - /** Retrieve the name of the format. + /** + * Retrieve the name of the format. */ [[nodiscard]] std::string const& getName() const @@ -54,7 +57,8 @@ public: return name_; } - /** Retrieve the transaction type this format represents. + /** + * Retrieve the transaction type this format represents. */ [[nodiscard]] KeyType getType() const @@ -74,32 +78,35 @@ public: KeyType const type_; }; - /** Create the known formats object. - - Derived classes will load the object with all the known formats. - */ + /** + * Create the known formats object. + * + * Derived classes will load the object with all the known formats. + */ private: KnownFormats() : name_(beast::typeName()) { } public: - /** Destroy the known formats object. - - The defined formats are deleted. - */ + /** + * Destroy the known formats object. + * + * The defined formats are deleted. + */ virtual ~KnownFormats() = default; KnownFormats(KnownFormats const&) = delete; KnownFormats& operator=(KnownFormats const&) = delete; - /** Retrieve the type for a format specified by name. - - If the format name is unknown, an exception is thrown. - - @param name The name of the type. - @return The type. - */ + /** + * Retrieve the type for a format specified by name. + * + * If the format name is unknown, an exception is thrown. + * + * @param name The name of the type. + * @return The type. + */ [[nodiscard]] KeyType findTypeByName(std::string const& name) const { @@ -110,7 +117,8 @@ public: name.substr(0, std::min(name.size(), std::size_t(32))) + "'"); } - /** Retrieve a format based on its type. + /** + * Retrieve a format based on its type. */ [[nodiscard]] Item const* findByType(KeyType type) const @@ -135,7 +143,8 @@ public: } protected: - /** Retrieve a format based on its name. + /** + * Retrieve a format based on its name. */ [[nodiscard]] Item const* findByName(std::string const& name) const @@ -146,15 +155,16 @@ protected: return itr->second; } - /** Add a new format. - - @param name The name of this format. - @param type The type of this format. - @param uniqueFields A std::vector of unique fields - @param commonFields A std::vector of common fields - - @return The created format. - */ + /** + * Add a new format. + * + * @param name The name of this format. + * @param type The type of this format. + * @param uniqueFields A std::vector of unique fields + * @param commonFields A std::vector of common fields + * + * @return The created format. + */ Item const& add(char const* name, KeyType type, diff --git a/include/xrpl/protocol/LedgerFormats.h b/include/xrpl/protocol/LedgerFormats.h index 142e5763be..7c504f6bdd 100644 --- a/include/xrpl/protocol/LedgerFormats.h +++ b/include/xrpl/protocol/LedgerFormats.h @@ -12,28 +12,29 @@ #include namespace xrpl { -/** Identifiers for on-ledger objects. - - Each ledger object requires a unique type identifier, which is stored within the object itself; - this makes it possible to iterate the entire ledger and determine each object's type and verify - that the object you retrieved from a given hash matches the expected type. - - @warning Since these values are stored inside objects stored on the ledger they are part of the - protocol. - **Changing them should be avoided because without special handling, this will result in a hard - fork.** - - @note Values outside this range may be used internally by the code for various purposes, but - attempting to use such values to identify on-ledger objects will result in an invariant failure. - - @note When retiring types, the specific values should not be removed but should be marked as - [[deprecated]]. This is to avoid accidental reuse of identifiers. - - @todo The C++ language does not enable checking for duplicate values here. - If it becomes possible then we should do this. - - @ingroup protocol -*/ +/** + * Identifiers for on-ledger objects. + * + * Each ledger object requires a unique type identifier, which is stored within the object itself; + * this makes it possible to iterate the entire ledger and determine each object's type and verify + * that the object you retrieved from a given hash matches the expected type. + * + * @warning Since these values are stored inside objects stored on the ledger they are part of the + * protocol. + * **Changing them should be avoided because without special handling, this will result in a hard + * fork.** + * + * @note Values outside this range may be used internally by the code for various purposes, but + * attempting to use such values to identify on-ledger objects will result in an invariant failure. + * + * @note When retiring types, the specific values should not be removed but should be marked as + * [[deprecated]]. This is to avoid accidental reuse of identifiers. + * + * @todo The C++ language does not enable checking for duplicate values here. + * If it becomes possible then we should do this. + * + * @ingroup protocol + */ // Protocol-critical, hundreds of usages // NOLINTNEXTLINE(cppcoreguidelines-use-enum-class) enum LedgerEntryType : std::uint16_t { @@ -49,66 +50,72 @@ enum LedgerEntryType : std::uint16_t { #pragma pop_macro("LEDGER_ENTRY") //--------------------------------------------------------------------------- - /** A special type, matching any ledger entry type. - - The value does not represent a concrete type, but rather is used in contexts where the - specific type of a ledger object is unimportant, unknown or unavailable. - - Objects with this special type cannot be created or stored on the ledger. - - \sa keylet::unchecked - */ + /** + * A special type, matching any ledger entry type. + * + * The value does not represent a concrete type, but rather is used in contexts where the + * specific type of a ledger object is unimportant, unknown or unavailable. + * + * Objects with this special type cannot be created or stored on the ledger. + * + * @see keylet::unchecked + */ ltANY = 0, - /** A special type, matching any ledger type except directory nodes. - - The value does not represent a concrete type, but rather is used in contexts where the - ledger object must not be a directory node but its specific type is otherwise unimportant, - unknown or unavailable. - - Objects with this special type cannot be created or stored on the ledger. - - \sa keylet::child + /** + * A special type, matching any ledger type except directory nodes. + * + * The value does not represent a concrete type, but rather is used in contexts where the + * ledger object must not be a directory node but its specific type is otherwise unimportant, + * unknown or unavailable. + * + * Objects with this special type cannot be created or stored on the ledger. + * + * @see keylet::child */ ltCHILD = 0x1CD2, //--------------------------------------------------------------------------- - /** A legacy, deprecated type. - - \deprecated **This object type is not supported and should not be used.** - Support for this type of object was never implemented. - No objects of this type were ever created. + /** + * A legacy, deprecated type. + * + * @deprecated **This object type is not supported and should not be used.** + * Support for this type of object was never implemented. + * No objects of this type were ever created. */ ltNICKNAME [[deprecated("This object type is not supported and should not be used.")]] = 0x006e, - /** A legacy, deprecated type. - - \deprecated **This object type is not supported and should not be used.** - Support for this type of object was never implemented. - No objects of this type were ever created. + /** + * A legacy, deprecated type. + * + * @deprecated **This object type is not supported and should not be used.** + * Support for this type of object was never implemented. + * No objects of this type were ever created. */ ltCONTRACT [[deprecated("This object type is not supported and should not be used.")]] = 0x0063, - /** A legacy, deprecated type. - - \deprecated **This object type is not supported and should not be used.** - Support for this type of object was never implemented. - No objects of this type were ever created. + /** + * A legacy, deprecated type. + * + * @deprecated **This object type is not supported and should not be used.** + * Support for this type of object was never implemented. + * No objects of this type were ever created. */ ltGENERATOR_MAP [[deprecated("This object type is not supported and should not be used.")]] = 0x0067, }; -/** Ledger object flags. - - These flags are specified in ledger objects and modify their behavior. - - @warning Ledger object flags form part of the protocol. - **Changing them should be avoided because without special handling, this will result in a hard - fork.** - - @ingroup protocol -*/ +/** + * Ledger object flags. + * + * These flags are specified in ledger objects and modify their behavior. + * + * @warning Ledger object flags form part of the protocol. + * **Changing them should be avoided because without special handling, this will result in a hard + * fork.** + * + * @ingroup protocol + */ #pragma push_macro("XMACRO") #pragma push_macro("TO_VALUE") #pragma push_macro("VALUE_TO_MAP") @@ -289,14 +296,16 @@ getAllLedgerFlags() //------------------------------------------------------------------------------ -/** Holds the list of known ledger entry formats. +/** + * Holds the list of known ledger entry formats. */ class LedgerFormats : public KnownFormats { private: - /** Create the object. - This will load the object with all the known ledger formats. - */ + /** + * Create the object. + * This will load the object with all the known ledger formats. + */ LedgerFormats(); public: diff --git a/include/xrpl/protocol/LedgerHeader.h b/include/xrpl/protocol/LedgerHeader.h index df8f314c5f..d169e53e2c 100644 --- a/include/xrpl/protocol/LedgerHeader.h +++ b/include/xrpl/protocol/LedgerHeader.h @@ -12,7 +12,9 @@ namespace xrpl { -/** Information about the notional ledger backing the view. */ +/** + * Information about the notional ledger backing the view. + */ struct LedgerHeader { explicit LedgerHeader() = default; @@ -67,15 +69,21 @@ getCloseAgree(LedgerHeader const& info) void addRaw(LedgerHeader const&, Serializer&, bool includeHash = false); -/** Deserialize a ledger header from a byte array. */ +/** + * Deserialize a ledger header from a byte array. + */ LedgerHeader deserializeHeader(Slice data, bool hasHash = false); -/** Deserialize a ledger header (prefixed with 4 bytes) from a byte array. */ +/** + * Deserialize a ledger header (prefixed with 4 bytes) from a byte array. + */ LedgerHeader deserializePrefixedHeader(Slice data, bool hasHash = false); -/** Calculate the hash of a ledger header. */ +/** + * Calculate the hash of a ledger header. + */ uint256 calculateLedgerHash(LedgerHeader const& info); diff --git a/include/xrpl/protocol/LedgerShortcut.h b/include/xrpl/protocol/LedgerShortcut.h index 68c31c4c3c..037621121d 100644 --- a/include/xrpl/protocol/LedgerShortcut.h +++ b/include/xrpl/protocol/LedgerShortcut.h @@ -9,13 +9,19 @@ namespace xrpl { * without needing to specify their exact hash or sequence number. */ enum class LedgerShortcut { - /** The current working ledger (open, not yet closed) */ + /** + * The current working ledger (open, not yet closed) + */ Current, - /** The most recently closed ledger (may not be validated) */ + /** + * The most recently closed ledger (may not be validated) + */ Closed, - /** The most recently validated ledger */ + /** + * The most recently validated ledger + */ Validated }; diff --git a/include/xrpl/protocol/MPTAmount.h b/include/xrpl/protocol/MPTAmount.h index 329d83610e..462092f7dd 100644 --- a/include/xrpl/protocol/MPTAmount.h +++ b/include/xrpl/protocol/MPTAmount.h @@ -60,7 +60,9 @@ public: bool operator<(MPTAmount const& other) const; - /** Returns true if the amount is not zero */ + /** + * Returns true if the amount is not zero + */ explicit constexpr operator bool() const noexcept; @@ -69,14 +71,17 @@ public: return value(); } - /** Return the sign of the amount */ + /** + * Return the sign of the amount + */ [[nodiscard]] constexpr int signum() const noexcept; - /** Returns the underlying value. Code SHOULD NOT call this - function unless the type has been abstracted away, - e.g. in a templated function. - */ + /** + * Returns the underlying value. Code SHOULD NOT call this + * function unless the type has been abstracted away, + * e.g. in a templated function. + */ [[nodiscard]] constexpr value_type value() const; @@ -100,14 +105,18 @@ MPTAmount::operator=(beast::Zero) return *this; } -/** Returns true if the amount is not zero */ +/** + * Returns true if the amount is not zero + */ constexpr MPTAmount:: operator bool() const noexcept { return value_ != 0; } -/** Return the sign of the amount */ +/** + * Return the sign of the amount + */ constexpr int MPTAmount::signum() const noexcept { @@ -116,10 +125,11 @@ MPTAmount::signum() const noexcept return (value_ != 0) ? 1 : 0; } -/** Returns the underlying value. Code SHOULD NOT call this - function unless the type has been abstracted away, - e.g. in a templated function. -*/ +/** + * Returns the underlying value. Code SHOULD NOT call this + * function unless the type has been abstracted away, + * e.g. in a templated function. + */ constexpr MPTAmount::value_type MPTAmount::value() const { diff --git a/include/xrpl/protocol/MPTIssue.h b/include/xrpl/protocol/MPTIssue.h index 0c495aa57f..7f473da6a2 100644 --- a/include/xrpl/protocol/MPTIssue.h +++ b/include/xrpl/protocol/MPTIssue.h @@ -82,7 +82,8 @@ operator<=>(MPTIssue const& lhs, MPTIssue const& rhs) return lhs.mptID_ <=> rhs.mptID_; } -/** MPT is a non-native token. +/** + * MPT is a non-native token. */ inline bool isXRP(MPTID const&) diff --git a/include/xrpl/protocol/NFTSyntheticSerializer.h b/include/xrpl/protocol/NFTSyntheticSerializer.h index a1d8bce985..bef05b9a8f 100644 --- a/include/xrpl/protocol/NFTSyntheticSerializer.h +++ b/include/xrpl/protocol/NFTSyntheticSerializer.h @@ -9,10 +9,9 @@ namespace xrpl::RPC { /** - Adds common synthetic fields to transaction-related JSON responses - - @{ + * Adds common synthetic fields to transaction-related JSON responses */ +/** @{ */ void insertNFTSyntheticInJson(json::Value&, std::shared_ptr const&, TxMeta const&); /** @} */ diff --git a/include/xrpl/protocol/NFTokenID.h b/include/xrpl/protocol/NFTokenID.h index f61c6bd5cb..b1b994eabd 100644 --- a/include/xrpl/protocol/NFTokenID.h +++ b/include/xrpl/protocol/NFTokenID.h @@ -12,13 +12,13 @@ namespace xrpl { /** - Add a `nftoken_ids` field to the `meta` output parameter. - The field is only added to successful NFTokenMint, NFTokenAcceptOffer, - and NFTokenCancelOffer transactions. - - Helper functions are not static because they can be used by Clio. - @{ + * Add a `nftoken_ids` field to the `meta` output parameter. + * The field is only added to successful NFTokenMint, NFTokenAcceptOffer, + * and NFTokenCancelOffer transactions. + * + * Helper functions are not static because they can be used by Clio. */ +/** @{ */ bool canHaveNFTokenID(std::shared_ptr const& serializedTx, TxMeta const& transactionMeta); diff --git a/include/xrpl/protocol/NFTokenOfferID.h b/include/xrpl/protocol/NFTokenOfferID.h index c4a80356bf..4810f7932a 100644 --- a/include/xrpl/protocol/NFTokenOfferID.h +++ b/include/xrpl/protocol/NFTokenOfferID.h @@ -11,12 +11,12 @@ namespace xrpl { /** - Add an `offer_id` field to the `meta` output parameter. - The field is only added to successful NFTokenCreateOffer transactions. - - Helper functions are not static because they can be used by Clio. - @{ + * Add an `offer_id` field to the `meta` output parameter. + * The field is only added to successful NFTokenCreateOffer transactions. + * + * Helper functions are not static because they can be used by Clio. */ +/** @{ */ bool canHaveNFTokenOfferID( std::shared_ptr const& serializedTx, diff --git a/include/xrpl/protocol/Protocol.h b/include/xrpl/protocol/Protocol.h index f802cfe058..e83e1c97b6 100644 --- a/include/xrpl/protocol/Protocol.h +++ b/include/xrpl/protocol/Protocol.h @@ -14,64 +14,88 @@ namespace xrpl { -/** Protocol specific constants. - - This information is, implicitly, part of the protocol. - - @note Changing these values without adding code to the - server to detect "pre-change" and "post-change" - will result in a hard fork. - - @ingroup protocol -*/ -/** Smallest legal byte size of a transaction. */ +/** + * Protocol specific constants. + * + * This information is, implicitly, part of the protocol. + * + * @note Changing these values without adding code to the + * server to detect "pre-change" and "post-change" + * will result in a hard fork. + * + * @ingroup protocol + */ +/** + * Smallest legal byte size of a transaction. + */ constexpr std::size_t kTxMinSizeBytes = 32; -/** Largest legal byte size of a transaction. */ +/** + * Largest legal byte size of a transaction. + */ constexpr std::size_t kTxMaxSizeBytes = megabytes(1); -/** The maximum number of unfunded offers to delete at once */ +/** + * The maximum number of unfunded offers to delete at once + */ constexpr std::size_t kUnfundedOfferRemoveLimit = 1000; -/** The maximum number of expired offers to delete at once */ +/** + * The maximum number of expired offers to delete at once + */ constexpr std::size_t kExpiredOfferRemoveLimit = 256; -/** The maximum number of metadata entries allowed in one transaction */ +/** + * The maximum number of metadata entries allowed in one transaction + */ constexpr std::size_t kOversizeMetaDataCap = 5200; -/** The maximum number of entries per directory page */ +/** + * The maximum number of entries per directory page + */ constexpr std::size_t kDirNodeMaxEntries = 32; -/** The maximum number of pages allowed in a directory - - Made obsolete by fixDirectoryLimit amendment. -*/ +/** + * The maximum number of pages allowed in a directory + * + * Made obsolete by fixDirectoryLimit amendment. + */ constexpr std::uint64_t kDirNodeMaxPages = 262144; -/** The maximum number of items in an NFT page */ +/** + * The maximum number of items in an NFT page + */ constexpr std::size_t kDirMaxTokensPerPage = 32; -/** The maximum number of owner directory entries for account to be deletable */ +/** + * The maximum number of owner directory entries for account to be deletable + */ constexpr std::size_t kMaxDeletableDirEntries = 1000; -/** The maximum number of token offers that can be canceled at once */ +/** + * The maximum number of token offers that can be canceled at once + */ constexpr std::size_t kMaxTokenOfferCancelCount = 500; -/** The maximum number of offers in an offer directory for NFT to be burnable */ +/** + * The maximum number of offers in an offer directory for NFT to be burnable + */ constexpr std::size_t kMaxDeletableTokenOfferEntries = 500; -/** The maximum token transfer fee allowed. - - Token transfer fees can range from 0% to 50% and are specified in tenths of - a basis point; that is a value of 1000 represents a transfer fee of 1% and - a value of 10000 represents a transfer fee of 10%. - - Note that for extremely low transfer fees values, it is possible that the - calculated fee will be 0. +/** + * The maximum token transfer fee allowed. + * + * Token transfer fees can range from 0% to 50% and are specified in tenths of + * a basis point; that is a value of 1000 represents a transfer fee of 1% and + * a value of 10000 represents a transfer fee of 10%. + * + * Note that for extremely low transfer fees values, it is possible that the + * calculated fee will be 0. */ constexpr std::uint16_t kMaxTransferFee = 50000; -/** There are 10,000 basis points (bips) in 100%. +/** + * There are 10,000 basis points (bips) in 100%. * * Basis points represent 0.01%. * @@ -116,36 +140,41 @@ tenthBipsOfValue(T value, TenthBips bips) } namespace Lending { -/** The maximum management fee rate allowed by a loan broker in 1/10 bips. - - Valid values are between 0 and 10% inclusive. -*/ +/** + * The maximum management fee rate allowed by a loan broker in 1/10 bips. + * + * Valid values are between 0 and 10% inclusive. + */ constexpr TenthBips16 kMaxManagementFeeRate( unsafeCast(percentageToTenthBips(10).value())); static_assert(kMaxManagementFeeRate == TenthBips16(std::uint16_t(10'000u))); -/** The maximum coverage rate required of a loan broker in 1/10 bips. - - Valid values are between 0 and 100% inclusive. -*/ +/** + * The maximum coverage rate required of a loan broker in 1/10 bips. + * + * Valid values are between 0 and 100% inclusive. + */ constexpr TenthBips32 kMaxCoverRate = percentageToTenthBips(100); static_assert(kMaxCoverRate == TenthBips32(100'000u)); -/** The maximum overpayment fee on a loan in 1/10 bips. -* - Valid values are between 0 and 100% inclusive. -*/ +/** + * The maximum overpayment fee on a loan in 1/10 bips. + * + * Valid values are between 0 and 100% inclusive. + */ constexpr TenthBips32 kMaxOverpaymentFee = percentageToTenthBips(100); static_assert(kMaxOverpaymentFee == TenthBips32(100'000u)); -/** Annualized interest rate of the Loan in 1/10 bips. +/** + * Annualized interest rate of the Loan in 1/10 bips. * * Valid values are between 0 and 100% inclusive. */ constexpr TenthBips32 kMaxInterestRate = percentageToTenthBips(100); static_assert(kMaxInterestRate == TenthBips32(100'000u)); -/** The maximum premium added to the interest rate for late payments on a loan +/** + * The maximum premium added to the interest rate for late payments on a loan * in 1/10 bips. * * Valid values are between 0 and 100% inclusive. @@ -153,7 +182,8 @@ static_assert(kMaxInterestRate == TenthBips32(100'000u)); constexpr TenthBips32 kMaxLateInterestRate = percentageToTenthBips(100); static_assert(kMaxLateInterestRate == TenthBips32(100'000u)); -/** The maximum close interest rate charged for repaying a loan early in 1/10 +/** + * The maximum close interest rate charged for repaying a loan early in 1/10 * bips. * * Valid values are between 0 and 100% inclusive. @@ -161,7 +191,8 @@ static_assert(kMaxLateInterestRate == TenthBips32(100'000u)); constexpr TenthBips32 kMaxCloseInterestRate = percentageToTenthBips(100); static_assert(kMaxCloseInterestRate == TenthBips32(100'000u)); -/** The maximum overpayment interest rate charged on loan overpayments in 1/10 +/** + * The maximum overpayment interest rate charged on loan overpayments in 1/10 * bips. * * Valid values are between 0 and 100% inclusive. @@ -169,7 +200,8 @@ static_assert(kMaxCloseInterestRate == TenthBips32(100'000u)); constexpr TenthBips32 kMaxOverpaymentInterestRate = percentageToTenthBips(100); static_assert(kMaxOverpaymentInterestRate == TenthBips32(100'000u)); -/** LoanPay transaction cost will be one base fee per X combined payments +/** + * LoanPay transaction cost will be one base fee per X combined payments * * The number of payments is estimated based on the Amount paid and the Loan's * Fixed Payment size. Overpayments (indicated with the tfLoanOverpayment flag) @@ -180,7 +212,8 @@ static_assert(kMaxOverpaymentInterestRate == TenthBips32(100'000u)); */ static constexpr int kLoanPaymentsPerFeeIncrement = 5; -/** Maximum number of combined payments that a LoanPay transaction will process +/** + * Maximum number of combined payments that a LoanPay transaction will process * * This limit is enforced during the loan payment process, and thus is not * estimated. If the limit is hit, no further payments or overpayments will be @@ -205,173 +238,267 @@ static constexpr int kLoanPaymentsPerFeeIncrement = 5; static constexpr int kLoanMaximumPaymentsPerTransaction = 100; } // namespace Lending -/** The maximum length of a URI inside an NFT */ +/** + * The maximum length of a URI inside an NFT + */ constexpr std::size_t kMaxTokenUriLength = 256; -/** The maximum length of a Data element inside a DID */ +/** + * The maximum length of a Data element inside a DID + */ constexpr std::size_t kMaxDidDocumentLength = 256; -/** The maximum length of a URI inside a DID */ +/** + * The maximum length of a URI inside a DID + */ constexpr std::size_t kMaxDidUriLength = 256; -/** The maximum length of an Attestation inside a DID */ +/** + * The maximum length of an Attestation inside a DID + */ constexpr std::size_t kMaxDidDataLength = 256; -/** The maximum length of a domain */ +/** + * The maximum length of a domain + */ constexpr std::size_t kMaxDomainLength = 256; -/** The maximum length of a URI inside a Credential */ +/** + * The maximum length of a URI inside a Credential + */ constexpr std::size_t kMaxCredentialUriLength = 256; -/** The maximum length of a CredentialType inside a Credential */ +/** + * The maximum length of a CredentialType inside a Credential + */ constexpr std::size_t kMaxCredentialTypeLength = 64; -/** The maximum number of credentials can be passed in array */ +/** + * The maximum number of credentials can be passed in array + */ constexpr std::size_t kMaxCredentialsArraySize = 8; -/** The maximum number of credentials can be passed in array for permissioned - * domain */ +/** + * The maximum number of credentials can be passed in array for permissioned + * domain + */ constexpr std::size_t kMaxPermissionedDomainCredentialsArraySize = 10; -/** The maximum length of MPTokenMetadata */ +/** + * The maximum length of MPTokenMetadata + */ constexpr std::size_t kMaxMpTokenMetadataLength = 1024; -/** The maximum amount of MPTokenIssuance */ +/** + * The maximum amount of MPTokenIssuance + */ constexpr std::uint64_t kMaxMpTokenAmount = 0x7FFF'FFFF'FFFF'FFFFull; static_assert(Number::kMaxRep >= kMaxMpTokenAmount); -/** The maximum length of Data payload */ +/** + * The maximum length of Data payload + */ constexpr std::size_t kMaxDataPayloadLength = 256; -/** Vault withdrawal policies */ +/** + * Vault withdrawal policies + */ constexpr std::uint8_t kVaultStrategyFirstComeFirstServe = 1; -/** Default IOU scale factor for a Vault */ +/** + * Default IOU scale factor for a Vault + */ constexpr std::uint8_t kVaultDefaultIouScale = 6; -/** Maximum scale factor for a Vault. The number is chosen to ensure that -1 IOU can be always converted to shares. -10^19 > maxMPTokenAmount (2^64-1) > 10^18 */ +/** + * Maximum scale factor for a Vault. The number is chosen to ensure that + * 1 IOU can be always converted to shares. + * 10^19 > maxMPTokenAmount (2^64-1) > 10^18 + */ constexpr std::uint8_t kVaultMaximumIouScale = 18; -/** Maximum recursion depth for vault shares being put as an asset inside - * another vault; counted from 0 */ +/** + * Maximum recursion depth for vault shares being put as an asset inside + * another vault; counted from 0 + */ constexpr std::uint8_t kMaxAssetCheckDepth = 5; -/** A ledger index. */ +/** + * A ledger index. + */ using LedgerIndex = std::uint32_t; constexpr std::uint32_t kFlagLedgerInterval = 256; -/** Returns true if the given ledgerIndex is a voting ledgerIndex */ +/** + * Returns true if the given ledgerIndex is a voting ledgerIndex + */ bool isVotingLedger(LedgerIndex seq); -/** Returns true if the given ledgerIndex is a flag ledgerIndex */ +/** + * Returns true if the given ledgerIndex is a flag ledgerIndex + */ bool isFlagLedger(LedgerIndex seq); -/** A transaction identifier. - The value is computed as the hash of the - canonicalized, serialized transaction object. -*/ +/** + * A transaction identifier. + * The value is computed as the hash of the + * canonicalized, serialized transaction object. + */ using TxID = uint256; -/** The maximum number of trustlines to delete as part of AMM account +/** + * The maximum number of trustlines to delete as part of AMM account * deletion cleanup. */ constexpr std::uint16_t kMaxDeletableAmmTrustLines = 512; -/** The maximum length of a URI inside an Oracle */ +/** + * The maximum length of a URI inside an Oracle + */ constexpr std::size_t kMaxOracleUri = 256; -/** The maximum length of a Provider inside an Oracle */ +/** + * The maximum length of a Provider inside an Oracle + */ constexpr std::size_t kMaxOracleProvider = 256; -/** The maximum size of a data series array inside an Oracle */ +/** + * The maximum size of a data series array inside an Oracle + */ constexpr std::size_t kMaxOracleDataSeries = 10; -/** The maximum length of a SymbolClass inside an Oracle */ +/** + * The maximum length of a SymbolClass inside an Oracle + */ constexpr std::size_t kMaxOracleSymbolClass = 16; -/** The maximum allowed time difference between lastUpdateTime and the time - of the last closed ledger -*/ +/** + * The maximum allowed time difference between lastUpdateTime and the time + * of the last closed ledger + */ constexpr std::size_t kMaxLastUpdateTimeDelta = 300; -/** The maximum price scaling factor +/** + * The maximum price scaling factor */ constexpr std::size_t kMaxPriceScale = 20; -/** The maximum percentage of outliers to trim +/** + * The maximum percentage of outliers to trim */ constexpr std::size_t kMaxTrim = 25; -/** The maximum number of delegate permissions an account can grant +/** + * The maximum number of delegate permissions an account can grant */ constexpr std::size_t kPermissionMaxSize = 10; -/** The maximum number of transactions that can be in a batch. */ +/** + * The maximum number of transactions that can be in a batch. + */ constexpr std::size_t kMaxBatchTxCount = 8; -/** The maximum number of batch signers. */ +/** + * The maximum number of batch signers. + */ constexpr std::size_t kMaxBatchSigners = kMaxBatchTxCount * 3; -/** Length of a secp256k1 scalar in bytes. */ +/** + * Length of a secp256k1 scalar in bytes. + */ constexpr std::size_t kEcScalarLength = kMPT_SCALAR_SIZE; -/** Length of EC point (compressed) */ +/** + * Length of EC point (compressed) + */ constexpr std::size_t kCompressedEcPointLength = 33; -/** Length of one compressed EC point component in an EC ElGamal ciphertext. */ +/** + * Length of one compressed EC point component in an EC ElGamal ciphertext. + */ constexpr std::size_t kEcCiphertextComponentLength = kMPT_ELGAMAL_CIPHER_SIZE; -/** EC ElGamal ciphertext length: two compressed EC points concatenated. */ +/** + * EC ElGamal ciphertext length: two compressed EC points concatenated. + */ constexpr std::size_t kEcGamalEncryptedTotalLength = kMPT_ELGAMAL_TOTAL_SIZE; -/** Length of EC public key (compressed) */ +/** + * Length of EC public key (compressed) + */ constexpr std::size_t kEcPubKeyLength = kMPT_PUBKEY_SIZE; -/** Length of EC private key in bytes */ +/** + * Length of EC private key in bytes + */ constexpr std::size_t kEcPrivKeyLength = kMPT_PRIVKEY_SIZE; -/** Length of the EC blinding factor in bytes */ +/** + * Length of the EC blinding factor in bytes + */ constexpr std::size_t kEcBlindingFactorLength = kMPT_BLINDING_FACTOR_SIZE; -/** Length of Schnorr ZKProof for public key registration (compact form) in bytes */ +/** + * Length of Schnorr ZKProof for public key registration (compact form) in bytes + */ constexpr std::size_t kEcSchnorrProofLength = kMPT_SCHNORR_PROOF_SIZE; -/** Length of Pedersen Commitment (compressed) */ +/** + * Length of Pedersen Commitment (compressed) + */ constexpr std::size_t kEcPedersenCommitmentLength = kMPT_PEDERSEN_COMMIT_SIZE; -/** Length of single bulletproof (range proof for 1 commitment) in bytes */ +/** + * Length of single bulletproof (range proof for 1 commitment) in bytes + */ constexpr std::size_t kEcSingleBulletproofLength = kMPT_SINGLE_BULLETPROOF_SIZE; -/** Length of double bulletproof (range proof for 2 commitments) in bytes */ +/** + * Length of double bulletproof (range proof for 2 commitments) in bytes + */ constexpr std::size_t kEcDoubleBulletproofLength = kMPT_DOUBLE_BULLETPROOF_SIZE; -/** Length of the compact sigma proof component for ConfidentialMPTSend. */ +/** + * Length of the compact sigma proof component for ConfidentialMPTSend. + */ constexpr std::size_t kEcSendSigmaProofLength = SECP256K1_COMPACT_STANDARD_PROOF_SIZE; -/** 192 bytes compact sigma proof + 754 bytes double bulletproof. */ +/** + * 192 bytes compact sigma proof + 754 bytes double bulletproof. + */ constexpr std::size_t kEcSendProofLength = kEcSendSigmaProofLength + kEcDoubleBulletproofLength; -/** Length of the compact sigma proof component for ConfidentialMPTConvertBack. */ +/** + * Length of the compact sigma proof component for ConfidentialMPTConvertBack. + */ constexpr std::size_t kEcConvertBackSigmaProofLength = SECP256K1_COMPACT_CONVERTBACK_PROOF_SIZE; -/** 128 bytes compact sigma proof + 688 bytes single bulletproof. */ +/** + * 128 bytes compact sigma proof + 688 bytes single bulletproof. + */ constexpr std::size_t kEcConvertBackProofLength = kEcConvertBackSigmaProofLength + kEcSingleBulletproofLength; -/** Length of the ZKProof for ConfidentialMPTClawback. */ +/** + * Length of the ZKProof for ConfidentialMPTClawback. + */ constexpr std::size_t kEcClawbackProofLength = SECP256K1_COMPACT_CLAWBACK_PROOF_SIZE; -/** Extra base fee multiplier charged to confidential MPT transactions. */ +/** + * Extra base fee multiplier charged to confidential MPT transactions. + */ constexpr std::uint32_t kConfidentialFeeMultiplier = 9; -/** Compressed EC point prefix for even y-coordinate */ +/** + * Compressed EC point prefix for even y-coordinate + */ constexpr std::uint8_t kEcCompressedPrefixEvenY = 0x02; -/** Compressed EC point prefix for odd y-coordinate */ +/** + * Compressed EC point prefix for odd y-coordinate + */ constexpr std::uint8_t kEcCompressedPrefixOddY = 0x03; } // namespace xrpl diff --git a/include/xrpl/protocol/PublicKey.h b/include/xrpl/protocol/PublicKey.h index 13db17fc6e..98301af487 100644 --- a/include/xrpl/protocol/PublicKey.h +++ b/include/xrpl/protocol/PublicKey.h @@ -26,28 +26,29 @@ namespace xrpl { -/** A public key. - - Public keys are used in the public-key cryptography - system used to verify signatures attached to messages. - - The format of the public key is XRPL specific, - information needed to determine the cryptosystem - parameters used is stored inside the key. - - As of this writing two systems are supported: - - secp256k1 - ed25519 - - secp256k1 public keys consist of a 33 byte - compressed public key, with the lead byte equal - to 0x02 or 0x03. - - The ed25519 public keys consist of a 1 byte - prefix constant 0xED, followed by 32 bytes of - public key data. -*/ +/** + * A public key. + * + * Public keys are used in the public-key cryptography + * system used to verify signatures attached to messages. + * + * The format of the public key is XRPL specific, + * information needed to determine the cryptosystem + * parameters used is stored inside the key. + * + * As of this writing two systems are supported: + * + * secp256k1 + * ed25519 + * + * secp256k1 public keys consist of a 33 byte + * compressed public key, with the lead byte equal + * to 0x02 or 0x03. + * + * The ed25519 public keys consist of a 1 byte + * prefix constant 0xED, followed by 32 bytes of + * public key data. + */ class PublicKey { protected: @@ -66,11 +67,12 @@ public: PublicKey& operator=(PublicKey const& other); - /** Create a public key. - - Preconditions: - publicKeyType(slice) != std::nullopt - */ + /** + * Create a public key. + * + * Preconditions: + * publicKeyType(slice) != std::nullopt + */ explicit PublicKey(Slice const& slice); [[nodiscard]] std::uint8_t const* @@ -121,7 +123,8 @@ public: } }; -/** Print the public key to a stream. +/** + * Print the public key to a stream. */ std::ostream& operator<<(std::ostream& os, PublicKey const& pk); @@ -180,39 +183,41 @@ parseBase58(TokenType type, std::string const& s); enum class ECDSACanonicality { Canonical, FullyCanonical }; -/** Determines the canonicality of a signature. - - A canonical signature is in its most reduced form. - For example the R and S components do not contain - additional leading zeroes. However, even in - canonical form, (R,S) and (R,G-S) are both - valid signatures for message M. - - Therefore, to prevent malleability attacks we - define a fully canonical signature as one where: - - R < G - S - - where G is the curve order. - - This routine returns std::nullopt if the format - of the signature is invalid (for example, the - points are encoded incorrectly). - - @return std::nullopt if the signature fails - validity checks. - - @note Only the format of the signature is checked, - no verification cryptography is performed. -*/ +/** + * Determines the canonicality of a signature. + * + * A canonical signature is in its most reduced form. + * For example the R and S components do not contain + * additional leading zeroes. However, even in + * canonical form, (R,S) and (R,G-S) are both + * valid signatures for message M. + * + * Therefore, to prevent malleability attacks we + * define a fully canonical signature as one where: + * + * R < G - S + * + * where G is the curve order. + * + * This routine returns std::nullopt if the format + * of the signature is invalid (for example, the + * points are encoded incorrectly). + * + * @return std::nullopt if the signature fails + * validity checks. + * + * @note Only the format of the signature is checked, + * no verification cryptography is performed. + */ std::optional ecdsaCanonicality(Slice const& sig); -/** Returns the type of public key. - - @return std::nullopt If the public key does not - represent a known type. -*/ +/** + * Returns the type of public key. + * + * @return std::nullopt If the public key does not + * represent a known type. + */ /** @{ */ [[nodiscard]] std::optional publicKeyType(Slice const& slice); @@ -224,7 +229,9 @@ publicKeyType(PublicKey const& publicKey) } /** @} */ -/** Verify a secp256k1 signature on the digest of a message. */ +/** + * Verify a secp256k1 signature on the digest of a message. + */ [[nodiscard]] bool verifyDigest( PublicKey const& publicKey, @@ -232,14 +239,17 @@ verifyDigest( Slice const& sig, bool mustBeFullyCanonical = true) noexcept; -/** Verify a signature on a message. - With secp256k1 signatures, the data is first hashed with - SHA512-Half, and the resulting digest is signed. -*/ +/** + * Verify a signature on a message. + * With secp256k1 signatures, the data is first hashed with + * SHA512-Half, and the resulting digest is signed. + */ [[nodiscard]] bool verify(PublicKey const& publicKey, Slice const& m, Slice const& sig) noexcept; -/** Calculate the 160-bit node ID from a node public key. */ +/** + * Calculate the 160-bit node ID from a node public key. + */ NodeID calcNodeID(PublicKey const&); diff --git a/include/xrpl/protocol/Quality.h b/include/xrpl/protocol/Quality.h index de61d79ca5..3475efa977 100644 --- a/include/xrpl/protocol/Quality.h +++ b/include/xrpl/protocol/Quality.h @@ -14,15 +14,16 @@ namespace xrpl { -/** Represents a pair of input and output currencies. - - The input currency can be converted to the output - currency by multiplying by the rate, represented by - Quality. - - For offers, "in" is always TakerPays and "out" is - always TakerGets. -*/ +/** + * Represents a pair of input and output currencies. + * + * The input currency can be converted to the output + * currency by multiplying by the rate, represented by + * Quality. + * + * For offers, "in" is always TakerPays and "out" is + * always TakerGets. + */ template struct TAmounts { @@ -36,7 +37,9 @@ struct TAmounts { } - /** Returns `true` if either quantity is not positive. */ + /** + * Returns `true` if either quantity is not positive. + */ [[nodiscard]] bool empty() const noexcept { @@ -84,11 +87,12 @@ operator!=(TAmounts const& lhs, TAmounts const& rhs) noexcept // XRPL specific constant used for parsing qualities and other things #define QUALITY_ONE 1'000'000'000 -/** Represents the logical ratio of output currency to input currency. - Internally this is stored using a custom floating point representation, - as the inverse of the ratio, so that quality will be descending in - a sequence of actual values that represent qualities. -*/ +/** + * Represents the logical ratio of output currency to input currency. + * Internally this is stored using a custom floating point representation, + * as the inverse of the ratio, so that quality will be descending in + * a sequence of actual values that represent qualities. + */ class Quality { public: @@ -109,26 +113,36 @@ private: public: Quality() = default; - /** Create a quality from the integer encoding of an STAmount */ + /** + * Create a quality from the integer encoding of an STAmount + */ explicit Quality(std::uint64_t value); - /** Create a quality from the ratio of two amounts. */ + /** + * Create a quality from the ratio of two amounts. + */ explicit Quality(Amounts const& amount); - /** Create a quality from the ratio of two amounts. */ + /** + * Create a quality from the ratio of two amounts. + */ template explicit Quality(TAmounts const& amount) : Quality(Amounts(toSTAmount(amount.in), toSTAmount(amount.out))) { } - /** Create a quality from the ratio of two amounts. */ + /** + * Create a quality from the ratio of two amounts. + */ template Quality(Out const& out, In const& in) : Quality(Amounts(toSTAmount(in), toSTAmount(out))) { } - /** Advances to the next higher quality level. */ + /** + * Advances to the next higher quality level. + */ /** @{ */ Quality& operator++(); @@ -137,7 +151,9 @@ public: operator++(int); /** @} */ - /** Advances to the next lower quality level. */ + /** + * Advances to the next lower quality level. + */ /** @{ */ Quality& operator--(); @@ -146,23 +162,27 @@ public: operator--(int); /** @} */ - /** Returns the quality as STAmount. */ + /** + * Returns the quality as STAmount. + */ [[nodiscard]] STAmount rate() const { return amountFromQuality(value_); } - /** Returns the quality rounded up to the specified number - of decimal digits. - */ + /** + * Returns the quality rounded up to the specified number + * of decimal digits. + */ [[nodiscard]] Quality round(int tickSize) const; - /** Returns the scaled amount with in capped. - Math is avoided if the result is exact. The output is clamped - to prevent money creation. - */ + /** + * Returns the scaled amount with in capped. + * Math is avoided if the result is exact. The output is clamped + * to prevent money creation. + */ [[nodiscard]] Amounts ceilIn(Amounts const& amount, STAmount const& limit) const; @@ -180,10 +200,11 @@ public: [[nodiscard]] TAmounts ceilInStrict(TAmounts const& amount, In const& limit, bool roundUp) const; - /** Returns the scaled amount with out capped. - Math is avoided if the result is exact. The input is clamped - to prevent money creation. - */ + /** + * Returns the scaled amount with out capped. + * Math is avoided if the result is exact. The input is clamped + * to prevent money creation. + */ [[nodiscard]] Amounts ceilOut(Amounts const& amount, STAmount const& limit) const; @@ -215,10 +236,11 @@ private: Round... round) const; public: - /** Returns `true` if lhs is lower quality than `rhs`. - Lower quality means the taker receives a worse deal. - Higher quality is better for the taker. - */ + /** + * Returns `true` if lhs is lower quality than `rhs`. + * Lower quality means the taker receives a worse deal. + * Higher quality is better for the taker. + */ friend bool operator<(Quality const& lhs, Quality const& rhs) noexcept { @@ -357,10 +379,11 @@ Quality::ceilOutStrict(TAmounts const& amount, Out const& limit, bool r return ceilTAmountsHelper(amount, limit, amount.out, kCeilOutFnPtr, roundUp); } -/** Calculate the quality of a two-hop path given the two hops. - @param lhs The first leg of the path: input to intermediate. - @param rhs The second leg of the path: intermediate to output. -*/ +/** + * Calculate the quality of a two-hop path given the two hops. + * @param lhs The first leg of the path: input to intermediate. + * @param rhs The second leg of the path: intermediate to output. + */ Quality composedQuality(Quality const& lhs, Quality const& rhs); diff --git a/include/xrpl/protocol/QualityFunction.h b/include/xrpl/protocol/QualityFunction.h index 7830519deb..128b37ce12 100644 --- a/include/xrpl/protocol/QualityFunction.h +++ b/include/xrpl/protocol/QualityFunction.h @@ -12,7 +12,8 @@ namespace xrpl { -/** Average quality of a path as a function of `out`: q(out) = m * out + b, +/** + * Average quality of a path as a function of `out`: q(out) = m * out + b, * where m = -1 / poolGets, b = poolPays / poolGets. If CLOB offer then * `m` is equal to 0 `b` is equal to the offer's quality. The function * is derived by substituting `in` in q = out / in with the swap out formula @@ -45,19 +46,22 @@ public: template QualityFunction(TAmounts const& amounts, std::uint32_t tfee, AMMTag); - /** Combines QF with the next step QF + /** + * Combines QF with the next step QF */ void combine(QualityFunction const& qf); - /** Find output to produce the requested + /** + * Find output to produce the requested * average quality. * @param quality requested average quality (quality limit) */ std::optional outFromAvgQ(Quality const& quality); - /** Return true if the quality function is constant + /** + * Return true if the quality function is constant */ [[nodiscard]] bool isConst() const diff --git a/include/xrpl/protocol/Rate.h b/include/xrpl/protocol/Rate.h index b8b04c8fb9..048787cab5 100644 --- a/include/xrpl/protocol/Rate.h +++ b/include/xrpl/protocol/Rate.h @@ -10,12 +10,13 @@ namespace xrpl { -/** Represents a transfer rate - - Transfer rates are specified as fractions of 1 billion. - For example, a transfer rate of 1% is represented as - 1,010,000,000. -*/ +/** + * Represents a transfer rate + * + * Transfer rates are specified as fractions of 1 billion. + * For example, a transfer rate of 1% is represented as + * 1,010,000,000. + */ struct Rate : private boost::totally_ordered { std::uint32_t value; @@ -65,13 +66,17 @@ STAmount divideRound(STAmount const& amount, Rate const& rate, Asset const& asset, bool roundUp); namespace nft { -/** Given a transfer fee (in basis points) convert it to a transfer rate. */ +/** + * Given a transfer fee (in basis points) convert it to a transfer rate. + */ Rate transferFeeAsRate(std::uint16_t fee); } // namespace nft -/** A transfer rate signifying a 1:1 exchange */ +/** + * A transfer rate signifying a 1:1 exchange + */ extern Rate const kParityRate; } // namespace xrpl diff --git a/include/xrpl/protocol/Rules.h b/include/xrpl/protocol/Rules.h index da2031650f..2c2136b6e8 100644 --- a/include/xrpl/protocol/Rules.h +++ b/include/xrpl/protocol/Rules.h @@ -11,7 +11,8 @@ namespace xrpl { -/** Check whether a feature is enabled in the current ledger rules +/** + * Check whether a feature is enabled in the current ledger rules * * @param feature The feature to be tested. * @param resultIfNoRules What to return if called from outside a Transactor context. @@ -19,7 +20,8 @@ namespace xrpl { bool isFeatureEnabled(uint256 const& feature, bool resultIfNoRules); -/** Check whether a feature is enabled in the current ledger rules +/** + * Check whether a feature is enabled in the current ledger rules * * @param feature The feature to be tested. * @@ -31,7 +33,9 @@ isFeatureEnabled(uint256 const& feature); class DigestAwareReadView; -/** Rules controlling protocol behavior. */ +/** + * Rules controlling protocol behavior. + */ class Rules { private: @@ -54,11 +58,12 @@ public: Rules() = delete; - /** Construct an empty rule set. - - These are the rules reflected by - the genesis ledger. - */ + /** + * Construct an empty rule set. + * + * These are the rules reflected by + * the genesis ledger. + */ explicit Rules(std::unordered_set> const& presets); private: @@ -80,14 +85,17 @@ private: presets() const; public: - /** Returns `true` if a feature is enabled. */ + /** + * Returns `true` if a feature is enabled. + */ [[nodiscard]] bool enabled(uint256 const& feature) const; - /** Returns `true` if two rule sets are identical. - - @note This is for diagnostics. - */ + /** + * Returns `true` if two rule sets are identical. + * + * @note This is for diagnostics. + */ bool operator==(Rules const&) const; @@ -101,7 +109,8 @@ getCurrentTransactionRules(); void setCurrentTransactionRules(std::optional r); -/** RAII class to set and restore the current transaction rules +/** + * RAII class to set and restore the current transaction rules */ class CurrentTransactionRulesGuard { diff --git a/include/xrpl/protocol/SField.h b/include/xrpl/protocol/SField.h index d97bcb0a1d..21ab7813f9 100644 --- a/include/xrpl/protocol/SField.h +++ b/include/xrpl/protocol/SField.h @@ -117,16 +117,17 @@ fieldCode(int id, int index) return (id << 16) | index; } -/** Identifies fields. - - Fields are necessary to tag data in signed transactions so that - the binary format of the transaction can be canonicalized. All - SFields are created at compile time. - - Each SField, once constructed, lives until program termination, and there - is only one instance per fieldType/fieldValue pair which serves the - entire application. -*/ +/** + * Identifies fields. + * + * Fields are necessary to tag data in signed transactions so that + * the binary format of the transaction can be canonicalized. All + * SFields are created at compile time. + * + * Each SField, once constructed, lives until program termination, and there + * is only one instance per fieldType/fieldValue pair which serves the + * entire application. + */ class SField { public: @@ -299,7 +300,9 @@ private: static std::unordered_map knownNameToField; }; -/** A field with a type known at compile time. */ +/** + * A field with a type known at compile time. + */ template struct TypedField : SField { @@ -309,7 +312,9 @@ struct TypedField : SField explicit TypedField(PrivateAccessTagT pat, Args&&... args); }; -/** Indicate std::optional field semantics. */ +/** + * Indicate std::optional field semantics. + */ template struct OptionaledField { diff --git a/include/xrpl/protocol/SOTemplate.h b/include/xrpl/protocol/SOTemplate.h index 682a7c655e..cb24ee315a 100644 --- a/include/xrpl/protocol/SOTemplate.h +++ b/include/xrpl/protocol/SOTemplate.h @@ -12,7 +12,9 @@ namespace xrpl { -/** Kind of element in each entry of an SOTemplate. */ +/** + * Kind of element in each entry of an SOTemplate. + */ // 2026 usages, 129 files // NOLINTNEXTLINE(cppcoreguidelines-use-enum-class) enum SOEStyle { @@ -25,13 +27,17 @@ enum SOEStyle { }; // Part of a Python-parsed DSL (transactions.macro); bare enumerator names required by the parser -/** Amount fields that can support MPT */ +/** + * Amount fields that can support MPT + */ // NOLINTNEXTLINE(cppcoreguidelines-use-enum-class) enum SOETxMPTIssue { SoeMptNone, SoeMptSupported, SoeMptNotSupported }; //------------------------------------------------------------------------------ -/** An element in a SOTemplate. */ +/** + * An element in a SOTemplate. + */ class SOElement { // Use std::reference_wrapper so SOElement can be stored in a std::vector. @@ -90,10 +96,11 @@ public: //------------------------------------------------------------------------------ -/** Defines the fields and their attributes within a STObject. - Each subclass of SerializedObject will provide its own template - describing the available fields and their metadata attributes. -*/ +/** + * Defines the fields and their attributes within a STObject. + * Each subclass of SerializedObject will provide its own template + * describing the available fields and their metadata attributes. + */ class SOTemplate { public: @@ -103,14 +110,16 @@ public: SOTemplate& operator=(SOTemplate&& other) = default; - /** Create a template populated with all fields. - After creating the template fields cannot be added, modified, or removed. - */ + /** + * Create a template populated with all fields. + * After creating the template fields cannot be added, modified, or removed. + */ SOTemplate(std::vector uniqueFields, std::vector commonFields = {}); - /** Create a template populated with all fields. - Note: Defers to the vector constructor above. - */ + /** + * Create a template populated with all fields. + * Note: Defers to the vector constructor above. + */ SOTemplate( std::initializer_list uniqueFields, std::initializer_list commonFields = {}); @@ -140,14 +149,18 @@ public: return end(); } - /** The number of entries in this template */ + /** + * The number of entries in this template + */ [[nodiscard]] std::size_t size() const { return elements_.size(); } - /** Retrieve the position of a named field. */ + /** + * Retrieve the position of a named field. + */ [[nodiscard]] int getIndex(SField const&) const; diff --git a/include/xrpl/protocol/STAmount.h b/include/xrpl/protocol/STAmount.h index 5e53a85129..cc80481582 100644 --- a/include/xrpl/protocol/STAmount.h +++ b/include/xrpl/protocol/STAmount.h @@ -190,7 +190,9 @@ public: [[nodiscard]] int signum() const noexcept; - /** Returns a zero value with the same issuer and currency. */ + /** + * Returns a zero value with the same issuer and currency. + */ [[nodiscard]] STAmount zeroed() const; @@ -255,7 +257,9 @@ public: void clear(Asset const& asset); - /** Set the Issue for this amount. */ + /** + * Set the Issue for this amount. + */ void setIssue(Asset const& asset); @@ -704,7 +708,8 @@ divRoundStrict(STAmount const& v1, STAmount const& v2, Asset const& asset, bool std::uint64_t getRate(STAmount const& offerOut, STAmount const& offerIn); -/** Round an arbitrary precision Amount to the precision of an STAmount that has +/** + * Round an arbitrary precision Amount to the precision of an STAmount that has * a given exponent. * * This is used to ensure that calculations involving IOU amounts do not collect @@ -714,7 +719,6 @@ getRate(STAmount const& offerOut, STAmount const& offerIn); * @param scale An exponent value to establish the precision limit of * `value`. Should be larger than `value.exponent()`. * @param rounding Optional Number rounding mode - * */ [[nodiscard]] STAmount roundToScale( @@ -722,7 +726,8 @@ roundToScale( std::int32_t scale, Number::RoundingMode rounding = Number::getround()); -/** Round an arbitrary precision Number IN PLACE to the precision of a given +/** + * Round an arbitrary precision Number IN PLACE to the precision of a given * Asset. * * This is used to ensure that calculations do not collect dust for IOUs, or @@ -738,7 +743,8 @@ roundToAsset(A const& asset, Number& value) value = STAmount{asset, value}; } -/** Round an arbitrary precision Number to the precision of a given Asset. +/** + * Round an arbitrary precision Number to the precision of a given Asset. * * This is used to ensure that calculations do not collect dust beyond specified * scale for IOUs, or fractional amounts for the integral types XRP and MPT. @@ -780,7 +786,8 @@ canAdd(STAmount const& amt1, STAmount const& amt2); bool canSubtract(STAmount const& amt1, STAmount const& amt2); -/** Get the scale of a Number for a given asset. +/** + * Get the scale of a Number for a given asset. * * "scale" is similar to "exponent", but from the perspective of STAmount, which has different rules * and mantissa ranges for determining the exponent than Number. diff --git a/include/xrpl/protocol/STBase.h b/include/xrpl/protocol/STBase.h index 341c80edd7..acc5500a57 100644 --- a/include/xrpl/protocol/STBase.h +++ b/include/xrpl/protocol/STBase.h @@ -15,7 +15,9 @@ namespace xrpl { -/// Note, should be treated as flags that can be | and & +/** + * Note, should be treated as flags that can be | and & + */ struct JsonOptions { using underlying_t = unsigned int; @@ -53,22 +55,28 @@ struct JsonOptions [[nodiscard]] constexpr auto friend operator!=(JsonOptions lh, JsonOptions rh) noexcept -> bool = default; - /// Returns JsonOptions union of lh and rh + /** + * Returns JsonOptions union of lh and rh + */ [[nodiscard]] constexpr JsonOptions friend operator|(JsonOptions lh, JsonOptions rh) noexcept { return {lh.value | rh.value}; } - /// Returns JsonOptions intersection of lh and rh + /** + * Returns JsonOptions intersection of lh and rh + */ [[nodiscard]] constexpr JsonOptions friend operator&(JsonOptions lh, JsonOptions rh) noexcept { return {lh.value & rh.value}; } - /// Returns JsonOptions binary negation, can be used with & (above) for set - /// difference e.g. `(options & ~JsonOptions::kIncludeDate)` + /** + * Returns JsonOptions binary negation, can be used with & (above) for set + * difference e.g. `(options & ~JsonOptions::kIncludeDate)` + */ [[nodiscard]] constexpr JsonOptions friend operator~(JsonOptions v) noexcept { @@ -103,19 +111,20 @@ class STVar; //------------------------------------------------------------------------------ -/** A type which can be exported to a well known binary format. - - A STBase: - - Always a field - - Can always go inside an eligible enclosing STBase - (such as STArray) - - Has a field name - - Like JSON, a SerializedObject is a basket which has rules - on what it can hold. - - @note "ST" stands for "Serialized Type." -*/ +/** + * A type which can be exported to a well known binary format. + * + * A STBase: + * - Always a field + * - Can always go inside an eligible enclosing STBase + * (such as STArray) + * - Has a field name + * + * Like JSON, a SerializedObject is a basket which has rules + * on what it can hold. + * + * @note "ST" stands for "Serialized Type." + */ class STBase { SField const* fName_; @@ -162,9 +171,10 @@ public: [[nodiscard]] virtual bool isDefault() const; - /** A STBase is a field. - This sets the name. - */ + /** + * A STBase is a field. + * This sets the name. + */ void setFName(SField const& n); diff --git a/include/xrpl/protocol/STExchange.h b/include/xrpl/protocol/STExchange.h index a9c1f57bd8..ad5bd4c012 100644 --- a/include/xrpl/protocol/STExchange.h +++ b/include/xrpl/protocol/STExchange.h @@ -18,7 +18,9 @@ namespace xrpl { -/** Convert between serialized type U and C++ type T. */ +/** + * Convert between serialized type U and C++ type T. + */ template struct STExchange; @@ -90,7 +92,9 @@ struct STExchange //------------------------------------------------------------------------------ -/** Return the value of a field in an STObject as a given type. */ +/** + * Return the value of a field in an STObject as a given type. + */ /** @{ */ template std::optional @@ -119,7 +123,9 @@ get(STObject const& st, TypedField const& f) } /** @} */ -/** Set a field value in an STObject. */ +/** + * Set a field value in an STObject. + */ template void set(STObject& st, TypedField const& f, T&& t) @@ -127,7 +133,9 @@ set(STObject& st, TypedField const& f, T&& t) st.set(STExchange>::set(f, std::forward(t))); } -/** Set a blob field using an init function. */ +/** + * Set a blob field using an init function. + */ template void set(STObject& st, TypedField const& f, std::size_t size, Init&& init) @@ -135,7 +143,9 @@ set(STObject& st, TypedField const& f, std::size_t size, Init&& init) st.set(std::make_unique(f, size, init)); } -/** Set a blob field from data. */ +/** + * Set a blob field from data. + */ template void set(STObject& st, TypedField const& f, void const* data, std::size_t size) @@ -143,7 +153,9 @@ set(STObject& st, TypedField const& f, void const* data, std::size_t siz st.set(std::make_unique(f, data, size)); } -/** Remove a field in an STObject. */ +/** + * Remove a field in an STObject. + */ template void erase(STObject& st, TypedField const& f) diff --git a/include/xrpl/protocol/STLedgerEntry.h b/include/xrpl/protocol/STLedgerEntry.h index a5f449f99c..8731488adb 100644 --- a/include/xrpl/protocol/STLedgerEntry.h +++ b/include/xrpl/protocol/STLedgerEntry.h @@ -33,7 +33,9 @@ public: using const_pointer = std::shared_ptr; using const_ref = std::shared_ptr const&; - /** Create an empty object with the given key and type. */ + /** + * Create an empty object with the given key and type. + */ explicit STLedgerEntry(Keylet const& k); STLedgerEntry(LedgerEntryType type, uint256 const& key); STLedgerEntry(SerialIter& sit, uint256 const& index); @@ -52,10 +54,11 @@ public: [[nodiscard]] json::Value getJson(JsonOptions options = JsonOptions::Values::None) const override; - /** Returns the 'key' (or 'index') of this item. - The key identifies this entry's position in - the SHAMap associative container. - */ + /** + * Returns the 'key' (or 'index') of this item. + * The key identifies this entry's position in + * the SHAMap associative container. + */ [[nodiscard]] uint256 const& key() const; @@ -105,10 +108,11 @@ inline STLedgerEntry::STLedgerEntry( { } -/** Returns the 'key' (or 'index') of this item. - The key identifies this entry's position in - the SHAMap associative container. -*/ +/** + * Returns the 'key' (or 'index') of this item. + * The key identifies this entry's position in + * the SHAMap associative container. + */ inline uint256 const& STLedgerEntry::key() const { diff --git a/include/xrpl/protocol/STObject.h b/include/xrpl/protocol/STObject.h index 9abf56e91b..a1cdff22e0 100644 --- a/include/xrpl/protocol/STObject.h +++ b/include/xrpl/protocol/STObject.h @@ -229,8 +229,10 @@ public: [[nodiscard]] AccountID getAccountID(SField const& field) const; - /** The account responsible for the authorization: the delegate when - sfDelegate is present, otherwise the account. */ + /** + * The account responsible for the authorization: the delegate when + * sfDelegate is present, otherwise the account. + */ [[nodiscard]] AccountID getInitiator() const; @@ -252,103 +254,112 @@ public: [[nodiscard]] STNumber const& getFieldNumber(SField const& field) const; - /** Get the value of a field. - @param A TypedField built from an SField value representing the desired - object field. In typical use, the TypedField will be implicitly - constructed. - @return The value of the specified field. - @throws STObject::FieldErr if the field is not present. - */ + /** + * Get the value of a field. + * @param A TypedField built from an SField value representing the desired + * object field. In typical use, the TypedField will be implicitly + * constructed. + * @return The value of the specified field. + * @throws STObject::FieldErr if the field is not present. + */ template T::value_type operator[](TypedField const& f) const; - /** Get the value of a field as a std::optional - - @param An OptionaledField built from an SField value representing the - desired object field. In typical use, the OptionaledField will be - constructed by using the ~ operator on an SField. - @return std::nullopt if the field is not present, else the value of - the specified field. - */ + /** + * Get the value of a field as a std::optional + * + * @param An OptionaledField built from an SField value representing the + * desired object field. In typical use, the OptionaledField will be + * constructed by using the ~ operator on an SField. + * @return std::nullopt if the field is not present, else the value of + * the specified field. + */ template std::optional> operator[](OptionaledField const& of) const; - /** Get a modifiable field value. - @param A TypedField built from an SField value representing the desired - object field. In typical use, the TypedField will be implicitly - constructed. - @return A modifiable reference to the value of the specified field. - @throws STObject::FieldErr if the field is not present. - */ + /** + * Get a modifiable field value. + * @param A TypedField built from an SField value representing the desired + * object field. In typical use, the TypedField will be implicitly + * constructed. + * @return A modifiable reference to the value of the specified field. + * @throws STObject::FieldErr if the field is not present. + */ template ValueProxy operator[](TypedField const& f); - /** Return a modifiable field value as std::optional - - @param An OptionaledField built from an SField value representing the - desired object field. In typical use, the OptionaledField will be - constructed by using the ~ operator on an SField. - @return Transparent proxy object to an `optional` holding a modifiable - reference to the value of the specified field. Returns - std::nullopt if the field is not present. - */ + /** + * Return a modifiable field value as std::optional + * + * @param An OptionaledField built from an SField value representing the + * desired object field. In typical use, the OptionaledField will be + * constructed by using the ~ operator on an SField. + * @return Transparent proxy object to an `optional` holding a modifiable + * reference to the value of the specified field. Returns + * std::nullopt if the field is not present. + */ template OptionalProxy operator[](OptionaledField const& of); - /** Get the value of a field. - @param A TypedField built from an SField value representing the desired - object field. In typical use, the TypedField will be implicitly - constructed. - @return The value of the specified field. - @throws STObject::FieldErr if the field is not present. - */ + /** + * Get the value of a field. + * @param A TypedField built from an SField value representing the desired + * object field. In typical use, the TypedField will be implicitly + * constructed. + * @return The value of the specified field. + * @throws STObject::FieldErr if the field is not present. + */ template [[nodiscard]] T::value_type at(TypedField const& f) const; - /** Get the value of a field as std::optional - - @param An OptionaledField built from an SField value representing the - desired object field. In typical use, the OptionaledField will be - constructed by using the ~ operator on an SField. - @return std::nullopt if the field is not present, else the value of - the specified field. - */ + /** + * Get the value of a field as std::optional + * + * @param An OptionaledField built from an SField value representing the + * desired object field. In typical use, the OptionaledField will be + * constructed by using the ~ operator on an SField. + * @return std::nullopt if the field is not present, else the value of + * the specified field. + */ template [[nodiscard]] std::optional> at(OptionaledField const& of) const; - /** Get a modifiable field value. - @param A TypedField built from an SField value representing the desired - object field. In typical use, the TypedField will be implicitly - constructed. - @return A modifiable reference to the value of the specified field. - @throws STObject::FieldErr if the field is not present. - */ + /** + * Get a modifiable field value. + * @param A TypedField built from an SField value representing the desired + * object field. In typical use, the TypedField will be implicitly + * constructed. + * @return A modifiable reference to the value of the specified field. + * @throws STObject::FieldErr if the field is not present. + */ template ValueProxy at(TypedField const& f); - /** Return a modifiable field value as std::optional - - @param An OptionaledField built from an SField value representing the - desired object field. In typical use, the OptionaledField will be - constructed by using the ~ operator on an SField. - @return Transparent proxy object to an `optional` holding a modifiable - reference to the value of the specified field. Returns - std::nullopt if the field is not present. - */ + /** + * Return a modifiable field value as std::optional + * + * @param An OptionaledField built from an SField value representing the + * desired object field. In typical use, the OptionaledField will be + * constructed by using the ~ operator on an SField. + * @return Transparent proxy object to an `optional` holding a modifiable + * reference to the value of the specified field. Returns + * std::nullopt if the field is not present. + */ template OptionalProxy at(OptionaledField const& of); - /** Set a field. - if the field already exists, it is replaced. - */ + /** + * Set a field. + * if the field already exists, it is replaced. + */ void set(std::unique_ptr v); @@ -503,8 +514,10 @@ public: value_type operator*() const; - /// Do not use operator->() unless the field is required, or you've checked - /// that it's set. + /** + * Do not use operator->() unless the field is required, or you've checked + * that it's set. + */ T const* operator->() const; @@ -604,17 +617,20 @@ public: OptionalProxy& operator=(OptionalProxy const&) = delete; - /** Returns `true` if the field is set. - - Fields with soeDEFAULT and set to the - default value will return `true` - */ + /** + * Returns `true` if the field is set. + * + * Fields with soeDEFAULT and set to the + * default value will return `true` + */ explicit operator bool() const noexcept; operator optional_type() const; - /** Explicit conversion to std::optional */ + /** + * Explicit conversion to std::optional + */ optional_type operator~() const; @@ -761,8 +777,10 @@ STObject::Proxy::operator*() const -> value_type return this->value(); } -/// Do not use operator->() unless the field is required, or you've checked that -/// it's set. +/** + * Do not use operator->() unless the field is required, or you've checked that + * it's set. + */ template T const* STObject::Proxy::operator->() const diff --git a/include/xrpl/protocol/STParsedJSON.h b/include/xrpl/protocol/STParsedJSON.h index 1eeecc8b9e..7189e0ec89 100644 --- a/include/xrpl/protocol/STParsedJSON.h +++ b/include/xrpl/protocol/STParsedJSON.h @@ -9,26 +9,33 @@ namespace xrpl { -/** Maximum JSON object nesting depth permitted during parsing. */ +/** + * Maximum JSON object nesting depth permitted during parsing. + */ inline constexpr std::size_t kMaxParsedJsonDepth = 64; -/** Maximum number of elements permitted in any JSON array field during parsing. - Requests exceeding this limit are rejected with an invalidParams error. */ +/** + * Maximum number of elements permitted in any JSON array field during parsing. + * Requests exceeding this limit are rejected with an invalidParams error. + */ inline constexpr std::size_t kMaxParsedJsonArraySize = 512; -/** Holds the serialized result of parsing an input JSON object. - This does validation and checking on the provided JSON. -*/ +/** + * Holds the serialized result of parsing an input JSON object. + * This does validation and checking on the provided JSON. + */ class STParsedJSONObject { public: - /** Parses and creates an STParsedJSON object. - The result of the parsing is stored in object and error. - Exceptions: - Does not throw. - @param name The name of the JSON field, used in diagnostics. - @param json The JSON-RPC to parse. - */ + /** + * Parses and creates an STParsedJSON object. + * The result of the parsing is stored in object and error. + * + * @note Does not throw. + * + * @param name The name of the JSON field, used in diagnostics. + * @param json The JSON-RPC to parse. + */ STParsedJSONObject(std::string const& name, json::Value const& json); STParsedJSONObject() = delete; @@ -37,10 +44,14 @@ public: operator=(STParsedJSONObject const&) = delete; ~STParsedJSONObject() = default; - /** The STObject if the parse was successful. */ + /** + * The STObject if the parse was successful. + */ std::optional object; - /** On failure, an appropriate set of error values. */ + /** + * On failure, an appropriate set of error values. + */ json::Value error; }; diff --git a/include/xrpl/protocol/STTakesAsset.h b/include/xrpl/protocol/STTakesAsset.h index 70bafd0e91..95667e4868 100644 --- a/include/xrpl/protocol/STTakesAsset.h +++ b/include/xrpl/protocol/STTakesAsset.h @@ -7,7 +7,8 @@ namespace xrpl { -/** Intermediate class for any STBase-derived class to store an Asset. +/** + * Intermediate class for any STBase-derived class to store an Asset. * * In the class definition, this class should be specified as a base class * _instead_ of STBase. @@ -41,7 +42,8 @@ STTakesAsset::associateAsset(Asset const& a) class STLedgerEntry; -/** Associate an Asset with all sMD_NeedsAsset fields in a ledger entry. +/** + * Associate an Asset with all sMD_NeedsAsset fields in a ledger entry. * * This function iterates over all fields in the given ledger entry. For each * field that is set and has the SField::sMD_NeedsAsset metadata flag, it calls @@ -54,7 +56,6 @@ class STLedgerEntry; * * @param sle The ledger entry whose fields will be updated. * @param asset The Asset to associate with the relevant fields. - * */ void associateAsset(STLedgerEntry& sle, Asset const& asset); diff --git a/include/xrpl/protocol/STTx.h b/include/xrpl/protocol/STTx.h index 5d5424e623..989bd11c10 100644 --- a/include/xrpl/protocol/STTx.h +++ b/include/xrpl/protocol/STTx.h @@ -55,12 +55,13 @@ public: explicit STTx(SerialIter&& sit); explicit STTx(STObject&& object); - /** Constructs a transaction. - - The returned transaction will have the specified type and - any fields that the callback function adds to the object - that's passed in. - */ + /** + * Constructs a transaction. + * + * The returned transaction will have the specified type and + * any fields that the callback function adds to the object + * that's passed in. + */ STTx(TxType type, std::function assembler); // STObject functions. @@ -92,7 +93,9 @@ public: [[nodiscard]] SeqProxy getSeqProxy() const; - /** Returns the first non-zero value of (Sequence, TicketSequence). */ + /** + * Returns the first non-zero value of (Sequence, TicketSequence). + */ [[nodiscard]] std::uint32_t getSeqValue() const; @@ -114,10 +117,11 @@ public: SecretKey const& secretKey, std::optional> signatureTarget = {}); - /** Check the signature. - @param rules The current ledger rules. - @return `true` if valid signature. If invalid, the error message string. - */ + /** + * Check the signature. + * @param rules The current ledger rules. + * @return `true` if valid signature. If invalid, the error message string. + */ [[nodiscard]] std::expected checkSign(Rules const& rules) const; @@ -145,12 +149,13 @@ public: getFeePayerID() const; private: - /** Check the signature. - @param rules The current ledger rules. - @param sigObject Reference to object that contains the signature fields. - Will be *this more often than not. - @return `true` if valid signature. If invalid, the error message string. - */ + /** + * Check the signature. + * @param rules The current ledger rules. + * @param sigObject Reference to object that contains the signature fields. + * Will be *this more often than not. + * @return `true` if valid signature. If invalid, the error message string. + */ [[nodiscard]] std::expected checkSign(Rules const& rules, STObject const& sigObject) const; @@ -181,17 +186,20 @@ private: bool passesLocalChecks(STObject const& st, std::string&); -/** Sterilize a transaction. - - The transaction is serialized and then deserialized, - ensuring that all equivalent transactions are in canonical - form. This also ensures that program metadata such as - the transaction's digest, are all computed. -*/ +/** + * Sterilize a transaction. + * + * The transaction is serialized and then deserialized, + * ensuring that all equivalent transactions are in canonical + * form. This also ensures that program metadata such as + * the transaction's digest, are all computed. + */ std::shared_ptr sterilize(STTx const& stx); -/** Check whether a transaction is a pseudo-transaction */ +/** + * Check whether a transaction is a pseudo-transaction + */ bool isPseudoTx(STObject const& tx); diff --git a/include/xrpl/protocol/STValidation.h b/include/xrpl/protocol/STValidation.h index 67a6594419..444fdfa600 100644 --- a/include/xrpl/protocol/STValidation.h +++ b/include/xrpl/protocol/STValidation.h @@ -54,30 +54,32 @@ class STValidation final : public STObject, public CountedObject NetClock::time_point seenTime_; public: - /** Construct a STValidation from a peer from serialized data. - - @param sit Iterator over serialized data - @param lookupNodeID Invocable with signature - NodeID(PublicKey const&) - used to find the Node ID based on the public key - that signed the validation. For manifest based - validators, this should be the NodeID of the master - public key. - @param checkSignature Whether to verify the data was signed properly - - @note Throws if the object is not valid - */ + /** + * Construct a STValidation from a peer from serialized data. + * + * @param sit Iterator over serialized data + * @param lookupNodeID Invocable with signature + * NodeID(PublicKey const&) + * used to find the Node ID based on the public key + * that signed the validation. For manifest based + * validators, this should be the NodeID of the master + * public key. + * @param checkSignature Whether to verify the data was signed properly + * + * @note Throws if the object is not valid + */ template STValidation(SerialIter& sit, LookupNodeID&& lookupNodeID, bool checkSignature); - /** Construct, sign and trust a new STValidation issued by this node. - - @param signTime When the validation is signed - @param publicKey The current signing public key - @param secretKey The current signing secret key - @param nodeID ID corresponding to node's public master key - @param f callback function to "fill" the validation with necessary data - */ + /** + * Construct, sign and trust a new STValidation issued by this node. + * + * @param signTime When the validation is signed + * @param publicKey The current signing public key + * @param secretKey The current signing secret key + * @param nodeID ID corresponding to node's public master key + * @param f callback function to "fill" the validation with necessary data + */ template STValidation( NetClock::time_point signTime, @@ -183,14 +185,15 @@ STValidation::STValidation(SerialIter& sit, LookupNodeID&& lookupNodeID, bool ch XRPL_ASSERT(nodeID_.isNonZero(), "xrpl::STValidation::STValidation(SerialIter) : nonzero node"); } -/** Construct, sign and trust a new STValidation issued by this node. - - @param signTime When the validation is signed - @param publicKey The current signing public key - @param secretKey The current signing secret key - @param nodeID ID corresponding to node's public master key - @param f callback function to "fill" the validation with necessary data -*/ +/** + * Construct, sign and trust a new STValidation issued by this node. + * + * @param signTime When the validation is signed + * @param publicKey The current signing public key + * @param secretKey The current signing secret key + * @param nodeID ID corresponding to node's public master key + * @param f callback function to "fill" the validation with necessary data + */ template STValidation::STValidation( NetClock::time_point signTime, diff --git a/include/xrpl/protocol/STVector256.h b/include/xrpl/protocol/STVector256.h index 46a1abc713..5a8418fef5 100644 --- a/include/xrpl/protocol/STVector256.h +++ b/include/xrpl/protocol/STVector256.h @@ -50,7 +50,9 @@ public: void setValue(STVector256 const& v); - /** Retrieve a copy of the vector we contain */ + /** + * Retrieve a copy of the vector we contain + */ explicit operator std::vector() const; @@ -138,7 +140,9 @@ STVector256::setValue(STVector256 const& v) value_ = v.value_; } -/** Retrieve a copy of the vector we contain */ +/** + * Retrieve a copy of the vector we contain + */ inline STVector256:: operator std::vector() const { diff --git a/include/xrpl/protocol/SecretKey.h b/include/xrpl/protocol/SecretKey.h index 8a0d917ab4..6d353acac0 100644 --- a/include/xrpl/protocol/SecretKey.h +++ b/include/xrpl/protocol/SecretKey.h @@ -17,7 +17,9 @@ namespace xrpl { -/** A secret key. */ +/** + * A secret key. + */ class SecretKey { public: @@ -56,11 +58,12 @@ public: return sizeof(buf_); } - /** Convert the secret key to a hexadecimal string. - - @note The operator<< function is deliberately omitted - to avoid accidental exposure of secret key material. - */ + /** + * Convert the secret key to a hexadecimal string. + * + * @note The operator<< function is deliberately omitted + * to avoid accidental exposure of secret key material. + */ [[nodiscard]] std::string toString() const; @@ -97,7 +100,9 @@ operator!=(SecretKey const& lhs, SecretKey const& rhs) = delete; //------------------------------------------------------------------------------ -/** Parse a secret key */ +/** + * Parse a secret key + */ template <> std::optional parseBase58(TokenType type, std::string const& s); @@ -108,38 +113,48 @@ toBase58(TokenType type, SecretKey const& sk) return encodeBase58Token(type, sk.data(), sk.size()); } -/** Create a secret key using secure random numbers. */ +/** + * Create a secret key using secure random numbers. + */ SecretKey randomSecretKey(); -/** Generate a new secret key deterministically. */ +/** + * Generate a new secret key deterministically. + */ SecretKey generateSecretKey(KeyType type, Seed const& seed); -/** Derive the public key from a secret key. */ +/** + * Derive the public key from a secret key. + */ PublicKey derivePublicKey(KeyType type, SecretKey const& sk); -/** Generate a key pair deterministically. - - This algorithm is specific to the XRPL: - - For secp256k1 key pairs, the seed is converted - to a Generator and used to compute the key pair - corresponding to ordinal 0 for the generator. -*/ +/** + * Generate a key pair deterministically. + * + * This algorithm is specific to the XRPL: + * + * For secp256k1 key pairs, the seed is converted + * to a Generator and used to compute the key pair + * corresponding to ordinal 0 for the generator. + */ std::pair generateKeyPair(KeyType type, Seed const& seed); -/** Create a key pair using secure random numbers. */ +/** + * Create a key pair using secure random numbers. + */ std::pair randomKeyPair(KeyType type); -/** Generate a signature for a message digest. - This can only be used with secp256k1 since Ed25519's - security properties come, in part, from how the message - is hashed. -*/ +/** + * Generate a signature for a message digest. + * This can only be used with secp256k1 since Ed25519's + * security properties come, in part, from how the message + * is hashed. + */ /** @{ */ Buffer signDigest(PublicKey const& pk, SecretKey const& sk, uint256 const& digest); @@ -151,10 +166,11 @@ signDigest(KeyType type, SecretKey const& sk, uint256 const& digest) } /** @} */ -/** Generate a signature for a message. - With secp256k1 signatures, the data is first hashed with - SHA512-Half, and the resulting digest is signed. -*/ +/** + * Generate a signature for a message. + * With secp256k1 signatures, the data is first hashed with + * SHA512-Half, and the resulting digest is signed. + */ /** @{ */ Buffer sign(PublicKey const& pk, SecretKey const& sk, Slice const& message); diff --git a/include/xrpl/protocol/Seed.h b/include/xrpl/protocol/Seed.h index a669f52079..4ccdd6707f 100644 --- a/include/xrpl/protocol/Seed.h +++ b/include/xrpl/protocol/Seed.h @@ -12,7 +12,9 @@ namespace xrpl { -/** Seeds are used to generate deterministic secret keys. */ +/** + * Seeds are used to generate deterministic secret keys. + */ class Seed { private: @@ -27,12 +29,15 @@ public: Seed& operator=(Seed const&) = default; - /** Destroy the seed. - The buffer will first be securely erased. - */ + /** + * Destroy the seed. + * The buffer will first be securely erased. + */ ~Seed(); - /** Construct a seed */ + /** + * Construct a seed + */ /** @{ */ explicit Seed(Slice const& slice); explicit Seed(uint128 const& seed); @@ -77,42 +82,52 @@ public: //------------------------------------------------------------------------------ -/** Create a seed using secure random numbers. */ +/** + * Create a seed using secure random numbers. + */ Seed randomSeed(); -/** Generate a seed deterministically. - - The algorithm is specific to the XRPL: - - The seed is calculated as the first 128 bits - of the SHA512-Half of the string text excluding - any terminating null. - - @note This will not attempt to determine the format of - the string (e.g. hex or base58). -*/ +/** + * Generate a seed deterministically. + * + * The algorithm is specific to the XRPL: + * + * The seed is calculated as the first 128 bits + * of the SHA512-Half of the string text excluding + * any terminating null. + * + * @note This will not attempt to determine the format of + * the string (e.g. hex or base58). + */ Seed generateSeed(std::string const& passPhrase); -/** Parse a Base58 encoded string into a seed */ +/** + * Parse a Base58 encoded string into a seed + */ template <> std::optional parseBase58(std::string const& s); -/** Attempt to parse a string as a seed. - - @param str the string to parse - @param rfc1751 true if we should attempt RFC1751 style parsing (deprecated) - * */ +/** + * Attempt to parse a string as a seed. + * + * @param str the string to parse + * @param rfc1751 true if we should attempt RFC1751 style parsing (deprecated) + */ std::optional parseGenericSeed(std::string const& str, bool rfc1751 = true); -/** Encode a Seed in RFC1751 format */ +/** + * Encode a Seed in RFC1751 format + */ std::string seedAs1751(Seed const& seed); -/** Format a seed as a Base58 string */ +/** + * Format a seed as a Base58 string + */ inline std::string toBase58(Seed const& seed) { diff --git a/include/xrpl/protocol/SeqProxy.h b/include/xrpl/protocol/SeqProxy.h index be040cceec..e6a97be0e7 100644 --- a/include/xrpl/protocol/SeqProxy.h +++ b/include/xrpl/protocol/SeqProxy.h @@ -5,33 +5,34 @@ namespace xrpl { -/** A type that represents either a sequence value or a ticket value. - - We use the value() of a SeqProxy in places where a sequence was used - before. An example of this is the sequence of an Offer stored in the - ledger. We do the same thing with the in-ledger identifier of a - Check, Payment Channel, and Escrow. - - Why is this safe? If we use the SeqProxy::value(), how do we know that - each ledger entry will be unique? - - There are two components that make this safe: - - 1. A "TicketCreate" transaction carefully avoids creating a ticket - that corresponds with an already used Sequence or Ticket value. - The transactor does this by referring to the account root's - sequence number. Creating the ticket advances the account root's - sequence number so the same ticket (or sequence) value cannot be - used again. - - 2. When a "TicketCreate" transaction creates a batch of tickets it advances - the account root sequence to one past the largest created ticket. - - Therefore all tickets in a batch other than the first may never have - the same value as a sequence on that same account. And since a ticket - may only be used once there will never be any duplicates within this - account. -*/ +/** + * A type that represents either a sequence value or a ticket value. + * + * We use the value() of a SeqProxy in places where a sequence was used + * before. An example of this is the sequence of an Offer stored in the + * ledger. We do the same thing with the in-ledger identifier of a + * Check, Payment Channel, and Escrow. + * + * Why is this safe? If we use the SeqProxy::value(), how do we know that + * each ledger entry will be unique? + * + * There are two components that make this safe: + * + * 1. A "TicketCreate" transaction carefully avoids creating a ticket + * that corresponds with an already used Sequence or Ticket value. + * The transactor does this by referring to the account root's + * sequence number. Creating the ticket advances the account root's + * sequence number so the same ticket (or sequence) value cannot be + * used again. + * + * 2. When a "TicketCreate" transaction creates a batch of tickets it advances + * the account root sequence to one past the largest created ticket. + * + * Therefore all tickets in a batch other than the first may never have + * the same value as a sequence on that same account. And since a ticket + * may only be used once there will never be any duplicates within this + * account. + */ class SeqProxy { public: @@ -51,7 +52,9 @@ public: SeqProxy& operator=(SeqProxy const& other) = default; - /** Factory function to return a sequence-based SeqProxy */ + /** + * Factory function to return a sequence-based SeqProxy + */ static constexpr SeqProxy sequence(std::uint32_t v) { diff --git a/include/xrpl/protocol/Sign.h b/include/xrpl/protocol/Sign.h index 18f085352d..fad2c35c9e 100644 --- a/include/xrpl/protocol/Sign.h +++ b/include/xrpl/protocol/Sign.h @@ -11,17 +11,18 @@ namespace xrpl { -/** Sign an STObject - - @param st Object to sign - @param prefix Prefix to insert before serialized object when hashing - @param type Signing key type used to derive public key - @param sk Signing secret key - @param sigField Field in which to store the signature on the object. - If not specified the value defaults to `sfSignature`. - - @note If a signature already exists, it is overwritten. -*/ +/** + * Sign an STObject + * + * @param st Object to sign + * @param prefix Prefix to insert before serialized object when hashing + * @param type Signing key type used to derive public key + * @param sk Signing secret key + * @param sigField Field in which to store the signature on the object. + * If not specified the value defaults to `sfSignature`. + * + * @note If a signature already exists, it is overwritten. + */ void sign( STObject& st, @@ -30,14 +31,15 @@ sign( SecretKey const& sk, SF_VL const& sigField = sfSignature); -/** Returns `true` if STObject contains valid signature - - @param st Signed object - @param prefix Prefix inserted before serialized object when hashing - @param pk Public key for verifying signature - @param sigField Object's field containing the signature. - If not specified the value defaults to `sfSignature`. -*/ +/** + * Returns `true` if STObject contains valid signature + * + * @param st Signed object + * @param prefix Prefix inserted before serialized object when hashing + * @param pk Public key for verifying signature + * @param sigField Object's field containing the signature. + * If not specified the value defaults to `sfSignature`. + */ bool verify( STObject const& st, @@ -45,22 +47,25 @@ verify( PublicKey const& pk, SF_VL const& sigField = sfSignature); -/** Return a Serializer suitable for computing a multisigning TxnSignature. */ +/** + * Return a Serializer suitable for computing a multisigning TxnSignature. + */ Serializer buildMultiSigningData(STObject const& obj, AccountID const& signingID); -/** Break the multi-signing hash computation into 2 parts for optimization. - - We can optimize verifying multiple multisignatures by splitting the - data building into two parts; - o A large part that is shared by all of the computations. - o A small part that is unique to each signer in the multisignature. - - The following methods support that optimization: - 1. startMultiSigningData provides the large part which can be shared. - 2. finishMultiSigningData caps the passed in serializer with each - signer's unique data. -*/ +/** + * Break the multi-signing hash computation into 2 parts for optimization. + * + * We can optimize verifying multiple multisignatures by splitting the + * data building into two parts; + * o A large part that is shared by all of the computations. + * o A small part that is unique to each signer in the multisignature. + * + * The following methods support that optimization: + * 1. startMultiSigningData provides the large part which can be shared. + * 2. finishMultiSigningData caps the passed in serializer with each + * signer's unique data. + */ Serializer startMultiSigningData(STObject const& obj); diff --git a/include/xrpl/protocol/SystemParameters.h b/include/xrpl/protocol/SystemParameters.h index b31dd0cd42..6ca36c8d9a 100644 --- a/include/xrpl/protocol/SystemParameters.h +++ b/include/xrpl/protocol/SystemParameters.h @@ -21,22 +21,30 @@ systemName() return kName; } -/** Configure the native currency. */ +/** + * Configure the native currency. + */ -/** Number of drops in the genesis account. */ +/** + * Number of drops in the genesis account. + */ constexpr XRPAmount kInitialXrp{100'000'000'000 * kDropsPerXrp}; static_assert(kInitialXrp.drops() == 100'000'000'000'000'000); static_assert(Number::kMaxRep >= kInitialXrp.drops()); -/** Returns true if the amount does not exceed the initial XRP in existence. */ +/** + * Returns true if the amount does not exceed the initial XRP in existence. + */ inline bool isLegalAmount(XRPAmount const& amount) { return amount <= kInitialXrp; } -/** Returns true if the absolute value of the amount does not exceed the initial - * XRP in existence. */ +/** + * Returns true if the absolute value of the amount does not exceed the initial + * XRP in existence. + */ inline bool isLegalAmountSigned(XRPAmount const& amount) { @@ -51,20 +59,30 @@ systemCurrencyCode() return kCode; } -/** The XRP ledger network's earliest allowed sequence */ +/** + * The XRP ledger network's earliest allowed sequence + */ static constexpr std::uint32_t kXrpLedgerEarliestSeq{32570u}; -/** The XRP Ledger mainnet's earliest ledger with a FeeSettings object. Only - * used in asserts and tests. */ +/** + * The XRP Ledger mainnet's earliest ledger with a FeeSettings object. Only + * used in asserts and tests. + */ static constexpr std::uint32_t kXrpLedgerEarliestFees{562177u}; -/** The minimum amount of support an amendment should have. */ +/** + * The minimum amount of support an amendment should have. + */ constexpr std::ratio<80, 100> kAmendmentMajorityCalcThreshold; -/** The minimum amount of time an amendment must hold a majority */ +/** + * The minimum amount of time an amendment must hold a majority + */ constexpr std::chrono::seconds const kDefaultAmendmentMajorityTime = weeks{2}; } // namespace xrpl -/** Default peer port (IANA registered) */ +/** + * Default peer port (IANA registered) + */ inline constexpr std::uint16_t kDefaultPeerPort{2459}; diff --git a/include/xrpl/protocol/TxFlags.h b/include/xrpl/protocol/TxFlags.h index 1d4f33a39d..0afdebb898 100644 --- a/include/xrpl/protocol/TxFlags.h +++ b/include/xrpl/protocol/TxFlags.h @@ -12,29 +12,30 @@ namespace xrpl { -/** Transaction flags. - - These flags are specified in a transaction's 'Flags' field and modify - the behavior of that transaction. - - There are two types of flags: - - (1) Universal flags: these are flags which apply to, and are interpreted the same way by, - all transactions, except, perhaps, to special pseudo-transactions. - - (2) Tx-Specific flags: these are flags which are interpreted according to the type of the - transaction being executed. That is, the same numerical flag value may have different - effects, depending on the transaction being executed. - - @note The universal transaction flags occupy the high-order 8 bits. - The tx-specific flags occupy the remaining 24 bits. - - @warning Transaction flags form part of the protocol. - **Changing them should be avoided because without special handling, this will result in - a hard fork.** - - @ingroup protocol -*/ +/** + * Transaction flags. + * + * These flags are specified in a transaction's 'Flags' field and modify + * the behavior of that transaction. + * + * There are two types of flags: + * + * (1) Universal flags: these are flags which apply to, and are interpreted the same way by, + * all transactions, except, perhaps, to special pseudo-transactions. + * + * (2) Tx-Specific flags: these are flags which are interpreted according to the type of the + * transaction being executed. That is, the same numerical flag value may have different + * effects, depending on the transaction being executed. + * + * @note The universal transaction flags occupy the high-order 8 bits. + * The tx-specific flags occupy the remaining 24 bits. + * + * @warning Transaction flags form part of the protocol. + * **Changing them should be avoided because without special handling, this will result in + * a hard fork.** + * + * @ingroup protocol + */ using FlagValue = std::uint32_t; diff --git a/include/xrpl/protocol/TxFormats.h b/include/xrpl/protocol/TxFormats.h index 36eb6d0889..8fb32c93cb 100644 --- a/include/xrpl/protocol/TxFormats.h +++ b/include/xrpl/protocol/TxFormats.h @@ -8,34 +8,36 @@ namespace xrpl { -/** Transaction type identifiers. - - These are part of the binary message format. - - @ingroup protocol -*/ -/** Transaction type identifiers - - Each ledger object requires a unique type identifier, which is stored - within the object itself; this makes it possible to iterate the entire - ledger and determine each object's type and verify that the object you - retrieved from a given hash matches the expected type. - - @warning Since these values are included in transactions, which are signed - objects, and used by the code to determine the type of transaction - being invoked, they are part of the protocol. **Changing them - should be avoided because without special handling, this will - result in a hard fork.** - - @note When retiring types, the specific values should not be removed but - should be marked as [[deprecated]]. This is to avoid accidental - reuse of identifiers. - - @todo The C++ language does not enable checking for duplicate values - here. If it becomes possible then we should do this. - - @ingroup protocol -*/ +/** + * Transaction type identifiers. + * + * These are part of the binary message format. + * + * @ingroup protocol + */ +/** + * Transaction type identifiers + * + * Each ledger object requires a unique type identifier, which is stored + * within the object itself; this makes it possible to iterate the entire + * ledger and determine each object's type and verify that the object you + * retrieved from a given hash matches the expected type. + * + * @warning Since these values are included in transactions, which are signed + * objects, and used by the code to determine the type of transaction + * being invoked, they are part of the protocol. **Changing them + * should be avoided because without special handling, this will + * result in a hard fork.** + * + * @note When retiring types, the specific values should not be removed but + * should be marked as [[deprecated]]. This is to avoid accidental + * reuse of identifiers. + * + * @todo The C++ language does not enable checking for duplicate values + * here. If it becomes possible then we should do this. + * + * @ingroup protocol + */ // clang-format off // Protocol-critical, hundreds of usages // NOLINTNEXTLINE(cppcoreguidelines-use-enum-class) @@ -52,28 +54,38 @@ enum TxType : std::uint16_t #undef TRANSACTION #pragma pop_macro("TRANSACTION") - /** This transaction type is deprecated; it is retained for historical purposes. */ + /** + * This transaction type is deprecated; it is retained for historical purposes. + */ TtNicknameSet [[deprecated("This transaction type is not supported and should not be used.")]] = 6, - /** This transaction type is deprecated; it is retained for historical purposes. */ + /** + * This transaction type is deprecated; it is retained for historical purposes. + */ TtContract [[deprecated("This transaction type is not supported and should not be used.")]] = 9, - /** This identifier was never used, but the slot is reserved for historical purposes. */ + /** + * This identifier was never used, but the slot is reserved for historical purposes. + */ TtSpinalTap [[deprecated("This transaction type is not supported and should not be used.")]] = 11, - /** This transaction type installs a hook. */ + /** + * This transaction type installs a hook. + */ TtHookSet [[maybe_unused]] = 22, }; // clang-format on -/** Manages the list of known transaction formats. +/** + * Manages the list of known transaction formats. */ class TxFormats : public KnownFormats { private: - /** Create the object. - This will load the object with all the known transaction formats. - */ + /** + * Create the object. + * This will load the object with all the known transaction formats. + */ TxFormats(); public: diff --git a/include/xrpl/protocol/TxMeta.h b/include/xrpl/protocol/TxMeta.h index 813f1b1615..88652afba9 100644 --- a/include/xrpl/protocol/TxMeta.h +++ b/include/xrpl/protocol/TxMeta.h @@ -60,7 +60,9 @@ public: STObject& getAffectedNode(uint256 const&); - /** Return a list of accounts affected by this transaction */ + /** + * Return a list of accounts affected by this transaction + */ [[nodiscard]] boost::container::flat_set getAffectedAccounts() const; diff --git a/include/xrpl/protocol/UintTypes.h b/include/xrpl/protocol/UintTypes.h index 1a3cb96691..25b0d1ffc9 100644 --- a/include/xrpl/protocol/UintTypes.h +++ b/include/xrpl/protocol/UintTypes.h @@ -30,34 +30,50 @@ public: } // namespace detail -/** Directory is an index into the directory of offer books. - The last 64 bits of this are the quality. */ +/** + * Directory is an index into the directory of offer books. + * The last 64 bits of this are the quality. + */ using Directory = BaseUInt<256, detail::DirectoryTag>; -/** Currency is a hash representing a specific currency. */ +/** + * Currency is a hash representing a specific currency. + */ using Currency = BaseUInt<160, detail::CurrencyTag>; -/** NodeID is a 160-bit hash representing one node. */ +/** + * NodeID is a 160-bit hash representing one node. + */ using NodeID = BaseUInt<160, detail::NodeIDTag>; -/** MPTID is a 192-bit value representing MPT Issuance ID, +/** + * MPTID is a 192-bit value representing MPT Issuance ID, * which is a concatenation of a 32-bit sequence (big endian) - * and a 160-bit account */ + * and a 160-bit account + */ using MPTID = BaseUInt<192>; -/** Domain is a 256-bit hash representing a specific domain. */ +/** + * Domain is a 256-bit hash representing a specific domain. + */ using Domain = BaseUInt<256>; -/** XRP currency. */ +/** + * XRP currency. + */ Currency const& xrpCurrency(); -/** A placeholder for empty currencies. */ +/** + * A placeholder for empty currencies. + */ Currency const& noCurrency(); -/** We deliberately disallow the currency that looks like "XRP" because too - many people were using it instead of the correct XRP currency. */ +/** + * We deliberately disallow the currency that looks like "XRP" because too + * many people were using it instead of the correct XRP currency. + */ Currency const& badCurrency(); @@ -67,26 +83,30 @@ isXRP(Currency const& c) return c == beast::kZero; } -/** Returns "", "XRP", or three letter ISO code. */ +/** + * Returns "", "XRP", or three letter ISO code. + */ std::string to_string(Currency const& c); -/** Tries to convert a string to a Currency, returns true on success. - - @note This function will return success if the resulting currency is - badCurrency(). This legacy behavior is unfortunate; changing this - will require very careful checking everywhere and may mean having - to rewrite some unit test code. -*/ +/** + * Tries to convert a string to a Currency, returns true on success. + * + * @note This function will return success if the resulting currency is + * badCurrency(). This legacy behavior is unfortunate; changing this + * will require very careful checking everywhere and may mean having + * to rewrite some unit test code. + */ bool toCurrency(Currency&, std::string const&); -/** Tries to convert a string to a Currency, returns noCurrency() on failure. - - @note This function can return badCurrency(). This legacy behavior is - unfortunate; changing this will require very careful checking - everywhere and may mean having to rewrite some unit test code. -*/ +/** + * Tries to convert a string to a Currency, returns noCurrency() on failure. + * + * @note This function can return badCurrency(). This legacy behavior is + * unfortunate; changing this will require very careful checking + * everywhere and may mean having to rewrite some unit test code. + */ Currency toCurrency(std::string const&); diff --git a/include/xrpl/protocol/Units.h b/include/xrpl/protocol/Units.h index 8b418704d6..169ee2c543 100644 --- a/include/xrpl/protocol/Units.h +++ b/include/xrpl/protocol/Units.h @@ -20,18 +20,26 @@ namespace xrpl { namespace unit { -/** "drops" are the smallest divisible amount of XRP. This is what most - of the code uses. */ +/** + * "drops" are the smallest divisible amount of XRP. This is what most + * of the code uses. + */ struct dropTag; -/** "fee levels" are used by the transaction queue to compare the relative - cost of transactions that require different levels of effort to process. - See also: src/xrpld/app/misc/FeeEscalation.md#fee-level */ +/** + * "fee levels" are used by the transaction queue to compare the relative + * cost of transactions that require different levels of effort to process. + * See also: src/xrpld/app/misc/FeeEscalation.md#fee-level + */ struct feelevelTag; -/** unitless values are plain scalars wrapped in a ValueUnit. They are - used for calculations in this header. */ +/** + * unitless values are plain scalars wrapped in a ValueUnit. They are + * used for calculations in this header. + */ struct unitlessTag; -/** Units to represent basis points (bips) and 1/10 basis points */ +/** + * Units to represent basis points (bips) and 1/10 basis points + */ class BipsTag; class TenthBipsTag; @@ -42,13 +50,14 @@ template concept Valid = std::is_class_v && std::is_object_v && std::is_object_v; -/** `Usable` is checked to ensure that only values with - known valid type tags can be used (sometimes transparently) in - non-unit contexts. At the time of implementation, this includes - all known tags, but more may be added in the future, and they - should not be added automatically unless determined to be - appropriate. -*/ +/** + * `Usable` is checked to ensure that only values with + * known valid type tags can be used (sometimes transparently) in + * non-unit contexts. At the time of implementation, this includes + * all known tags, but more may be added in the future, and they + * should not be added automatically unless determined to be + * appropriate. + */ template concept Usable = Valid && (std::is_same_v || @@ -115,9 +124,11 @@ public: return *this; } - /** Instances with the same unit, and a type that is - "safe" to convert to this one can be converted - implicitly */ + /** + * Instances with the same unit, and a type that is + * "safe" to convert to this one can be converted + * implicitly + */ template Other> constexpr ValueUnit(ValueUnit const& value) requires SafeToCast @@ -260,14 +271,18 @@ public: return value_ < other.value_; } - /** Returns true if the amount is not zero */ + /** + * Returns true if the amount is not zero + */ explicit constexpr operator bool() const noexcept { return value_ != 0; } - /** Return the sign of the amount */ + /** + * Return the sign of the amount + */ [[nodiscard]] constexpr int signum() const noexcept { @@ -276,7 +291,9 @@ public: return value_ ? 1 : 0; } - /** Returns the number of drops */ + /** + * Returns the number of drops + */ // TODO: Move this to a new class, maybe with the old "TaggedFee" name [[nodiscard]] constexpr value_type fee() const @@ -319,10 +336,11 @@ public: } } - /** Returns the underlying value. Code SHOULD NOT call this - function unless the type has been abstracted away, - e.g. in a templated function. - */ + /** + * Returns the underlying value. Code SHOULD NOT call this + * function unless the type has been abstracted away, + * e.g. in a templated function. + */ [[nodiscard]] constexpr value_type value() const { diff --git a/include/xrpl/protocol/XRPAmount.h b/include/xrpl/protocol/XRPAmount.h index 3190980ecb..26e10929df 100644 --- a/include/xrpl/protocol/XRPAmount.h +++ b/include/xrpl/protocol/XRPAmount.h @@ -138,7 +138,9 @@ public: return drops_ < other.drops_; } - /** Returns true if the amount is not zero */ + /** + * Returns true if the amount is not zero + */ explicit constexpr operator bool() const noexcept { @@ -150,7 +152,9 @@ public: return drops(); } - /** Return the sign of the amount */ + /** + * Return the sign of the amount + */ [[nodiscard]] constexpr int signum() const noexcept { @@ -159,7 +163,9 @@ public: return (drops_ != 0) ? 1 : 0; } - /** Returns the number of drops */ + /** + * Returns the number of drops + */ [[nodiscard]] constexpr value_type drops() const { @@ -217,10 +223,11 @@ public: return static_cast(drops_); } - /** Returns the underlying value. Code SHOULD NOT call this - function unless the type has been abstracted away, - e.g. in a templated function. - */ + /** + * Returns the underlying value. Code SHOULD NOT call this + * function unless the type has been abstracted away, + * e.g. in a templated function. + */ [[nodiscard]] constexpr value_type value() const { @@ -241,7 +248,9 @@ public: } }; -/** Number of drops per 1 XRP */ +/** + * Number of drops per 1 XRP + */ constexpr XRPAmount kDropsPerXrp{1'000'000}; constexpr double diff --git a/include/xrpl/protocol/detail/STVar.h b/include/xrpl/protocol/detail/STVar.h index e95461c253..12026f3d09 100644 --- a/include/xrpl/protocol/detail/STVar.h +++ b/include/xrpl/protocol/detail/STVar.h @@ -120,7 +120,8 @@ private: } } - /** Construct requested Serializable Type according to id. + /** + * Construct requested Serializable Type according to id. * The variadic args are: (SField), or (SerialIter, SField). * depth is ignored in former case. */ diff --git a/include/xrpl/protocol/digest.h b/include/xrpl/protocol/digest.h index c1e70cada2..44fca3d1ea 100644 --- a/include/xrpl/protocol/digest.h +++ b/include/xrpl/protocol/digest.h @@ -12,21 +12,23 @@ namespace xrpl { -/** Message digest functions used in the codebase - - @note These are modeled to meet the requirements of `Hasher` in the - `hash_append` interface, discussed in proposal: - - N3980 "Types Don't Know #" - http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3980.html -*/ +/** + * Message digest functions used in the codebase + * + * @note These are modeled to meet the requirements of `Hasher` in the + * `hash_append` interface, discussed in proposal: + * + * N3980 "Types Don't Know #" + * http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3980.html + */ //------------------------------------------------------------------------------ -/** RIPEMD-160 digest - - @note This uses the OpenSSL implementation -*/ +/** + * RIPEMD-160 digest + * + * @note This uses the OpenSSL implementation + */ struct OpensslRipemd160Hasher { public: @@ -46,10 +48,11 @@ private: char ctx_[96]{}; }; -/** SHA-512 digest - - @note This uses the OpenSSL implementation -*/ +/** + * SHA-512 digest + * + * @note This uses the OpenSSL implementation + */ struct OpensslSha512Hasher { public: @@ -69,10 +72,11 @@ private: char ctx_[216]{}; }; -/** SHA-256 digest - - @note This uses the OpenSSL implementation -*/ +/** + * SHA-256 digest + * + * @note This uses the OpenSSL implementation + */ struct OpensslSha256Hasher { public: @@ -100,21 +104,22 @@ using sha512_hasher = OpensslSha512Hasher; //------------------------------------------------------------------------------ -/** Returns the RIPEMD-160 digest of the SHA256 hash of the message. - - This operation is used to compute the 160-bit identifier - representing an XRPL account, from a message. Typically the - message is the public key of the account - which is not - stored in the account root. - - The same computation is used regardless of the cryptographic - scheme implied by the public key. For example, the public key - may be an ed25519 public key or a secp256k1 public key. Support - for new cryptographic systems may be added, using the same - formula for calculating the account identifier. - - Meets the requirements of Hasher (in hash_append) -*/ +/** + * Returns the RIPEMD-160 digest of the SHA256 hash of the message. + * + * This operation is used to compute the 160-bit identifier + * representing an XRPL account, from a message. Typically the + * message is the public key of the account - which is not + * stored in the account root. + * + * The same computation is used regardless of the cryptographic + * scheme implied by the public key. For example, the public key + * may be an ed25519 public key or a secp256k1 public key. Support + * for new cryptographic systems may be added, using the same + * formula for calculating the account identifier. + * + * Meets the requirements of Hasher (in hash_append) + */ struct RipeshaHasher { private: @@ -145,11 +150,12 @@ public: namespace detail { -/** Returns the SHA512-Half digest of a message. - - The SHA512-Half is the first 256 bits of the - SHA-512 digest of the message. -*/ +/** + * Returns the SHA512-Half digest of a message. + * + * The SHA512-Half is the first 256 bits of the + * SHA-512 digest of the message. + */ template struct BasicSha512HalfHasher { @@ -201,7 +207,9 @@ using sha512_half_hasher_s = detail::BasicSha512HalfHasher; //------------------------------------------------------------------------------ -/** Returns the SHA512-Half of a series of objects. */ +/** + * Returns the SHA512-Half of a series of objects. + */ template sha512_half_hasher::result_type sha512Half(Args const&... args) @@ -212,12 +220,13 @@ sha512Half(Args const&... args) return static_cast(h); } -/** Returns the SHA512-Half of a series of objects. - - Postconditions: - Temporary memory storing copies of - input messages will be cleared. -*/ +/** + * Returns the SHA512-Half of a series of objects. + * + * Postconditions: + * Temporary memory storing copies of + * input messages will be cleared. + */ template sha512_half_hasher_s::result_type sha512HalfS(Args const&... args) diff --git a/include/xrpl/protocol/serialize.h b/include/xrpl/protocol/serialize.h index e758b57d49..130a2366cb 100644 --- a/include/xrpl/protocol/serialize.h +++ b/include/xrpl/protocol/serialize.h @@ -9,7 +9,9 @@ namespace xrpl { -/** Serialize an object to a blob. */ +/** + * Serialize an object to a blob. + */ template Blob serializeBlob(Object const& o) @@ -19,7 +21,9 @@ serializeBlob(Object const& o) return s.peekData(); } -/** Serialize an object to a hex string. */ +/** + * Serialize an object to a hex string. + */ inline std::string serializeHex(STObject const& o) { diff --git a/include/xrpl/protocol/tokens.h b/include/xrpl/protocol/tokens.h index bce7e7b5d6..0ce80030e6 100644 --- a/include/xrpl/protocol/tokens.h +++ b/include/xrpl/protocol/tokens.h @@ -35,17 +35,18 @@ template [[nodiscard]] std::optional parseBase58(TokenType type, std::string const& s); -/** Encode data in Base58Check format using XRPL alphabet - - For details on the format see - https://xrpl.org/base58-encodings.html#base58-encodings - - @param type The type of token to encode. - @param token Pointer to the data to encode. - @param size The size of the data to encode. - - @return the encoded token. -*/ +/** + * Encode data in Base58Check format using XRPL alphabet + * + * For details on the format see + * https://xrpl.org/base58-encodings.html#base58-encodings + * + * @param type The type of token to encode. + * @param token Pointer to the data to encode. + * @param size The size of the data to encode. + * + * @return the encoded token. + */ [[nodiscard]] std::string encodeBase58Token(TokenType type, void const* token, std::size_t size); diff --git a/include/xrpl/protocol_autogen/LedgerEntryBase.h b/include/xrpl/protocol_autogen/LedgerEntryBase.h index 5758adbb24..7902055b85 100644 --- a/include/xrpl/protocol_autogen/LedgerEntryBase.h +++ b/include/xrpl/protocol_autogen/LedgerEntryBase.h @@ -158,7 +158,9 @@ public: } protected: - /** @brief The underlying serialized ledger entry being wrapped. */ + /** + * @brief The underlying serialized ledger entry being wrapped. + */ SLE::const_pointer sle_; }; diff --git a/include/xrpl/protocol_autogen/TransactionBase.h b/include/xrpl/protocol_autogen/TransactionBase.h index 161d718c66..a135709576 100644 --- a/include/xrpl/protocol_autogen/TransactionBase.h +++ b/include/xrpl/protocol_autogen/TransactionBase.h @@ -450,7 +450,9 @@ public: } protected: - /** @brief The underlying transaction object being wrapped. */ + /** + * @brief The underlying transaction object being wrapped. + */ std::shared_ptr tx_; }; diff --git a/include/xrpl/protocol_autogen/ledger_entries/AMM.h b/include/xrpl/protocol_autogen/ledger_entries/AMM.h index 11fd3738c9..e6daaf0043 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/AMM.h +++ b/include/xrpl/protocol_autogen/ledger_entries/AMM.h @@ -265,7 +265,9 @@ public: object_ = *sle; } - /** @brief Ledger entry-specific field setters */ + /** + * @brief Ledger entry-specific field setters + */ /** * @brief Set sfAccount (SoeRequired) diff --git a/include/xrpl/protocol_autogen/ledger_entries/AccountRoot.h b/include/xrpl/protocol_autogen/ledger_entries/AccountRoot.h index 725f46437e..04249b4e04 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/AccountRoot.h +++ b/include/xrpl/protocol_autogen/ledger_entries/AccountRoot.h @@ -636,7 +636,9 @@ public: object_ = *sle; } - /** @brief Ledger entry-specific field setters */ + /** + * @brief Ledger entry-specific field setters + */ /** * @brief Set sfAccount (SoeRequired) diff --git a/include/xrpl/protocol_autogen/ledger_entries/Amendments.h b/include/xrpl/protocol_autogen/ledger_entries/Amendments.h index 6a801308ca..c8b6c2d524 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/Amendments.h +++ b/include/xrpl/protocol_autogen/ledger_entries/Amendments.h @@ -175,7 +175,9 @@ public: object_ = *sle; } - /** @brief Ledger entry-specific field setters */ + /** + * @brief Ledger entry-specific field setters + */ /** * @brief Set sfAmendments (SoeOptional) diff --git a/include/xrpl/protocol_autogen/ledger_entries/Bridge.h b/include/xrpl/protocol_autogen/ledger_entries/Bridge.h index 2c7479b243..31d0e50c02 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/Bridge.h +++ b/include/xrpl/protocol_autogen/ledger_entries/Bridge.h @@ -219,7 +219,9 @@ public: object_ = *sle; } - /** @brief Ledger entry-specific field setters */ + /** + * @brief Ledger entry-specific field setters + */ /** * @brief Set sfAccount (SoeRequired) diff --git a/include/xrpl/protocol_autogen/ledger_entries/Check.h b/include/xrpl/protocol_autogen/ledger_entries/Check.h index 5b3fd10b92..d354425c39 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/Check.h +++ b/include/xrpl/protocol_autogen/ledger_entries/Check.h @@ -278,7 +278,9 @@ public: object_ = *sle; } - /** @brief Ledger entry-specific field setters */ + /** + * @brief Ledger entry-specific field setters + */ /** * @brief Set sfAccount (SoeRequired) diff --git a/include/xrpl/protocol_autogen/ledger_entries/Credential.h b/include/xrpl/protocol_autogen/ledger_entries/Credential.h index dfce76e45c..f4d243aea8 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/Credential.h +++ b/include/xrpl/protocol_autogen/ledger_entries/Credential.h @@ -228,7 +228,9 @@ public: object_ = *sle; } - /** @brief Ledger entry-specific field setters */ + /** + * @brief Ledger entry-specific field setters + */ /** * @brief Set sfSubject (SoeRequired) diff --git a/include/xrpl/protocol_autogen/ledger_entries/DID.h b/include/xrpl/protocol_autogen/ledger_entries/DID.h index ad423377e7..71113862c5 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/DID.h +++ b/include/xrpl/protocol_autogen/ledger_entries/DID.h @@ -202,7 +202,9 @@ public: object_ = *sle; } - /** @brief Ledger entry-specific field setters */ + /** + * @brief Ledger entry-specific field setters + */ /** * @brief Set sfAccount (SoeRequired) diff --git a/include/xrpl/protocol_autogen/ledger_entries/Delegate.h b/include/xrpl/protocol_autogen/ledger_entries/Delegate.h index bfe5f5587a..84a33ea2be 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/Delegate.h +++ b/include/xrpl/protocol_autogen/ledger_entries/Delegate.h @@ -181,7 +181,9 @@ public: object_ = *sle; } - /** @brief Ledger entry-specific field setters */ + /** + * @brief Ledger entry-specific field setters + */ /** * @brief Set sfAccount (SoeRequired) diff --git a/include/xrpl/protocol_autogen/ledger_entries/DepositPreauth.h b/include/xrpl/protocol_autogen/ledger_entries/DepositPreauth.h index 069bed6b77..c4c4434251 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/DepositPreauth.h +++ b/include/xrpl/protocol_autogen/ledger_entries/DepositPreauth.h @@ -179,7 +179,9 @@ public: object_ = *sle; } - /** @brief Ledger entry-specific field setters */ + /** + * @brief Ledger entry-specific field setters + */ /** * @brief Set sfAccount (SoeRequired) diff --git a/include/xrpl/protocol_autogen/ledger_entries/DirectoryNode.h b/include/xrpl/protocol_autogen/ledger_entries/DirectoryNode.h index 50659c33f6..7879f104af 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/DirectoryNode.h +++ b/include/xrpl/protocol_autogen/ledger_entries/DirectoryNode.h @@ -440,7 +440,9 @@ public: object_ = *sle; } - /** @brief Ledger entry-specific field setters */ + /** + * @brief Ledger entry-specific field setters + */ /** * @brief Set sfOwner (SoeOptional) diff --git a/include/xrpl/protocol_autogen/ledger_entries/Escrow.h b/include/xrpl/protocol_autogen/ledger_entries/Escrow.h index f3c033d26d..106d69722f 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/Escrow.h +++ b/include/xrpl/protocol_autogen/ledger_entries/Escrow.h @@ -372,7 +372,9 @@ public: object_ = *sle; } - /** @brief Ledger entry-specific field setters */ + /** + * @brief Ledger entry-specific field setters + */ /** * @brief Set sfAccount (SoeRequired) diff --git a/include/xrpl/protocol_autogen/ledger_entries/FeeSettings.h b/include/xrpl/protocol_autogen/ledger_entries/FeeSettings.h index 8f43d3b782..21478f749d 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/FeeSettings.h +++ b/include/xrpl/protocol_autogen/ledger_entries/FeeSettings.h @@ -294,7 +294,9 @@ public: object_ = *sle; } - /** @brief Ledger entry-specific field setters */ + /** + * @brief Ledger entry-specific field setters + */ /** * @brief Set sfBaseFee (SoeOptional) diff --git a/include/xrpl/protocol_autogen/ledger_entries/LedgerHashes.h b/include/xrpl/protocol_autogen/ledger_entries/LedgerHashes.h index f1d3684b55..2c6747b07b 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/LedgerHashes.h +++ b/include/xrpl/protocol_autogen/ledger_entries/LedgerHashes.h @@ -139,7 +139,9 @@ public: object_ = *sle; } - /** @brief Ledger entry-specific field setters */ + /** + * @brief Ledger entry-specific field setters + */ /** * @brief Set sfFirstLedgerSequence (SoeOptional) diff --git a/include/xrpl/protocol_autogen/ledger_entries/Loan.h b/include/xrpl/protocol_autogen/ledger_entries/Loan.h index 5d837736ec..a0abf9bd97 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/Loan.h +++ b/include/xrpl/protocol_autogen/ledger_entries/Loan.h @@ -616,7 +616,9 @@ public: object_ = *sle; } - /** @brief Ledger entry-specific field setters */ + /** + * @brief Ledger entry-specific field setters + */ /** * @brief Set sfPreviousTxnID (SoeRequired) diff --git a/include/xrpl/protocol_autogen/ledger_entries/LoanBroker.h b/include/xrpl/protocol_autogen/ledger_entries/LoanBroker.h index 88f05e3433..281af2cfeb 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/LoanBroker.h +++ b/include/xrpl/protocol_autogen/ledger_entries/LoanBroker.h @@ -387,7 +387,9 @@ public: object_ = *sle; } - /** @brief Ledger entry-specific field setters */ + /** + * @brief Ledger entry-specific field setters + */ /** * @brief Set sfPreviousTxnID (SoeRequired) diff --git a/include/xrpl/protocol_autogen/ledger_entries/MPToken.h b/include/xrpl/protocol_autogen/ledger_entries/MPToken.h index 379cfe53f5..874d779d09 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/MPToken.h +++ b/include/xrpl/protocol_autogen/ledger_entries/MPToken.h @@ -335,7 +335,9 @@ public: object_ = *sle; } - /** @brief Ledger entry-specific field setters */ + /** + * @brief Ledger entry-specific field setters + */ /** * @brief Set sfAccount (SoeRequired) diff --git a/include/xrpl/protocol_autogen/ledger_entries/MPTokenIssuance.h b/include/xrpl/protocol_autogen/ledger_entries/MPTokenIssuance.h index b6c77093ac..8518a0fe14 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/MPTokenIssuance.h +++ b/include/xrpl/protocol_autogen/ledger_entries/MPTokenIssuance.h @@ -420,7 +420,9 @@ public: object_ = *sle; } - /** @brief Ledger entry-specific field setters */ + /** + * @brief Ledger entry-specific field setters + */ /** * @brief Set sfIssuer (SoeRequired) diff --git a/include/xrpl/protocol_autogen/ledger_entries/NFTokenOffer.h b/include/xrpl/protocol_autogen/ledger_entries/NFTokenOffer.h index 072d3721f9..61aaccacdc 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/NFTokenOffer.h +++ b/include/xrpl/protocol_autogen/ledger_entries/NFTokenOffer.h @@ -217,7 +217,9 @@ public: object_ = *sle; } - /** @brief Ledger entry-specific field setters */ + /** + * @brief Ledger entry-specific field setters + */ /** * @brief Set sfOwner (SoeRequired) diff --git a/include/xrpl/protocol_autogen/ledger_entries/NFTokenPage.h b/include/xrpl/protocol_autogen/ledger_entries/NFTokenPage.h index 5e00cb1120..1aea6d6b01 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/NFTokenPage.h +++ b/include/xrpl/protocol_autogen/ledger_entries/NFTokenPage.h @@ -166,7 +166,9 @@ public: object_ = *sle; } - /** @brief Ledger entry-specific field setters */ + /** + * @brief Ledger entry-specific field setters + */ /** * @brief Set sfPreviousPageMin (SoeOptional) diff --git a/include/xrpl/protocol_autogen/ledger_entries/NegativeUNL.h b/include/xrpl/protocol_autogen/ledger_entries/NegativeUNL.h index 7ca9729082..a35865202d 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/NegativeUNL.h +++ b/include/xrpl/protocol_autogen/ledger_entries/NegativeUNL.h @@ -199,7 +199,9 @@ public: object_ = *sle; } - /** @brief Ledger entry-specific field setters */ + /** + * @brief Ledger entry-specific field setters + */ /** * @brief Set sfDisabledValidators (SoeOptional) diff --git a/include/xrpl/protocol_autogen/ledger_entries/Offer.h b/include/xrpl/protocol_autogen/ledger_entries/Offer.h index f51b54cfd2..e3539fc1fc 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/Offer.h +++ b/include/xrpl/protocol_autogen/ledger_entries/Offer.h @@ -268,7 +268,9 @@ public: object_ = *sle; } - /** @brief Ledger entry-specific field setters */ + /** + * @brief Ledger entry-specific field setters + */ /** * @brief Set sfAccount (SoeRequired) diff --git a/include/xrpl/protocol_autogen/ledger_entries/Oracle.h b/include/xrpl/protocol_autogen/ledger_entries/Oracle.h index 902032f94f..727e63ea84 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/Oracle.h +++ b/include/xrpl/protocol_autogen/ledger_entries/Oracle.h @@ -231,7 +231,9 @@ public: object_ = *sle; } - /** @brief Ledger entry-specific field setters */ + /** + * @brief Ledger entry-specific field setters + */ /** * @brief Set sfOwner (SoeRequired) diff --git a/include/xrpl/protocol_autogen/ledger_entries/PayChannel.h b/include/xrpl/protocol_autogen/ledger_entries/PayChannel.h index 61a4e2d044..4d00fbb7e5 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/PayChannel.h +++ b/include/xrpl/protocol_autogen/ledger_entries/PayChannel.h @@ -339,7 +339,9 @@ public: object_ = *sle; } - /** @brief Ledger entry-specific field setters */ + /** + * @brief Ledger entry-specific field setters + */ /** * @brief Set sfAccount (SoeRequired) diff --git a/include/xrpl/protocol_autogen/ledger_entries/PermissionedDomain.h b/include/xrpl/protocol_autogen/ledger_entries/PermissionedDomain.h index 638dda2420..e793e2d658 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/PermissionedDomain.h +++ b/include/xrpl/protocol_autogen/ledger_entries/PermissionedDomain.h @@ -157,7 +157,9 @@ public: object_ = *sle; } - /** @brief Ledger entry-specific field setters */ + /** + * @brief Ledger entry-specific field setters + */ /** * @brief Set sfOwner (SoeRequired) diff --git a/include/xrpl/protocol_autogen/ledger_entries/RippleState.h b/include/xrpl/protocol_autogen/ledger_entries/RippleState.h index 388a062ec1..dda1b78e66 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/RippleState.h +++ b/include/xrpl/protocol_autogen/ledger_entries/RippleState.h @@ -335,7 +335,9 @@ public: object_ = *sle; } - /** @brief Ledger entry-specific field setters */ + /** + * @brief Ledger entry-specific field setters + */ /** * @brief Set sfBalance (SoeRequired) diff --git a/include/xrpl/protocol_autogen/ledger_entries/SignerList.h b/include/xrpl/protocol_autogen/ledger_entries/SignerList.h index 443e5588f9..3aff8fa51b 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/SignerList.h +++ b/include/xrpl/protocol_autogen/ledger_entries/SignerList.h @@ -181,7 +181,9 @@ public: object_ = *sle; } - /** @brief Ledger entry-specific field setters */ + /** + * @brief Ledger entry-specific field setters + */ /** * @brief Set sfOwner (SoeOptional) diff --git a/include/xrpl/protocol_autogen/ledger_entries/Sponsorship.h b/include/xrpl/protocol_autogen/ledger_entries/Sponsorship.h index c309a38aef..065e655682 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/Sponsorship.h +++ b/include/xrpl/protocol_autogen/ledger_entries/Sponsorship.h @@ -228,7 +228,9 @@ public: object_ = *sle; } - /** @brief Ledger entry-specific field setters */ + /** + * @brief Ledger entry-specific field setters + */ /** * @brief Set sfPreviousTxnID (SoeRequired) diff --git a/include/xrpl/protocol_autogen/ledger_entries/Ticket.h b/include/xrpl/protocol_autogen/ledger_entries/Ticket.h index 6fa5b57f6c..e1205bdb67 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/Ticket.h +++ b/include/xrpl/protocol_autogen/ledger_entries/Ticket.h @@ -143,7 +143,9 @@ public: object_ = *sle; } - /** @brief Ledger entry-specific field setters */ + /** + * @brief Ledger entry-specific field setters + */ /** * @brief Set sfAccount (SoeRequired) diff --git a/include/xrpl/protocol_autogen/ledger_entries/Vault.h b/include/xrpl/protocol_autogen/ledger_entries/Vault.h index d1aaeb4ed8..2bf92b4f5d 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/Vault.h +++ b/include/xrpl/protocol_autogen/ledger_entries/Vault.h @@ -339,7 +339,9 @@ public: object_ = *sle; } - /** @brief Ledger entry-specific field setters */ + /** + * @brief Ledger entry-specific field setters + */ /** * @brief Set sfPreviousTxnID (SoeRequired) diff --git a/include/xrpl/protocol_autogen/ledger_entries/XChainOwnedClaimID.h b/include/xrpl/protocol_autogen/ledger_entries/XChainOwnedClaimID.h index 3f8058a4a1..bfd7a0a8b8 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/XChainOwnedClaimID.h +++ b/include/xrpl/protocol_autogen/ledger_entries/XChainOwnedClaimID.h @@ -196,7 +196,9 @@ public: object_ = *sle; } - /** @brief Ledger entry-specific field setters */ + /** + * @brief Ledger entry-specific field setters + */ /** * @brief Set sfAccount (SoeRequired) diff --git a/include/xrpl/protocol_autogen/ledger_entries/XChainOwnedCreateAccountClaimID.h b/include/xrpl/protocol_autogen/ledger_entries/XChainOwnedCreateAccountClaimID.h index e24009a4b7..4872d4063e 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/XChainOwnedCreateAccountClaimID.h +++ b/include/xrpl/protocol_autogen/ledger_entries/XChainOwnedCreateAccountClaimID.h @@ -170,7 +170,9 @@ public: object_ = *sle; } - /** @brief Ledger entry-specific field setters */ + /** + * @brief Ledger entry-specific field setters + */ /** * @brief Set sfAccount (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/AMMBid.h b/include/xrpl/protocol_autogen/transactions/AMMBid.h index cd2792e810..30a2b6f2ab 100644 --- a/include/xrpl/protocol_autogen/transactions/AMMBid.h +++ b/include/xrpl/protocol_autogen/transactions/AMMBid.h @@ -190,7 +190,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfAsset (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/AMMClawback.h b/include/xrpl/protocol_autogen/transactions/AMMClawback.h index ccbd7d99e6..38aba892c4 100644 --- a/include/xrpl/protocol_autogen/transactions/AMMClawback.h +++ b/include/xrpl/protocol_autogen/transactions/AMMClawback.h @@ -154,7 +154,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfHolder (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/AMMCreate.h b/include/xrpl/protocol_autogen/transactions/AMMCreate.h index cc88428e7a..c6ccd4e860 100644 --- a/include/xrpl/protocol_autogen/transactions/AMMCreate.h +++ b/include/xrpl/protocol_autogen/transactions/AMMCreate.h @@ -127,7 +127,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfAmount (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/AMMDelete.h b/include/xrpl/protocol_autogen/transactions/AMMDelete.h index 4cc0497c32..05899a46c8 100644 --- a/include/xrpl/protocol_autogen/transactions/AMMDelete.h +++ b/include/xrpl/protocol_autogen/transactions/AMMDelete.h @@ -114,7 +114,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfAsset (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/AMMDeposit.h b/include/xrpl/protocol_autogen/transactions/AMMDeposit.h index e01332c3e2..5416547dab 100644 --- a/include/xrpl/protocol_autogen/transactions/AMMDeposit.h +++ b/include/xrpl/protocol_autogen/transactions/AMMDeposit.h @@ -246,7 +246,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfAsset (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/AMMVote.h b/include/xrpl/protocol_autogen/transactions/AMMVote.h index b19d440c84..7dce3c252f 100644 --- a/include/xrpl/protocol_autogen/transactions/AMMVote.h +++ b/include/xrpl/protocol_autogen/transactions/AMMVote.h @@ -127,7 +127,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfAsset (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/AMMWithdraw.h b/include/xrpl/protocol_autogen/transactions/AMMWithdraw.h index 196f0faba2..81258f22d6 100644 --- a/include/xrpl/protocol_autogen/transactions/AMMWithdraw.h +++ b/include/xrpl/protocol_autogen/transactions/AMMWithdraw.h @@ -220,7 +220,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfAsset (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/AccountDelete.h b/include/xrpl/protocol_autogen/transactions/AccountDelete.h index c0346d9499..cf6e97bb63 100644 --- a/include/xrpl/protocol_autogen/transactions/AccountDelete.h +++ b/include/xrpl/protocol_autogen/transactions/AccountDelete.h @@ -151,7 +151,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfDestination (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/AccountSet.h b/include/xrpl/protocol_autogen/transactions/AccountSet.h index 7ddda45752..55c449e78e 100644 --- a/include/xrpl/protocol_autogen/transactions/AccountSet.h +++ b/include/xrpl/protocol_autogen/transactions/AccountSet.h @@ -346,7 +346,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfEmailHash (SoeOptional) diff --git a/include/xrpl/protocol_autogen/transactions/Batch.h b/include/xrpl/protocol_autogen/transactions/Batch.h index 00f553ada7..1a59d2b4c0 100644 --- a/include/xrpl/protocol_autogen/transactions/Batch.h +++ b/include/xrpl/protocol_autogen/transactions/Batch.h @@ -123,7 +123,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfRawTransactions (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/CheckCancel.h b/include/xrpl/protocol_autogen/transactions/CheckCancel.h index 4f7534278b..b75b717e3f 100644 --- a/include/xrpl/protocol_autogen/transactions/CheckCancel.h +++ b/include/xrpl/protocol_autogen/transactions/CheckCancel.h @@ -99,7 +99,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfCheckID (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/CheckCash.h b/include/xrpl/protocol_autogen/transactions/CheckCash.h index a58a20c57e..c742a15154 100644 --- a/include/xrpl/protocol_autogen/transactions/CheckCash.h +++ b/include/xrpl/protocol_autogen/transactions/CheckCash.h @@ -153,7 +153,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfCheckID (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/CheckCreate.h b/include/xrpl/protocol_autogen/transactions/CheckCreate.h index 17f985ac63..63e55f8604 100644 --- a/include/xrpl/protocol_autogen/transactions/CheckCreate.h +++ b/include/xrpl/protocol_autogen/transactions/CheckCreate.h @@ -191,7 +191,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfDestination (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/Clawback.h b/include/xrpl/protocol_autogen/transactions/Clawback.h index ecd7ebe7a2..9a3a7f9feb 100644 --- a/include/xrpl/protocol_autogen/transactions/Clawback.h +++ b/include/xrpl/protocol_autogen/transactions/Clawback.h @@ -126,7 +126,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfAmount (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/ConfidentialMPTClawback.h b/include/xrpl/protocol_autogen/transactions/ConfidentialMPTClawback.h index 2b16590649..c80fc81dc5 100644 --- a/include/xrpl/protocol_autogen/transactions/ConfidentialMPTClawback.h +++ b/include/xrpl/protocol_autogen/transactions/ConfidentialMPTClawback.h @@ -138,7 +138,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfMPTokenIssuanceID (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/ConfidentialMPTConvert.h b/include/xrpl/protocol_autogen/transactions/ConfidentialMPTConvert.h index f7a4cb601a..dec7f733c9 100644 --- a/include/xrpl/protocol_autogen/transactions/ConfidentialMPTConvert.h +++ b/include/xrpl/protocol_autogen/transactions/ConfidentialMPTConvert.h @@ -229,7 +229,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfMPTokenIssuanceID (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/ConfidentialMPTConvertBack.h b/include/xrpl/protocol_autogen/transactions/ConfidentialMPTConvertBack.h index 68bf326645..53a8e64125 100644 --- a/include/xrpl/protocol_autogen/transactions/ConfidentialMPTConvertBack.h +++ b/include/xrpl/protocol_autogen/transactions/ConfidentialMPTConvertBack.h @@ -203,7 +203,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfMPTokenIssuanceID (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/ConfidentialMPTMergeInbox.h b/include/xrpl/protocol_autogen/transactions/ConfidentialMPTMergeInbox.h index bb932080d8..848da42a41 100644 --- a/include/xrpl/protocol_autogen/transactions/ConfidentialMPTMergeInbox.h +++ b/include/xrpl/protocol_autogen/transactions/ConfidentialMPTMergeInbox.h @@ -99,7 +99,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfMPTokenIssuanceID (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/ConfidentialMPTSend.h b/include/xrpl/protocol_autogen/transactions/ConfidentialMPTSend.h index 2d8a77d56f..806a2586e9 100644 --- a/include/xrpl/protocol_autogen/transactions/ConfidentialMPTSend.h +++ b/include/xrpl/protocol_autogen/transactions/ConfidentialMPTSend.h @@ -268,7 +268,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfMPTokenIssuanceID (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/CredentialAccept.h b/include/xrpl/protocol_autogen/transactions/CredentialAccept.h index 152c18ea09..f2ab546320 100644 --- a/include/xrpl/protocol_autogen/transactions/CredentialAccept.h +++ b/include/xrpl/protocol_autogen/transactions/CredentialAccept.h @@ -112,7 +112,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfIssuer (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/CredentialCreate.h b/include/xrpl/protocol_autogen/transactions/CredentialCreate.h index d7e056f590..6cf09c852b 100644 --- a/include/xrpl/protocol_autogen/transactions/CredentialCreate.h +++ b/include/xrpl/protocol_autogen/transactions/CredentialCreate.h @@ -164,7 +164,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfSubject (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/CredentialDelete.h b/include/xrpl/protocol_autogen/transactions/CredentialDelete.h index 512f230a26..24a2bfa62a 100644 --- a/include/xrpl/protocol_autogen/transactions/CredentialDelete.h +++ b/include/xrpl/protocol_autogen/transactions/CredentialDelete.h @@ -151,7 +151,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfSubject (SoeOptional) diff --git a/include/xrpl/protocol_autogen/transactions/DIDDelete.h b/include/xrpl/protocol_autogen/transactions/DIDDelete.h index 5f90821bfd..304287883d 100644 --- a/include/xrpl/protocol_autogen/transactions/DIDDelete.h +++ b/include/xrpl/protocol_autogen/transactions/DIDDelete.h @@ -86,7 +86,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Build and return the DIDDelete wrapper. diff --git a/include/xrpl/protocol_autogen/transactions/DIDSet.h b/include/xrpl/protocol_autogen/transactions/DIDSet.h index 27242cda71..67e5ba23c5 100644 --- a/include/xrpl/protocol_autogen/transactions/DIDSet.h +++ b/include/xrpl/protocol_autogen/transactions/DIDSet.h @@ -164,7 +164,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfDIDDocument (SoeOptional) diff --git a/include/xrpl/protocol_autogen/transactions/DelegateSet.h b/include/xrpl/protocol_autogen/transactions/DelegateSet.h index 1f454ff4a1..592a778952 100644 --- a/include/xrpl/protocol_autogen/transactions/DelegateSet.h +++ b/include/xrpl/protocol_autogen/transactions/DelegateSet.h @@ -112,7 +112,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfAuthorize (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/DepositPreauth.h b/include/xrpl/protocol_autogen/transactions/DepositPreauth.h index d303ffbeff..b5d575aac5 100644 --- a/include/xrpl/protocol_autogen/transactions/DepositPreauth.h +++ b/include/xrpl/protocol_autogen/transactions/DepositPreauth.h @@ -186,7 +186,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfAuthorize (SoeOptional) diff --git a/include/xrpl/protocol_autogen/transactions/EnableAmendment.h b/include/xrpl/protocol_autogen/transactions/EnableAmendment.h index b397c5df87..e811ca16df 100644 --- a/include/xrpl/protocol_autogen/transactions/EnableAmendment.h +++ b/include/xrpl/protocol_autogen/transactions/EnableAmendment.h @@ -112,7 +112,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfLedgerSequence (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/EscrowCancel.h b/include/xrpl/protocol_autogen/transactions/EscrowCancel.h index 4da943c351..e7e49eca0d 100644 --- a/include/xrpl/protocol_autogen/transactions/EscrowCancel.h +++ b/include/xrpl/protocol_autogen/transactions/EscrowCancel.h @@ -112,7 +112,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfOwner (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/EscrowCreate.h b/include/xrpl/protocol_autogen/transactions/EscrowCreate.h index 35775c31ae..b994e4ec07 100644 --- a/include/xrpl/protocol_autogen/transactions/EscrowCreate.h +++ b/include/xrpl/protocol_autogen/transactions/EscrowCreate.h @@ -217,7 +217,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfDestination (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/EscrowFinish.h b/include/xrpl/protocol_autogen/transactions/EscrowFinish.h index f6ca73d209..2476def5c2 100644 --- a/include/xrpl/protocol_autogen/transactions/EscrowFinish.h +++ b/include/xrpl/protocol_autogen/transactions/EscrowFinish.h @@ -190,7 +190,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfOwner (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/LedgerStateFix.h b/include/xrpl/protocol_autogen/transactions/LedgerStateFix.h index 52723ad5eb..af86dea0b0 100644 --- a/include/xrpl/protocol_autogen/transactions/LedgerStateFix.h +++ b/include/xrpl/protocol_autogen/transactions/LedgerStateFix.h @@ -151,7 +151,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfLedgerFixType (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/LoanBrokerCoverClawback.h b/include/xrpl/protocol_autogen/transactions/LoanBrokerCoverClawback.h index 4842381362..875e0a4c5e 100644 --- a/include/xrpl/protocol_autogen/transactions/LoanBrokerCoverClawback.h +++ b/include/xrpl/protocol_autogen/transactions/LoanBrokerCoverClawback.h @@ -139,7 +139,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfLoanBrokerID (SoeOptional) diff --git a/include/xrpl/protocol_autogen/transactions/LoanBrokerCoverDeposit.h b/include/xrpl/protocol_autogen/transactions/LoanBrokerCoverDeposit.h index 98cebdccb2..38cc113844 100644 --- a/include/xrpl/protocol_autogen/transactions/LoanBrokerCoverDeposit.h +++ b/include/xrpl/protocol_autogen/transactions/LoanBrokerCoverDeposit.h @@ -113,7 +113,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfLoanBrokerID (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/LoanBrokerCoverWithdraw.h b/include/xrpl/protocol_autogen/transactions/LoanBrokerCoverWithdraw.h index e734c6802a..56a93acbb4 100644 --- a/include/xrpl/protocol_autogen/transactions/LoanBrokerCoverWithdraw.h +++ b/include/xrpl/protocol_autogen/transactions/LoanBrokerCoverWithdraw.h @@ -165,7 +165,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfLoanBrokerID (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/LoanBrokerDelete.h b/include/xrpl/protocol_autogen/transactions/LoanBrokerDelete.h index 55c0a12381..29b3a787fd 100644 --- a/include/xrpl/protocol_autogen/transactions/LoanBrokerDelete.h +++ b/include/xrpl/protocol_autogen/transactions/LoanBrokerDelete.h @@ -99,7 +99,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfLoanBrokerID (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/LoanBrokerSet.h b/include/xrpl/protocol_autogen/transactions/LoanBrokerSet.h index 854022242d..41c87c281d 100644 --- a/include/xrpl/protocol_autogen/transactions/LoanBrokerSet.h +++ b/include/xrpl/protocol_autogen/transactions/LoanBrokerSet.h @@ -255,7 +255,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfVaultID (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/LoanDelete.h b/include/xrpl/protocol_autogen/transactions/LoanDelete.h index 70c9e50097..8ed537b37a 100644 --- a/include/xrpl/protocol_autogen/transactions/LoanDelete.h +++ b/include/xrpl/protocol_autogen/transactions/LoanDelete.h @@ -99,7 +99,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfLoanID (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/LoanManage.h b/include/xrpl/protocol_autogen/transactions/LoanManage.h index f11782c4e1..5eb95d21b1 100644 --- a/include/xrpl/protocol_autogen/transactions/LoanManage.h +++ b/include/xrpl/protocol_autogen/transactions/LoanManage.h @@ -99,7 +99,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfLoanID (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/LoanPay.h b/include/xrpl/protocol_autogen/transactions/LoanPay.h index 4012225e17..8e1faeb981 100644 --- a/include/xrpl/protocol_autogen/transactions/LoanPay.h +++ b/include/xrpl/protocol_autogen/transactions/LoanPay.h @@ -113,7 +113,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfLoanID (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/LoanSet.h b/include/xrpl/protocol_autogen/transactions/LoanSet.h index 3fa3c905c2..2cadebd02e 100644 --- a/include/xrpl/protocol_autogen/transactions/LoanSet.h +++ b/include/xrpl/protocol_autogen/transactions/LoanSet.h @@ -500,7 +500,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfLoanBrokerID (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/MPTokenAuthorize.h b/include/xrpl/protocol_autogen/transactions/MPTokenAuthorize.h index 90440c41f9..2fb93eaf35 100644 --- a/include/xrpl/protocol_autogen/transactions/MPTokenAuthorize.h +++ b/include/xrpl/protocol_autogen/transactions/MPTokenAuthorize.h @@ -125,7 +125,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfMPTokenIssuanceID (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceCreate.h b/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceCreate.h index d723a3041c..e6fece8354 100644 --- a/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceCreate.h +++ b/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceCreate.h @@ -242,7 +242,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfAssetScale (SoeOptional) diff --git a/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceDestroy.h b/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceDestroy.h index 19c4530792..cbcd206097 100644 --- a/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceDestroy.h +++ b/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceDestroy.h @@ -99,7 +99,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfMPTokenIssuanceID (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceSet.h b/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceSet.h index 8099af1148..803868c640 100644 --- a/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceSet.h +++ b/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceSet.h @@ -281,7 +281,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfMPTokenIssuanceID (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/NFTokenAcceptOffer.h b/include/xrpl/protocol_autogen/transactions/NFTokenAcceptOffer.h index 7627437a52..325d2d7fbd 100644 --- a/include/xrpl/protocol_autogen/transactions/NFTokenAcceptOffer.h +++ b/include/xrpl/protocol_autogen/transactions/NFTokenAcceptOffer.h @@ -164,7 +164,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfNFTokenBuyOffer (SoeOptional) diff --git a/include/xrpl/protocol_autogen/transactions/NFTokenBurn.h b/include/xrpl/protocol_autogen/transactions/NFTokenBurn.h index 9f981b1ec4..ec423ea468 100644 --- a/include/xrpl/protocol_autogen/transactions/NFTokenBurn.h +++ b/include/xrpl/protocol_autogen/transactions/NFTokenBurn.h @@ -125,7 +125,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfNFTokenID (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/NFTokenCancelOffer.h b/include/xrpl/protocol_autogen/transactions/NFTokenCancelOffer.h index 2eca27fc8d..4c4fb1dc65 100644 --- a/include/xrpl/protocol_autogen/transactions/NFTokenCancelOffer.h +++ b/include/xrpl/protocol_autogen/transactions/NFTokenCancelOffer.h @@ -99,7 +99,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfNFTokenOffers (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/NFTokenCreateOffer.h b/include/xrpl/protocol_autogen/transactions/NFTokenCreateOffer.h index a5c6e226d9..a535a578e0 100644 --- a/include/xrpl/protocol_autogen/transactions/NFTokenCreateOffer.h +++ b/include/xrpl/protocol_autogen/transactions/NFTokenCreateOffer.h @@ -190,7 +190,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfNFTokenID (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/NFTokenMint.h b/include/xrpl/protocol_autogen/transactions/NFTokenMint.h index ad68f1a18b..5af41eb3dd 100644 --- a/include/xrpl/protocol_autogen/transactions/NFTokenMint.h +++ b/include/xrpl/protocol_autogen/transactions/NFTokenMint.h @@ -255,7 +255,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfNFTokenTaxon (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/NFTokenModify.h b/include/xrpl/protocol_autogen/transactions/NFTokenModify.h index 277146ce78..9b9701fed6 100644 --- a/include/xrpl/protocol_autogen/transactions/NFTokenModify.h +++ b/include/xrpl/protocol_autogen/transactions/NFTokenModify.h @@ -151,7 +151,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfNFTokenID (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/OfferCancel.h b/include/xrpl/protocol_autogen/transactions/OfferCancel.h index 833924f683..5e6010e0dd 100644 --- a/include/xrpl/protocol_autogen/transactions/OfferCancel.h +++ b/include/xrpl/protocol_autogen/transactions/OfferCancel.h @@ -99,7 +99,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfOfferSequence (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/OfferCreate.h b/include/xrpl/protocol_autogen/transactions/OfferCreate.h index 13e868643f..ffc1216297 100644 --- a/include/xrpl/protocol_autogen/transactions/OfferCreate.h +++ b/include/xrpl/protocol_autogen/transactions/OfferCreate.h @@ -192,7 +192,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfTakerPays (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/OracleDelete.h b/include/xrpl/protocol_autogen/transactions/OracleDelete.h index d6d46d1d7a..ebdc8fb7e9 100644 --- a/include/xrpl/protocol_autogen/transactions/OracleDelete.h +++ b/include/xrpl/protocol_autogen/transactions/OracleDelete.h @@ -99,7 +99,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfOracleDocumentID (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/OracleSet.h b/include/xrpl/protocol_autogen/transactions/OracleSet.h index 1d295b4cc2..0ec6d5cad0 100644 --- a/include/xrpl/protocol_autogen/transactions/OracleSet.h +++ b/include/xrpl/protocol_autogen/transactions/OracleSet.h @@ -203,7 +203,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfOracleDocumentID (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/Payment.h b/include/xrpl/protocol_autogen/transactions/Payment.h index b2e82cd6af..389900bf12 100644 --- a/include/xrpl/protocol_autogen/transactions/Payment.h +++ b/include/xrpl/protocol_autogen/transactions/Payment.h @@ -295,7 +295,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfDestination (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/PaymentChannelClaim.h b/include/xrpl/protocol_autogen/transactions/PaymentChannelClaim.h index 1db3057366..4c567b13f4 100644 --- a/include/xrpl/protocol_autogen/transactions/PaymentChannelClaim.h +++ b/include/xrpl/protocol_autogen/transactions/PaymentChannelClaim.h @@ -229,7 +229,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfChannel (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/PaymentChannelCreate.h b/include/xrpl/protocol_autogen/transactions/PaymentChannelCreate.h index cf0cbae2aa..0a513d575a 100644 --- a/include/xrpl/protocol_autogen/transactions/PaymentChannelCreate.h +++ b/include/xrpl/protocol_autogen/transactions/PaymentChannelCreate.h @@ -190,7 +190,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfDestination (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/PaymentChannelFund.h b/include/xrpl/protocol_autogen/transactions/PaymentChannelFund.h index 3612fcbf48..51210dd796 100644 --- a/include/xrpl/protocol_autogen/transactions/PaymentChannelFund.h +++ b/include/xrpl/protocol_autogen/transactions/PaymentChannelFund.h @@ -138,7 +138,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfChannel (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/PermissionedDomainDelete.h b/include/xrpl/protocol_autogen/transactions/PermissionedDomainDelete.h index e08ff1ed5a..3db921776c 100644 --- a/include/xrpl/protocol_autogen/transactions/PermissionedDomainDelete.h +++ b/include/xrpl/protocol_autogen/transactions/PermissionedDomainDelete.h @@ -99,7 +99,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfDomainID (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/PermissionedDomainSet.h b/include/xrpl/protocol_autogen/transactions/PermissionedDomainSet.h index 68f1cfebb2..3e352cad76 100644 --- a/include/xrpl/protocol_autogen/transactions/PermissionedDomainSet.h +++ b/include/xrpl/protocol_autogen/transactions/PermissionedDomainSet.h @@ -125,7 +125,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfDomainID (SoeOptional) diff --git a/include/xrpl/protocol_autogen/transactions/SetFee.h b/include/xrpl/protocol_autogen/transactions/SetFee.h index bc5fc0e603..177f39199b 100644 --- a/include/xrpl/protocol_autogen/transactions/SetFee.h +++ b/include/xrpl/protocol_autogen/transactions/SetFee.h @@ -294,7 +294,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfLedgerSequence (SoeOptional) diff --git a/include/xrpl/protocol_autogen/transactions/SetRegularKey.h b/include/xrpl/protocol_autogen/transactions/SetRegularKey.h index 1eca1bef25..a943bb0279 100644 --- a/include/xrpl/protocol_autogen/transactions/SetRegularKey.h +++ b/include/xrpl/protocol_autogen/transactions/SetRegularKey.h @@ -112,7 +112,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfRegularKey (SoeOptional) diff --git a/include/xrpl/protocol_autogen/transactions/SignerListSet.h b/include/xrpl/protocol_autogen/transactions/SignerListSet.h index c711864e95..6e9d0e41ba 100644 --- a/include/xrpl/protocol_autogen/transactions/SignerListSet.h +++ b/include/xrpl/protocol_autogen/transactions/SignerListSet.h @@ -123,7 +123,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfSignerQuorum (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/SponsorshipSet.h b/include/xrpl/protocol_autogen/transactions/SponsorshipSet.h index 2831c8a077..0124da5e58 100644 --- a/include/xrpl/protocol_autogen/transactions/SponsorshipSet.h +++ b/include/xrpl/protocol_autogen/transactions/SponsorshipSet.h @@ -216,7 +216,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfCounterpartySponsor (SoeOptional) diff --git a/include/xrpl/protocol_autogen/transactions/SponsorshipTransfer.h b/include/xrpl/protocol_autogen/transactions/SponsorshipTransfer.h index bc27f1603b..ab26e887e3 100644 --- a/include/xrpl/protocol_autogen/transactions/SponsorshipTransfer.h +++ b/include/xrpl/protocol_autogen/transactions/SponsorshipTransfer.h @@ -138,7 +138,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfObjectID (SoeOptional) diff --git a/include/xrpl/protocol_autogen/transactions/TicketCreate.h b/include/xrpl/protocol_autogen/transactions/TicketCreate.h index 0b4206152e..0d8670a76a 100644 --- a/include/xrpl/protocol_autogen/transactions/TicketCreate.h +++ b/include/xrpl/protocol_autogen/transactions/TicketCreate.h @@ -99,7 +99,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfTicketCount (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/TrustSet.h b/include/xrpl/protocol_autogen/transactions/TrustSet.h index 30d537492d..22891b94ec 100644 --- a/include/xrpl/protocol_autogen/transactions/TrustSet.h +++ b/include/xrpl/protocol_autogen/transactions/TrustSet.h @@ -164,7 +164,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfLimitAmount (SoeOptional) diff --git a/include/xrpl/protocol_autogen/transactions/UNLModify.h b/include/xrpl/protocol_autogen/transactions/UNLModify.h index a8556ca699..6569e4bf7d 100644 --- a/include/xrpl/protocol_autogen/transactions/UNLModify.h +++ b/include/xrpl/protocol_autogen/transactions/UNLModify.h @@ -125,7 +125,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfUNLModifyDisabling (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/VaultClawback.h b/include/xrpl/protocol_autogen/transactions/VaultClawback.h index 8ec1d359dc..270ccc94bb 100644 --- a/include/xrpl/protocol_autogen/transactions/VaultClawback.h +++ b/include/xrpl/protocol_autogen/transactions/VaultClawback.h @@ -139,7 +139,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfVaultID (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/VaultCreate.h b/include/xrpl/protocol_autogen/transactions/VaultCreate.h index c8efa83cbf..b7e1527754 100644 --- a/include/xrpl/protocol_autogen/transactions/VaultCreate.h +++ b/include/xrpl/protocol_autogen/transactions/VaultCreate.h @@ -256,7 +256,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfAsset (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/VaultDelete.h b/include/xrpl/protocol_autogen/transactions/VaultDelete.h index b4c08ae229..67cc32f543 100644 --- a/include/xrpl/protocol_autogen/transactions/VaultDelete.h +++ b/include/xrpl/protocol_autogen/transactions/VaultDelete.h @@ -125,7 +125,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfVaultID (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/VaultDeposit.h b/include/xrpl/protocol_autogen/transactions/VaultDeposit.h index 01f707e07f..5bb5362114 100644 --- a/include/xrpl/protocol_autogen/transactions/VaultDeposit.h +++ b/include/xrpl/protocol_autogen/transactions/VaultDeposit.h @@ -113,7 +113,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfVaultID (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/VaultSet.h b/include/xrpl/protocol_autogen/transactions/VaultSet.h index 5bdc6a7a95..14df70f13b 100644 --- a/include/xrpl/protocol_autogen/transactions/VaultSet.h +++ b/include/xrpl/protocol_autogen/transactions/VaultSet.h @@ -177,7 +177,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfVaultID (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/VaultWithdraw.h b/include/xrpl/protocol_autogen/transactions/VaultWithdraw.h index 75b5b90035..3211524e1f 100644 --- a/include/xrpl/protocol_autogen/transactions/VaultWithdraw.h +++ b/include/xrpl/protocol_autogen/transactions/VaultWithdraw.h @@ -165,7 +165,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfVaultID (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/XChainAccountCreateCommit.h b/include/xrpl/protocol_autogen/transactions/XChainAccountCreateCommit.h index 36d8170af1..b8d551c5e1 100644 --- a/include/xrpl/protocol_autogen/transactions/XChainAccountCreateCommit.h +++ b/include/xrpl/protocol_autogen/transactions/XChainAccountCreateCommit.h @@ -138,7 +138,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfXChainBridge (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/XChainAddAccountCreateAttestation.h b/include/xrpl/protocol_autogen/transactions/XChainAddAccountCreateAttestation.h index a04eec1505..22b57803dc 100644 --- a/include/xrpl/protocol_autogen/transactions/XChainAddAccountCreateAttestation.h +++ b/include/xrpl/protocol_autogen/transactions/XChainAddAccountCreateAttestation.h @@ -229,7 +229,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfXChainBridge (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/XChainAddClaimAttestation.h b/include/xrpl/protocol_autogen/transactions/XChainAddClaimAttestation.h index 1896f11681..5e80c05aae 100644 --- a/include/xrpl/protocol_autogen/transactions/XChainAddClaimAttestation.h +++ b/include/xrpl/protocol_autogen/transactions/XChainAddClaimAttestation.h @@ -229,7 +229,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfXChainBridge (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/XChainClaim.h b/include/xrpl/protocol_autogen/transactions/XChainClaim.h index f5b680049e..ec403b5eb8 100644 --- a/include/xrpl/protocol_autogen/transactions/XChainClaim.h +++ b/include/xrpl/protocol_autogen/transactions/XChainClaim.h @@ -164,7 +164,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfXChainBridge (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/XChainCommit.h b/include/xrpl/protocol_autogen/transactions/XChainCommit.h index a9ce7d1d08..48b2263645 100644 --- a/include/xrpl/protocol_autogen/transactions/XChainCommit.h +++ b/include/xrpl/protocol_autogen/transactions/XChainCommit.h @@ -151,7 +151,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfXChainBridge (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/XChainCreateBridge.h b/include/xrpl/protocol_autogen/transactions/XChainCreateBridge.h index ae5be4108f..9614b0bd88 100644 --- a/include/xrpl/protocol_autogen/transactions/XChainCreateBridge.h +++ b/include/xrpl/protocol_autogen/transactions/XChainCreateBridge.h @@ -138,7 +138,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfXChainBridge (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/XChainCreateClaimID.h b/include/xrpl/protocol_autogen/transactions/XChainCreateClaimID.h index 7c1beb20c7..d17759619f 100644 --- a/include/xrpl/protocol_autogen/transactions/XChainCreateClaimID.h +++ b/include/xrpl/protocol_autogen/transactions/XChainCreateClaimID.h @@ -125,7 +125,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfXChainBridge (SoeRequired) diff --git a/include/xrpl/protocol_autogen/transactions/XChainModifyBridge.h b/include/xrpl/protocol_autogen/transactions/XChainModifyBridge.h index 30558ab88c..e79c9139ce 100644 --- a/include/xrpl/protocol_autogen/transactions/XChainModifyBridge.h +++ b/include/xrpl/protocol_autogen/transactions/XChainModifyBridge.h @@ -151,7 +151,9 @@ public: object_ = *tx; } - /** @brief Transaction-specific field setters */ + /** + * @brief Transaction-specific field setters + */ /** * @brief Set sfXChainBridge (SoeRequired) diff --git a/include/xrpl/rdb/RelationalDatabase.h b/include/xrpl/rdb/RelationalDatabase.h index 91c282ff16..c77c8a7ed3 100644 --- a/include/xrpl/rdb/RelationalDatabase.h +++ b/include/xrpl/rdb/RelationalDatabase.h @@ -65,8 +65,10 @@ public: struct AccountTxOptions { AccountID const& account; - /// Ledger sequence range to search. A value of 0 for min or max - /// means unbounded in that direction (no constraint applied). + /** + * Ledger sequence range to search. A value of 0 for min or max + * means unbounded in that direction (no constraint applied). + */ LedgerRange ledgerRange{}; std::uint32_t offset = 0; std::uint32_t limit = 0; diff --git a/include/xrpl/rdb/SociDB.h b/include/xrpl/rdb/SociDB.h index 0f427bd18b..61edcf2263 100644 --- a/include/xrpl/rdb/SociDB.h +++ b/include/xrpl/rdb/SociDB.h @@ -1,12 +1,13 @@ #pragma once -/** An embedded database wrapper with an intuitive, type-safe interface. - - This collection of classes let's you access embedded SQLite databases - using C++ syntax that is very similar to regular SQL. - - This module requires the @ref beast_sqlite external module. -*/ +/** + * An embedded database wrapper with an intuitive, type-safe interface. + * + * This collection of classes let's you access embedded SQLite databases + * using C++ syntax that is very similar to regular SQL. + * + * This module requires the @ref beast_sqlite external module. + */ #include #include @@ -35,9 +36,9 @@ namespace xrpl { class BasicConfig; /** - DBConfig is used when a client wants to delay opening a soci::session after - parsing the config parameters. If a client want to open a session - immediately, use the free function "open" below. + * DBConfig is used when a client wants to delay opening a soci::session after + * parsing the config parameters. If a client want to open a session + * immediately, use the free function "open" below. */ class DBConfig { @@ -53,27 +54,27 @@ public: }; /** - Open a soci session. - - @param s Session to open. - - @param config Parameters to pick the soci backend and how to connect to that - backend. - - @param dbName Name of the database. This has different meaning for different - backends. Sometimes it is part of a filename (sqlite3), - other times it is a database name (postgresql). -*/ + * Open a soci session. + * + * @param s Session to open. + * + * @param config Parameters to pick the soci backend and how to connect to that + * backend. + * + * @param dbName Name of the database. This has different meaning for different + * backends. Sometimes it is part of a filename (sqlite3), + * other times it is a database name (postgresql). + */ void open(soci::session& s, BasicConfig const& config, std::string const& dbName); /** - * Open a soci session. + * Open a soci session. * - * @param s Session to open. - * @param beName Backend name. - * @param connectionString Connection string to forward to soci::open. - * see the soci::open documentation for how to use this. + * @param s Session to open. + * @param beName Backend name. + * @param connectionString Connection string to forward to soci::open. + * see the soci::open documentation for how to use this. * */ void @@ -107,11 +108,12 @@ public: checkpoint() = 0; }; -/** Returns a new checkpointer which makes checkpoints of a - soci database every checkpointPageCount pages, using a job on the job queue. - - The checkpointer contains references to the session and job queue - and so must outlive them both. +/** + * Returns a new checkpointer which makes checkpoints of a + * soci database every checkpointPageCount pages, using a job on the job queue. + * + * The checkpointer contains references to the session and job queue + * and so must outlive them both. */ std::shared_ptr makeCheckpointer(std::uintptr_t id, std::weak_ptr, JobQueue&, ServiceRegistry&); diff --git a/include/xrpl/resource/Charge.h b/include/xrpl/resource/Charge.h index 394d641e6c..12ea548fd2 100644 --- a/include/xrpl/resource/Charge.h +++ b/include/xrpl/resource/Charge.h @@ -6,28 +6,40 @@ namespace xrpl::Resource { -/** A consumption charge. */ +/** + * A consumption charge. + */ class Charge { public: - /** The type used to hold a consumption charge. */ + /** + * The type used to hold a consumption charge. + */ using value_type = int; // A default constructed Charge has no way to get a label. Delete Charge() = delete; - /** Create a charge with the specified cost and name. */ + /** + * Create a charge with the specified cost and name. + */ Charge(value_type cost, std::string label = std::string()); - /** Return the human readable label associated with the charge. */ + /** + * Return the human readable label associated with the charge. + */ [[nodiscard]] std::string const& label() const; - /** Return the cost of the charge in Resource::Manager units. */ + /** + * Return the cost of the charge in Resource::Manager units. + */ [[nodiscard]] value_type cost() const; - /** Converts this charge into a human readable string. */ + /** + * Converts this charge into a human readable string. + */ [[nodiscard]] std::string toString() const; diff --git a/include/xrpl/resource/Consumer.h b/include/xrpl/resource/Consumer.h index 18207832ac..9abcbffc82 100644 --- a/include/xrpl/resource/Consumer.h +++ b/include/xrpl/resource/Consumer.h @@ -13,7 +13,9 @@ namespace xrpl::Resource { struct Entry; class Logic; -/** An endpoint that consumes resources. */ +/** + * An endpoint that consumes resources. + */ class Consumer { private: @@ -27,42 +29,55 @@ public: Consumer& operator=(Consumer const& other); - /** Return a human readable string uniquely identifying this consumer. */ + /** + * Return a human readable string uniquely identifying this consumer. + */ [[nodiscard]] std::string toString() const; - /** Returns `true` if this is a privileged endpoint. */ + /** + * Returns `true` if this is a privileged endpoint. + */ [[nodiscard]] bool isUnlimited() const; - /** Raise the Consumer's privilege level to a Named endpoint. - The reference to the original endpoint descriptor is released. - */ + /** + * Raise the Consumer's privilege level to a Named endpoint. + * The reference to the original endpoint descriptor is released. + */ void elevate(std::string const& name); - /** Returns the current disposition of this consumer. - This should be checked upon creation to determine if the consumer - should be disconnected immediately. - */ + /** + * Returns the current disposition of this consumer. + * This should be checked upon creation to determine if the consumer + * should be disconnected immediately. + */ [[nodiscard]] Disposition disposition() const; - /** Apply a load charge to the consumer. */ + /** + * Apply a load charge to the consumer. + */ Disposition charge(Charge const& fee, std::string const& context = {}); - /** Returns `true` if the consumer should be warned. - This consumes the warning. - */ + /** + * Returns `true` if the consumer should be warned. + * This consumes the warning. + */ bool warn(); - /** Returns `true` if the consumer should be disconnected. */ + /** + * Returns `true` if the consumer should be disconnected. + */ bool disconnect(beast::Journal const& j); - /** Returns the credit balance representing consumption. */ + /** + * Returns the credit balance representing consumption. + */ int balance(); diff --git a/include/xrpl/resource/Disposition.h b/include/xrpl/resource/Disposition.h index 6dad8db19a..cd5bceafa5 100644 --- a/include/xrpl/resource/Disposition.h +++ b/include/xrpl/resource/Disposition.h @@ -2,16 +2,24 @@ namespace xrpl::Resource { -/** The disposition of a consumer after applying a load charge. */ +/** + * The disposition of a consumer after applying a load charge. + */ enum class Disposition { - /** No action required. */ + /** + * No action required. + */ Ok - /** Consumer should be warned that consumption is high. */ + /** + * Consumer should be warned that consumption is high. + */ , Warn - /** Consumer should be disconnected for excess consumption. */ + /** + * Consumer should be disconnected for excess consumption. + */ , Drop }; diff --git a/include/xrpl/resource/Fees.h b/include/xrpl/resource/Fees.h index 55d539ac6a..5001b504d6 100644 --- a/include/xrpl/resource/Fees.h +++ b/include/xrpl/resource/Fees.h @@ -4,7 +4,9 @@ namespace xrpl::Resource { -/** Schedule of fees charged for imposing load on the server. */ +/** + * Schedule of fees charged for imposing load on the server. + */ /** @{ */ extern Charge const kFeeMalformedRequest; // A request that we can immediately tell is invalid. extern Charge const kFeeRequestNoReply; // A request that we cannot satisfy. diff --git a/include/xrpl/resource/Gossip.h b/include/xrpl/resource/Gossip.h index e626af37c3..4ad5852de0 100644 --- a/include/xrpl/resource/Gossip.h +++ b/include/xrpl/resource/Gossip.h @@ -6,12 +6,16 @@ namespace xrpl::Resource { -/** Data format for exchanging consumption information across peers. */ +/** + * Data format for exchanging consumption information across peers. + */ struct Gossip { explicit Gossip() = default; - /** Describes a single consumer. */ + /** + * Describes a single consumer. + */ struct Item { explicit Item() = default; diff --git a/include/xrpl/resource/ResourceManager.h b/include/xrpl/resource/ResourceManager.h index 13e0d09343..03aab60c75 100644 --- a/include/xrpl/resource/ResourceManager.h +++ b/include/xrpl/resource/ResourceManager.h @@ -16,7 +16,9 @@ namespace xrpl::Resource { -/** Tracks load and resource consumption. */ +/** + * Tracks load and resource consumption. + */ class Manager : public beast::PropertyStream::Source { protected: @@ -25,8 +27,10 @@ protected: public: ~Manager() override = 0; - /** Create a new endpoint keyed by inbound IP address or the forwarded - * IP if proxied. */ + /** + * Create a new endpoint keyed by inbound IP address or the forwarded + * IP if proxied. + */ virtual Consumer newInboundEndpoint(beast::IP::Endpoint const& address) = 0; virtual Consumer @@ -35,27 +39,36 @@ public: bool const proxy, std::string_view forwardedFor) = 0; - /** Create a new endpoint keyed by outbound IP address and port. */ + /** + * Create a new endpoint keyed by outbound IP address and port. + */ virtual Consumer newOutboundEndpoint(beast::IP::Endpoint const& address) = 0; - /** Create a new unlimited endpoint keyed by forwarded IP. */ + /** + * Create a new unlimited endpoint keyed by forwarded IP. + */ virtual Consumer newUnlimitedEndpoint(beast::IP::Endpoint const& address) = 0; - /** Extract packaged consumer information for export. */ + /** + * Extract packaged consumer information for export. + */ virtual Gossip exportConsumers() = 0; - /** Extract consumer information for reporting. */ + /** + * Extract consumer information for reporting. + */ virtual json::Value getJson() = 0; virtual json::Value getJson(int threshold) = 0; - /** Import packaged consumer information. - @param origin An identifier that unique labels the origin. - */ + /** + * Import packaged consumer information. + * @param origin An identifier that unique labels the origin. + */ virtual void importConsumers(std::string const& origin, Gossip const& gossip) = 0; }; diff --git a/include/xrpl/resource/detail/Entry.h b/include/xrpl/resource/detail/Entry.h index 6f44ac2c29..1336bda6ab 100644 --- a/include/xrpl/resource/detail/Entry.h +++ b/include/xrpl/resource/detail/Entry.h @@ -23,8 +23,8 @@ struct Entry : public beast::List::Node Entry() = delete; /** - @param now Construction time of Entry. - */ + * @param now Construction time of Entry. + */ explicit Entry(clock_type::time_point const now) : refcount(0), localBalance(now), remoteBalance(0) { diff --git a/include/xrpl/resource/detail/Import.h b/include/xrpl/resource/detail/Import.h index a3df6fd73b..b19dbc4d1a 100644 --- a/include/xrpl/resource/detail/Import.h +++ b/include/xrpl/resource/detail/Import.h @@ -7,7 +7,9 @@ namespace xrpl::Resource { -/** A set of imported consumer data from a gossip origin. */ +/** + * A set of imported consumer data from a gossip origin. + */ struct Import { struct Item diff --git a/include/xrpl/resource/detail/Logic.h b/include/xrpl/resource/detail/Logic.h index 78cd0ac36c..3f36ad84a3 100644 --- a/include/xrpl/resource/detail/Logic.h +++ b/include/xrpl/resource/detail/Logic.h @@ -192,7 +192,9 @@ public: return getJson(kWarningThreshold); } - /** Returns a json::ValueType::Object. */ + /** + * Returns a json::ValueType::Object. + */ json::Value getJson(int threshold) { diff --git a/include/xrpl/resource/detail/Tuning.h b/include/xrpl/resource/detail/Tuning.h index 7b2046f45c..62f7fa3f9d 100644 --- a/include/xrpl/resource/detail/Tuning.h +++ b/include/xrpl/resource/detail/Tuning.h @@ -4,7 +4,9 @@ namespace xrpl::Resource { -/** Tunable constants. */ +/** + * Tunable constants. + */ // balance at which a warning is issued static constexpr auto kWarningThreshold = 5000; diff --git a/include/xrpl/server/Handoff.h b/include/xrpl/server/Handoff.h index b80a9c5745..6dc547467b 100644 --- a/include/xrpl/server/Handoff.h +++ b/include/xrpl/server/Handoff.h @@ -13,7 +13,9 @@ using http_request_type = boost::beast::http::request; -/** Used to indicate the result of a server connection handoff. */ +/** + * Used to indicate the result of a server connection handoff. + */ struct Handoff { // When `true`, the Session will close the socket. The diff --git a/include/xrpl/server/InfoSub.h b/include/xrpl/server/InfoSub.h index d708a456c4..2e9bd857c7 100644 --- a/include/xrpl/server/InfoSub.h +++ b/include/xrpl/server/InfoSub.h @@ -35,20 +35,21 @@ public: doStatus(json::Value const&) = 0; }; -/** Manages a client's subscription to data feeds. +/** + * Manages a client's subscription to data feeds. * - * An InfoSub holds a non-owning reference to its `Source` (typically the - * process-wide `NetworkOPsImp`). The destructor reaches back into the - * `Source` to remove this subscriber from every server-side subscription - * map. + * An InfoSub holds a non-owning reference to its `Source` (typically the + * process-wide `NetworkOPsImp`). The destructor reaches back into the + * `Source` to remove this subscriber from every server-side subscription + * map. * - * @note Lifetime contract: every `InfoSub` instance MUST be destroyed - * before the backing `Source`. NetworkOPsImp shutdown drops all - * subscriber strong refs before its own teardown to satisfy this. - * @note Thread-safety: per-instance state is guarded by `lock_`. The - * destructor reads tracking sets without taking `lock_` because - * the strong-pointer ref-count is zero at destruction time, so - * no other thread can be calling the public mutators. + * @note Lifetime contract: every `InfoSub` instance MUST be destroyed + * before the backing `Source`. NetworkOPsImp shutdown drops all + * subscriber strong refs before its own teardown to satisfy this. + * @note Thread-safety: per-instance state is guarded by `lock_`. The + * destructor reads tracking sets without taking `lock_` because + * the strong-pointer ref-count is zero at destruction time, so + * no other thread can be calling the public mutators. */ class InfoSub : public CountedObject { @@ -64,7 +65,8 @@ public: using Consumer = Resource::Consumer; public: - /** Abstracts the source of subscription data. + /** + * Abstracts the source of subscription data. */ class Source { @@ -217,7 +219,8 @@ public: virtual bool tryRemoveRpcSub(std::string const& strUrl) = 0; - /** Journal used by InfoSub for diagnostics that occur after the + /** + * Journal used by InfoSub for diagnostics that occur after the * owning subsystem (e.g. application-level Logs) is the only * surviving sink — primarily destructor-time cleanup failures. */ @@ -249,27 +252,29 @@ public: void deleteSubAccountInfo(AccountID const& account, bool rt); - /** Record that this subscriber is following @p book. + /** + * Record that this subscriber is following @p book. * - * Called by NetworkOPsImp::subBook so that ~InfoSub() can issue a - * matching unsubBook for every book this subscriber is tracking, - * keeping per-subscriber state symmetric with the server-side map. + * Called by NetworkOPsImp::subBook so that ~InfoSub() can issue a + * matching unsubBook for every book this subscriber is tracking, + * keeping per-subscriber state symmetric with the server-side map. * - * @param book The order book this subscriber has just subscribed to. - * @note Idempotent: re-inserting an already-tracked book is a no-op. - * @note Thread-safe: takes InfoSub::lock_. + * @param book The order book this subscriber has just subscribed to. + * @note Idempotent: re-inserting an already-tracked book is a no-op. + * @note Thread-safe: takes InfoSub::lock_. */ void insertBookSubscription(Book const& book); - /** Stop tracking @p book for this subscriber. + /** + * Stop tracking @p book for this subscriber. * - * Called by the unsubscribe RPC handler so that the book is not - * re-unsubscribed by ~InfoSub(). Pairs with insertBookSubscription. + * Called by the unsubscribe RPC handler so that the book is not + * re-unsubscribed by ~InfoSub(). Pairs with insertBookSubscription. * - * @param book The order book to forget. - * @note No-op if @p book was not previously inserted. - * @note Thread-safe: takes InfoSub::lock_. + * @param book The order book to forget. + * @note No-op if @p book was not previously inserted. + * @note Thread-safe: takes InfoSub::lock_. */ void deleteBookSubscription(Book const& book); diff --git a/include/xrpl/server/LoadFeeTrack.h b/include/xrpl/server/LoadFeeTrack.h index a19ca063d5..3afd602241 100644 --- a/include/xrpl/server/LoadFeeTrack.h +++ b/include/xrpl/server/LoadFeeTrack.h @@ -13,15 +13,16 @@ namespace xrpl { struct Fees; -/** Manages the current fee schedule. - - The "base" fee is the cost to send a reference transaction under no load, - expressed in millionths of one XRP. - - The "load" fee is how much the local server currently charges to send a - reference transaction. This fee fluctuates based on the load of the - server. -*/ +/** + * Manages the current fee schedule. + * + * The "base" fee is the cost to send a reference transaction under no load, + * expressed in millionths of one XRP. + * + * The "load" fee is how much the local server currently charges to send a + * reference transaction. This fee fluctuates based on the load of the + * server. + */ class LoadFeeTrack final { public: diff --git a/include/xrpl/server/Manifest.h b/include/xrpl/server/Manifest.h index c58c784de3..710545271a 100644 --- a/include/xrpl/server/Manifest.h +++ b/include/xrpl/server/Manifest.h @@ -68,22 +68,32 @@ namespace xrpl { struct Manifest { - /// The manifest in serialized form. + /** + * The manifest in serialized form. + */ std::string serialized; - /// The master key associated with this manifest. + /** + * The master key associated with this manifest. + */ PublicKey masterKey; - /// The ephemeral key associated with this manifest. + /** + * The ephemeral key associated with this manifest. + */ // A revoked manifest does not have a signingKey // This field is specified as "optional" in manifestFormat's // SOTemplate std::optional signingKey; - /// The sequence number of this manifest. + /** + * The sequence number of this manifest. + */ std::uint32_t sequence = 0; - /// The domain, if one was specified in the manifest; empty otherwise. + /** + * The domain, if one was specified in the manifest; empty otherwise. + */ std::string domain; Manifest() = delete; @@ -109,46 +119,61 @@ struct Manifest Manifest& operator=(Manifest&& other) = default; - /// Returns `true` if manifest signature is valid + /** + * Returns `true` if manifest signature is valid + */ [[nodiscard]] bool verify() const; - /// Returns hash of serialized manifest data + /** + * Returns hash of serialized manifest data + */ [[nodiscard]] uint256 hash() const; - /// Returns `true` if manifest revokes master key + /** + * Returns `true` if manifest revokes master key + */ // The maximum possible sequence number means that the master key has // been revoked static bool revoked(std::uint32_t sequence); - /// Returns `true` if manifest revokes master key + /** + * Returns `true` if manifest revokes master key + */ [[nodiscard]] bool revoked() const; - /// Returns manifest signature + /** + * Returns manifest signature + */ [[nodiscard]] std::optional getSignature() const; - /// Returns manifest master key signature + /** + * Returns manifest master key signature + */ [[nodiscard]] Blob getMasterSignature() const; }; -/** Format the specified manifest to a string for debugging purposes. */ +/** + * Format the specified manifest to a string for debugging purposes. + */ std::string to_string(Manifest const& m); -/** Constructs Manifest from serialized string - - @param s Serialized manifest string - - @return `std::nullopt` if string is invalid - - @note This does not verify manifest signatures. - `Manifest::verify` should be called after constructing manifest. -*/ +/** + * Constructs Manifest from serialized string + * + * @param s Serialized manifest string + * + * @return `std::nullopt` if string is invalid + * + * @note This does not verify manifest signatures. + * `Manifest::verify` should be called after constructing manifest. + */ /** @{ */ std::optional deserializeManifest(Slice s, beast::Journal journal); @@ -200,19 +225,29 @@ loadValidatorToken( beast::Journal journal = beast::Journal(beast::Journal::getNullSink())); enum class ManifestDisposition { - /// Manifest is valid + /** + * Manifest is valid + */ Accepted = 0, - /// Sequence is too old + /** + * Sequence is too old + */ Stale, - /// The master key is not acceptable to us + /** + * The master key is not acceptable to us + */ BadMasterKey, - /// The ephemeral key is not acceptable to us + /** + * The ephemeral key is not acceptable to us + */ BadEphemeralKey, - /// Timely, but invalid signature + /** + * Timely, but invalid signature + */ Invalid }; @@ -238,17 +273,23 @@ to_string(ManifestDisposition m) class DatabaseCon; -/** Remembers manifests with the highest sequence number. */ +/** + * Remembers manifests with the highest sequence number. + */ class ManifestCache { private: beast::Journal j_; std::shared_mutex mutable mutex_; - /** Active manifests stored by master public key. */ + /** + * Active manifests stored by master public key. + */ hash_map map_; - /** Master public keys stored by current ephemeral public key. */ + /** + * Master public keys stored by current ephemeral public key. + */ hash_map signingToMasterKeys_; std::atomic seq_{0}; @@ -258,104 +299,114 @@ public: { } - /** A monotonically increasing number used to detect new manifests. */ + /** + * A monotonically increasing number used to detect new manifests. + */ std::uint32_t sequence() const { return seq_.load(); } - /** Returns master key's current signing key. - - @param pk Master public key - - @return pk if no known signing key from a manifest - - @par Thread Safety - - May be called concurrently - */ + /** + * Returns master key's current signing key. + * + * @param pk Master public key + * + * @return pk if no known signing key from a manifest + * + * @par Thread Safety + * + * May be called concurrently + */ std::optional getSigningKey(PublicKey const& pk) const; - /** Returns ephemeral signing key's master public key. - - @param pk Ephemeral signing public key - - @return pk if signing key is not in a valid manifest - - @par Thread Safety - - May be called concurrently - */ + /** + * Returns ephemeral signing key's master public key. + * + * @param pk Ephemeral signing public key + * + * @return pk if signing key is not in a valid manifest + * + * @par Thread Safety + * + * May be called concurrently + */ PublicKey getMasterKey(PublicKey const& pk) const; - /** Returns master key's current manifest sequence. - - @return sequence corresponding to Master public key - if configured or std::nullopt otherwise - */ + /** + * Returns master key's current manifest sequence. + * + * @return sequence corresponding to Master public key + * if configured or std::nullopt otherwise + */ std::optional getSequence(PublicKey const& pk) const; - /** Returns domain claimed by a given public key - - @return domain corresponding to Master public key - if present, otherwise std::nullopt - */ + /** + * Returns domain claimed by a given public key + * + * @return domain corresponding to Master public key + * if present, otherwise std::nullopt + */ std::optional getDomain(PublicKey const& pk) const; - /** Returns manifest corresponding to a given public key - - @return manifest corresponding to Master public key - if present, otherwise std::nullopt - */ + /** + * Returns manifest corresponding to a given public key + * + * @return manifest corresponding to Master public key + * if present, otherwise std::nullopt + */ std::optional getManifest(PublicKey const& pk) const; - /** Returns `true` if master key has been revoked in a manifest. - - @param pk Master public key - - @par Thread Safety - - May be called concurrently - */ + /** + * Returns `true` if master key has been revoked in a manifest. + * + * @param pk Master public key + * + * @par Thread Safety + * + * May be called concurrently + */ bool revoked(PublicKey const& pk) const; - /** Add manifest to cache. - - @param m Manifest to add - - @return `ManifestDisposition::accepted` if successful, or - `stale` or `invalid` otherwise - - @par Thread Safety - - May be called concurrently - */ + /** + * Add manifest to cache. + * + * @param m Manifest to add + * + * @return `ManifestDisposition::accepted` if successful, or + * `stale` or `invalid` otherwise + * + * @par Thread Safety + * + * May be called concurrently + */ ManifestDisposition applyManifest(Manifest m); - /** Populate manifest cache with manifests in database and config. - - @param dbCon Database connection with dbTable - - @param dbTable Database table - - @param configManifest Base64 encoded manifest for local node's - validator keys - - @param configRevocation Base64 encoded validator key revocation - from the config - - @par Thread Safety - - May be called concurrently - */ + /** + * Populate manifest cache with manifests in database and config. + * + * @param dbCon Database connection with dbTable + * + * @param dbTable Database table + * + * @param configManifest Base64 encoded manifest for local node's + * validator keys + * + * @param configRevocation Base64 encoded validator key revocation + * from the config + * + * @par Thread Safety + * + * May be called concurrently + */ bool load( DatabaseCon& dbCon, @@ -363,48 +414,51 @@ public: std::string const& configManifest, std::vector const& configRevocation); - /** Populate manifest cache with manifests in database. - - @param dbCon Database connection with dbTable - - @param dbTable Database table - - @par Thread Safety - - May be called concurrently - */ + /** + * Populate manifest cache with manifests in database. + * + * @param dbCon Database connection with dbTable + * + * @param dbTable Database table + * + * @par Thread Safety + * + * May be called concurrently + */ void load(DatabaseCon& dbCon, std::string const& dbTable); - /** Save cached manifests to database. - - @param dbCon Database connection with `ValidatorManifests` table - - @param isTrusted Function that returns true if manifest is trusted - - @par Thread Safety - - May be called concurrently - */ + /** + * Save cached manifests to database. + * + * @param dbCon Database connection with `ValidatorManifests` table + * + * @param isTrusted Function that returns true if manifest is trusted + * + * @par Thread Safety + * + * May be called concurrently + */ void save( DatabaseCon& dbCon, std::string const& dbTable, std::function const& isTrusted); - /** Invokes the callback once for every populated manifest. - - @note Do not call ManifestCache member functions from within the - callback. This can re-lock the mutex from the same thread, which is UB. - @note Do not write ManifestCache member variables from within the - callback. This can lead to data races. - - @param f Function called for each manifest - - @par Thread Safety - - May be called concurrently - */ + /** + * Invokes the callback once for every populated manifest. + * + * @note Do not call ManifestCache member functions from within the + * callback. This can re-lock the mutex from the same thread, which is UB. + * @note Do not write ManifestCache member variables from within the + * callback. This can lead to data races. + * + * @param f Function called for each manifest + * + * @par Thread Safety + * + * May be called concurrently + */ template void forEachManifest(Function&& f) const @@ -417,22 +471,23 @@ public: } } - /** Invokes the callback once for every populated manifest. - - @note Do not call ManifestCache member functions from within the - callback. This can re-lock the mutex from the same thread, which is UB. - @note Do not write ManifestCache member variables from - within the callback. This can lead to data races. - - @param pf Pre-function called with the maximum number of times f will be - called (useful for memory allocations) - - @param f Function called for each manifest - - @par Thread Safety - - May be called concurrently - */ + /** + * Invokes the callback once for every populated manifest. + * + * @note Do not call ManifestCache member functions from within the + * callback. This can re-lock the mutex from the same thread, which is UB. + * @note Do not write ManifestCache member variables from + * within the callback. This can lead to data races. + * + * @param pf Pre-function called with the maximum number of times f will be + * called (useful for memory allocations) + * + * @param f Function called for each manifest + * + * @par Thread Safety + * + * May be called concurrently + */ template void forEachManifest(PreFun&& pf, EachFun&& f) const diff --git a/include/xrpl/server/NetworkOPs.h b/include/xrpl/server/NetworkOPs.h index ed2dbbb220..d3d3bb9f70 100644 --- a/include/xrpl/server/NetworkOPs.h +++ b/include/xrpl/server/NetworkOPs.h @@ -47,35 +47,37 @@ class SHAMap; // there's a functional network. // -/** Specifies the mode under which the server believes it's operating. - - This has implications about how the server processes transactions and - how it responds to requests (e.g. account balance request). - - @note Other code relies on the numerical values of these constants; do - not change them without verifying each use and ensuring that it is - not a breaking change. -*/ +/** + * Specifies the mode under which the server believes it's operating. + * + * This has implications about how the server processes transactions and + * how it responds to requests (e.g. account balance request). + * + * @note Other code relies on the numerical values of these constants; do + * not change them without verifying each use and ensuring that it is + * not a breaking change. + */ enum class OperatingMode { - DISCONNECTED = 0, //!< not ready to process requests - CONNECTED = 1, //!< convinced we are talking to the network - SYNCING = 2, //!< fallen slightly behind - TRACKING = 3, //!< convinced we agree with the network - FULL = 4 //!< we have the ledger and can even validate + DISCONNECTED = 0, ///< not ready to process requests + CONNECTED = 1, ///< convinced we are talking to the network + SYNCING = 2, ///< fallen slightly behind + TRACKING = 3, ///< convinced we agree with the network + FULL = 4 ///< we have the ledger and can even validate }; -/** Provides server functionality for clients. - - Clients include backend applications, local commands, and connected - clients. This class acts as a proxy, fulfilling the command with local - data if possible, or asking the network and returning the results if - needed. - - A backend application or local client can trust a local instance of - xrpld / NetworkOPs. However, client software connecting to non-local - instances of xrpld will need to be hardened to protect against hostile - or unreliable servers. -*/ +/** + * Provides server functionality for clients. + * + * Clients include backend applications, local commands, and connected + * clients. This class acts as a proxy, fulfilling the command with local + * data if possible, or asking the network and returning the results if + * needed. + * + * A backend application or local client can trust a local instance of + * xrpld / NetworkOPs. However, client software connecting to non-local + * instances of xrpld will need to be hardened to protect against hostile + * or unreliable servers. + */ class NetworkOPs : public InfoSub::Source { public: @@ -225,12 +227,13 @@ public: virtual json::Value getLedgerFetchInfo() = 0; - /** Accepts the current transaction tree, return the new ledger's sequence - - This API is only used via RPC with the server in STANDALONE mode and - performs a virtual consensus round, with all the transactions we are - proposing being accepted. - */ + /** + * Accepts the current transaction tree, return the new ledger's sequence + * + * This API is only used via RPC with the server in STANDALONE mode and + * performs a virtual consensus round, with all the transactions we are + * proposing being accepted. + */ virtual std::uint32_t acceptLedger(std::optional consensusDelay = std::nullopt) = 0; @@ -259,15 +262,16 @@ public: virtual void stateAccounting(json::Value& obj) = 0; - /** Total number of (book, subscriber) entries currently tracked. + /** + * Total number of (book, subscriber) entries currently tracked. * - * Counts every weak_ptr stored across every book in subBook_, NOT the - * number of distinct subscribers and NOT the number of distinct - * books: a single subscriber following N books contributes N entries. + * Counts every weak_ptr stored across every book in subBook_, NOT the + * number of distinct subscribers and NOT the number of distinct + * books: a single subscriber following N books contributes N entries. * - * @note Diagnostic accessor; intended for tests and operator visibility - * into per-book subscription state. The returned value is a - * snapshot under the subscription lock. + * @note Diagnostic accessor; intended for tests and operator visibility + * into per-book subscription state. The returned value is a + * snapshot under the subscription lock. */ virtual std::size_t getBookSubscribersCount() = 0; diff --git a/include/xrpl/server/Port.h b/include/xrpl/server/Port.h index c48a2546c1..b8bca6f95f 100644 --- a/include/xrpl/server/Port.h +++ b/include/xrpl/server/Port.h @@ -22,7 +22,9 @@ namespace xrpl { class Section; -/** Configuration information for a Server listening port. */ +/** + * Configuration information for a Server listening port. + */ struct Port { explicit Port() = default; diff --git a/include/xrpl/server/Server.h b/include/xrpl/server/Server.h index 956a414be8..f8d6005d0c 100644 --- a/include/xrpl/server/Server.h +++ b/include/xrpl/server/Server.h @@ -9,7 +9,9 @@ namespace xrpl { -/** Create the HTTP server using the specified handler. */ +/** + * Create the HTTP server using the specified handler. + */ template std::unique_ptr makeServer(Handler& handler, boost::asio::io_context& ioContext, beast::Journal journal) diff --git a/include/xrpl/server/Session.h b/include/xrpl/server/Session.h index 266570862a..be8d9a497c 100644 --- a/include/xrpl/server/Session.h +++ b/include/xrpl/server/Session.h @@ -15,11 +15,12 @@ namespace xrpl { -/** Persistent state information for a connection session. - These values are preserved between calls for efficiency. - Some fields are input parameters, some are output parameters, - and all only become defined during specific callbacks. -*/ +/** + * Persistent state information for a connection session. + * These values are preserved between calls for efficiency. + * Some fields are input parameters, some are output parameters, + * and all only become defined during specific callbacks. + */ class Session { public: @@ -29,29 +30,40 @@ public: operator=(Session const&) = delete; virtual ~Session() = default; - /** A user-definable pointer. - The initial value is always zero. - Changes to the value are persisted between calls. - */ + /** + * A user-definable pointer. + * The initial value is always zero. + * Changes to the value are persisted between calls. + */ void* tag = nullptr; - /** Returns the Journal to use for logging. */ + /** + * Returns the Journal to use for logging. + */ virtual beast::Journal journal() = 0; - /** Returns the Port settings for this connection. */ + /** + * Returns the Port settings for this connection. + */ virtual Port const& port() = 0; - /** Returns the remote address of the connection. */ + /** + * Returns the remote address of the connection. + */ virtual beast::IP::Endpoint remoteAddress() = 0; - /** Returns the current HTTP request. */ + /** + * Returns the current HTTP request. + */ virtual http_request_type& request() = 0; - /** Send a copy of data asynchronously. */ + /** + * Send a copy of data asynchronously. + */ /** @{ */ void write(std::string_view s) @@ -80,32 +92,37 @@ public: /** @} */ - /** Detach the session. - This holds the session open so that the response can be sent - asynchronously. Calls to io_context::run made by the server - will not return until all detached sessions are closed. - */ + /** + * Detach the session. + * This holds the session open so that the response can be sent + * asynchronously. Calls to io_context::run made by the server + * will not return until all detached sessions are closed. + */ virtual std::shared_ptr detach() = 0; - /** Indicate that the response is complete. - The handler should call this when it has completed writing - the response. If Keep-Alive is indicated on the connection, - this will trigger a read for the next request; else, the - connection will be closed when all remaining data has been sent. - */ + /** + * Indicate that the response is complete. + * The handler should call this when it has completed writing + * the response. If Keep-Alive is indicated on the connection, + * this will trigger a read for the next request; else, the + * connection will be closed when all remaining data has been sent. + */ virtual void complete() = 0; - /** Close the session. - This will be performed asynchronously. The session will be - closed gracefully after all pending writes have completed. - @param graceful `true` to wait until all data has finished sending. - */ + /** + * Close the session. + * This will be performed asynchronously. The session will be + * closed gracefully after all pending writes have completed. + * @param graceful `true` to wait until all data has finished sending. + */ virtual void close(bool graceful) = 0; - /** Convert the connection to WebSocket. */ + /** + * Convert the connection to WebSocket. + */ virtual std::shared_ptr websocketUpgrade() = 0; }; diff --git a/include/xrpl/server/SimpleWriter.h b/include/xrpl/server/SimpleWriter.h index 996403eafb..597e6e6d8f 100644 --- a/include/xrpl/server/SimpleWriter.h +++ b/include/xrpl/server/SimpleWriter.h @@ -14,7 +14,9 @@ namespace xrpl { -/// Deprecated: Writer that serializes a HTTP/1 message +/** + * Deprecated: Writer that serializes a HTTP/1 message + */ class SimpleWriter : public Writer { boost::beast::multi_buffer sb_; diff --git a/include/xrpl/server/WSSession.h b/include/xrpl/server/WSSession.h index 0087f9f50f..69f7decdb3 100644 --- a/include/xrpl/server/WSSession.h +++ b/include/xrpl/server/WSSession.h @@ -28,23 +28,24 @@ public: operator=(WSMsg const&) = delete; virtual ~WSMsg() = default; - /** Retrieve message data. - - Returns a tribool indicating whether or not - data is available, and a ConstBufferSequence - representing the data. - - tribool values: - maybe: Data is not ready yet - false: Data is available - true: Data is available, and - it is the last chunk of bytes. - - Derived classes that do not know when the data - ends (for example, when returning the output of a - paged database query) may return `true` and an - empty vector. - */ + /** + * Retrieve message data. + * + * Returns a tribool indicating whether or not + * data is available, and a ConstBufferSequence + * representing the data. + * + * tribool values: + * maybe: Data is not ready yet + * false: Data is available + * true: Data is available, and + * it is the last chunk of bytes. + * + * Derived classes that do not know when the data + * ends (for example, when returning the output of a + * paged database query) may return `true` and an + * empty vector. + */ virtual std::pair> prepare(std::size_t bytes, std::function resume) = 0; }; @@ -106,7 +107,9 @@ struct WSSession [[nodiscard]] virtual boost::asio::ip::tcp::endpoint const& remoteEndpoint() const = 0; - /** Send a WebSockets message. */ + /** + * Send a WebSockets message. + */ virtual void send(std::shared_ptr w) = 0; @@ -116,10 +119,11 @@ struct WSSession virtual void close(boost::beast::websocket::close_reason const& reason) = 0; - /** Indicate that the response is complete. - The handler should call this when it has completed writing - the response. - */ + /** + * Indicate that the response is complete. + * The handler should call this when it has completed writing + * the response. + */ virtual void complete() = 0; }; diff --git a/include/xrpl/server/Wallet.h b/include/xrpl/server/Wallet.h index eea44db200..ed8378989f 100644 --- a/include/xrpl/server/Wallet.h +++ b/include/xrpl/server/Wallet.h @@ -78,19 +78,22 @@ saveManifests( void addValidatorManifest(soci::session& session, std::string const& serialized); -/** Delete any saved public/private key associated with this node. */ +/** + * Delete any saved public/private key associated with this node. + */ void clearNodeIdentity(soci::session& session); -/** Returns a stable public and private key for this node. - - The node's public identity is defined by a secp256k1 keypair - that is (normally) randomly generated. This function will - return such a keypair, securely generating one if needed. - - @param session Session with the database. - - @return Pair of public and private secp256k1 keys. +/** + * Returns a stable public and private key for this node. + * + * The node's public identity is defined by a secp256k1 keypair + * that is (normally) randomly generated. This function will + * return such a keypair, securely generating one if needed. + * + * @param session Session with the database. + * + * @return Pair of public and private secp256k1 keys. */ std::pair getNodeIdentity(soci::session& session); diff --git a/include/xrpl/server/Writer.h b/include/xrpl/server/Writer.h index fe9d9519e1..a80563af43 100644 --- a/include/xrpl/server/Writer.h +++ b/include/xrpl/server/Writer.h @@ -13,26 +13,32 @@ class Writer public: virtual ~Writer() = default; - /** Returns `true` if there is no more data to pull. */ + /** + * Returns `true` if there is no more data to pull. + */ virtual bool complete() = 0; - /** Removes bytes from the input sequence. - - Can be called with 0. - */ + /** + * Removes bytes from the input sequence. + * + * Can be called with 0. + */ virtual void consume(std::size_t bytes) = 0; - /** Add data to the input sequence. - @param bytes A hint to the number of bytes desired. - @param resume A functor to later resume execution. - @return `true` if the writer is ready to provide more data. - */ + /** + * Add data to the input sequence. + * @param bytes A hint to the number of bytes desired. + * @param resume A functor to later resume execution. + * @return `true` if the writer is ready to provide more data. + */ virtual bool prepare(std::size_t bytes, std::function resume) = 0; - /** Returns a ConstBufferSequence representing the input sequence. */ + /** + * Returns a ConstBufferSequence representing the input sequence. + */ virtual std::vector data() = 0; }; diff --git a/include/xrpl/server/detail/BaseHTTPPeer.h b/include/xrpl/server/detail/BaseHTTPPeer.h index 7b35dbd4be..c7553c1da3 100644 --- a/include/xrpl/server/detail/BaseHTTPPeer.h +++ b/include/xrpl/server/detail/BaseHTTPPeer.h @@ -35,7 +35,9 @@ namespace xrpl { -/** Represents an active connection. */ +/** + * Represents an active connection. + */ template class BaseHTTPPeer : public IOList::Work, public Session { diff --git a/include/xrpl/server/detail/BaseWSPeer.h b/include/xrpl/server/detail/BaseWSPeer.h index d557140bd4..59a866ab8c 100644 --- a/include/xrpl/server/detail/BaseWSPeer.h +++ b/include/xrpl/server/detail/BaseWSPeer.h @@ -30,7 +30,9 @@ namespace xrpl { -/** Represents an active WebSocket connection. */ +/** + * Represents an active WebSocket connection. + */ template class BaseWSPeer : public BasePeer, public WSSession { @@ -48,9 +50,11 @@ private: boost::beast::multi_buffer rb_; boost::beast::multi_buffer wb_; std::list> wq_; - /// The socket has been closed, or will close after the next write - /// finishes. Do not do any more writes, and don't try to close - /// again. + /** + * The socket has been closed, or will close after the next write + * finishes. Do not do any more writes, and don't try to close + * again. + */ bool doClose_ = false; boost::beast::websocket::close_reason cr_; waitable_timer timer_; diff --git a/include/xrpl/server/detail/Door.h b/include/xrpl/server/detail/Door.h index d2d7a7baf4..285bde14ac 100644 --- a/include/xrpl/server/detail/Door.h +++ b/include/xrpl/server/detail/Door.h @@ -40,7 +40,9 @@ namespace xrpl { -/** A listening socket. */ +/** + * A listening socket. + */ template class Door : public IOList::Work, public std::enable_shared_from_this> { @@ -129,12 +131,13 @@ public: void run(); - /** Close the Door listening socket and connections. - The listening socket is closed, and all open connections - belonging to the Door are closed. - Thread Safety: - May be called concurrently - */ + /** + * Close the Door listening socket and connections. + * The listening socket is closed, and all open connections + * belonging to the Door are closed. + * Thread Safety: + * May be called concurrently + */ void close() override; diff --git a/include/xrpl/server/detail/ServerImpl.h b/include/xrpl/server/detail/ServerImpl.h index df2bba0284..c2e411cf9b 100644 --- a/include/xrpl/server/detail/ServerImpl.h +++ b/include/xrpl/server/detail/ServerImpl.h @@ -26,39 +26,45 @@ namespace xrpl { using Endpoints = std::unordered_map; -/** A multi-protocol server. - - This server maintains multiple configured listening ports, - with each listening port allows for multiple protocols including - HTTP, HTTP/S, WebSocket, Secure WebSocket, and the Peer protocol. -*/ +/** + * A multi-protocol server. + * + * This server maintains multiple configured listening ports, + * with each listening port allows for multiple protocols including + * HTTP, HTTP/S, WebSocket, Secure WebSocket, and the Peer protocol. + */ class Server { public: - /** Destroy the server. - The server is closed if it is not already closed. This call - blocks until the server has stopped. - */ + /** + * Destroy the server. + * The server is closed if it is not already closed. This call + * blocks until the server has stopped. + */ virtual ~Server() = default; - /** Returns the Journal associated with the server. */ + /** + * Returns the Journal associated with the server. + */ virtual beast::Journal journal() = 0; - /** Set the listening port settings. - This may only be called once. - */ + /** + * Set the listening port settings. + * This may only be called once. + */ virtual Endpoints ports(std::vector const& v) = 0; - /** Close the server. - The close is performed asynchronously. The handler will be notified - when the server has stopped. The server is considered stopped when - there are no pending I/O completion handlers and all connections - have closed. - Thread safety: - Safe to call concurrently from any thread. - */ + /** + * Close the server. + * The close is performed asynchronously. The handler will be notified + * when the server has stopped. The server is considered stopped when + * there are no pending I/O completion handlers and all connections + * have closed. + * Thread safety: + * Safe to call concurrently from any thread. + */ virtual void close() = 0; }; diff --git a/include/xrpl/server/detail/io_list.h b/include/xrpl/server/detail/io_list.h index 0153bd3457..7ffa85b898 100644 --- a/include/xrpl/server/detail/io_list.h +++ b/include/xrpl/server/detail/io_list.h @@ -12,7 +12,9 @@ namespace xrpl { -/** Manages a set of objects performing asynchronous I/O. */ +/** + * Manages a set of objects performing asynchronous I/O. + */ class IOList final { public: @@ -31,12 +33,13 @@ public: destroy(); } - /** Return the IOList associated with the work. - - Requirements: - The call to IOList::emplace to - create the work has already returned. - */ + /** + * Return the IOList associated with the work. + * + * Requirements: + * The call to IOList::emplace to + * create the work has already returned. + */ IOList& ios() { @@ -62,71 +65,74 @@ private: public: IOList() = default; - /** Destroy the list. - - Effects: - Closes the IOList if it was not previously - closed. No finisher is invoked in this case. - - Blocks until all work is destroyed. - */ + /** + * Destroy the list. + * + * Effects: + * Closes the IOList if it was not previously + * closed. No finisher is invoked in this case. + * + * Blocks until all work is destroyed. + */ ~IOList() { destroy(); } - /** Return `true` if the list is closed. - - Thread Safety: - Undefined result if called concurrently - with close(). - */ + /** + * Return `true` if the list is closed. + * + * Thread Safety: + * Undefined result if called concurrently + * with close(). + */ [[nodiscard]] bool closed() const { return closed_; } - /** Create associated work if not closed. - - Requirements: - `std::is_base_of_v == true` - - Thread Safety: - May be called concurrently. - - Effects: - Atomically creates, inserts, and returns new - work T, or returns nullptr if the io_list is - closed, - - If the call succeeds and returns a new object, - it is guaranteed that a subsequent call to close - will invoke Work::close on the object. - - */ + /** + * Create associated work if not closed. + * + * Requirements: + * `std::is_base_of_v == true` + * + * Thread Safety: + * May be called concurrently. + * + * Effects: + * Atomically creates, inserts, and returns new + * work T, or returns nullptr if the io_list is + * closed, + * + * If the call succeeds and returns a new object, + * it is guaranteed that a subsequent call to close + * will invoke Work::close on the object. + */ template std::shared_ptr emplace(Args&&... args); - /** Cancel active I/O. - - Thread Safety: - May not be called concurrently. - - Effects: - Associated work is closed. - - Finisher if provided, will be called when - all associated work is destroyed. The finisher - may be called from a foreign thread, or within - the call to this function. - - Only the first call to close will set the - finisher. - - No effect after the first call. - */ + /** + * Cancel active I/O. + * + * Thread Safety: + * May not be called concurrently. + * + * Effects: + * Associated work is closed. + * + * Finisher if provided, will be called when + * all associated work is destroyed. The finisher + * may be called from a foreign thread, or within + * the call to this function. + * + * Only the first call to close will set the + * finisher. + * + * No effect after the first call. + */ template void close(Finisher&& f); @@ -137,20 +143,21 @@ public: close([] {}); } - /** Block until the io_list stops. - - Effects: - The caller is blocked until the io_list is - closed and all associated work is destroyed. - - Thread safety: - May be called concurrently. - - Preconditions: - No call to io_context::run on any io_context - used by work objects associated with this io_list - exists in the caller's call stack. - */ + /** + * Block until the io_list stops. + * + * Effects: + * The caller is blocked until the io_list is + * closed and all associated work is destroyed. + * + * Thread safety: + * May be called concurrently. + * + * Preconditions: + * No call to io_context::run on any io_context + * used by work objects associated with this io_list + * exists in the caller's call stack. + */ template void join(); diff --git a/include/xrpl/shamap/Family.h b/include/xrpl/shamap/Family.h index c5bf953bfd..7624b3e600 100644 --- a/include/xrpl/shamap/Family.h +++ b/include/xrpl/shamap/Family.h @@ -35,18 +35,23 @@ public: virtual beast::Journal const& journal() = 0; - /** Return a pointer to the Family Full Below Cache */ + /** + * Return a pointer to the Family Full Below Cache + */ virtual std::shared_ptr getFullBelowCache() = 0; - /** Return a pointer to the Family Tree Node Cache */ + /** + * Return a pointer to the Family Tree Node Cache + */ virtual std::shared_ptr getTreeNodeCache() = 0; virtual void sweep() = 0; - /** Acquire ledger that has a missing node by ledger sequence + /** + * Acquire ledger that has a missing node by ledger sequence * * @param refNum Sequence of ledger to acquire. * @param nodeHash Hash of missing node to report in throw. @@ -54,7 +59,8 @@ public: virtual void missingNodeAcquireBySeq(std::uint32_t refNum, uint256 const& nodeHash) = 0; - /** Acquire ledger that has a missing node by ledger hash + /** + * Acquire ledger that has a missing node by ledger hash * * @param refHash Hash of ledger to acquire. * @param refNum Ledger sequence with missing node. diff --git a/include/xrpl/shamap/FullBelowCache.h b/include/xrpl/shamap/FullBelowCache.h index b6d1142eb4..1bb67c7453 100644 --- a/include/xrpl/shamap/FullBelowCache.h +++ b/include/xrpl/shamap/FullBelowCache.h @@ -17,9 +17,10 @@ namespace xrpl { namespace detail { -/** Remembers which tree keys have all descendants resident. - This optimizes the process of acquiring a complete tree. -*/ +/** + * Remembers which tree keys have all descendants resident. + * This optimizes the process of acquiring a complete tree. + */ class BasicFullBelowCache { private: @@ -31,13 +32,14 @@ public: using key_type = uint256; using clock_type = CacheType::clock_type; - /** Construct the cache. - - @param name A label for diagnostics and stats reporting. - @param collector The collector to use for reporting stats. - @param targetSize The cache target size. - @param targetExpirationSeconds The expiration time for items. - */ + /** + * Construct the cache. + * + * @param name A label for diagnostics and stats reporting. + * @param collector The collector to use for reporting stats. + * @param targetSize The cache target size. + * @param targetExpirationSeconds The expiration time for items. + */ BasicFullBelowCache( std::string const& name, clock_type& clock, @@ -49,59 +51,67 @@ public: { } - /** Return the clock associated with the cache. */ + /** + * Return the clock associated with the cache. + */ clock_type& clock() { return cache_.clock(); } - /** Return the number of elements in the cache. - Thread safety: - Safe to call from any thread. - */ + /** + * Return the number of elements in the cache. + * Thread safety: + * Safe to call from any thread. + */ std::size_t size() const { return cache_.size(); } - /** Remove expired cache items. - Thread safety: - Safe to call from any thread. - */ + /** + * Remove expired cache items. + * Thread safety: + * Safe to call from any thread. + */ void sweep() { cache_.sweep(); } - /** Refresh the last access time of an item, if it exists. - Thread safety: - Safe to call from any thread. - @param key The key to refresh. - @return `true` If the key exists. - */ + /** + * Refresh the last access time of an item, if it exists. + * Thread safety: + * Safe to call from any thread. + * @param key The key to refresh. + * @return `true` If the key exists. + */ bool touchIfExists(key_type const& key) { return cache_.touchIfExists(key); } - /** Insert a key into the cache. - If the key already exists, the last access time will still - be refreshed. - Thread safety: - Safe to call from any thread. - @param key The key to insert. - */ + /** + * Insert a key into the cache. + * If the key already exists, the last access time will still + * be refreshed. + * Thread safety: + * Safe to call from any thread. + * @param key The key to insert. + */ void insert(key_type const& key) { cache_.insert(key); } - /** generation determines whether cached entry is valid */ + /** + * generation determines whether cached entry is valid + */ std::uint32_t getGeneration() const { diff --git a/include/xrpl/shamap/SHAMap.h b/include/xrpl/shamap/SHAMap.h index d49e323b3f..a1194ccfd3 100644 --- a/include/xrpl/shamap/SHAMap.h +++ b/include/xrpl/shamap/SHAMap.h @@ -38,55 +38,62 @@ namespace xrpl { class SHAMapNodeID; class SHAMapSyncFilter; -/** Describes the current state of a given SHAMap */ +/** + * Describes the current state of a given SHAMap + */ enum class SHAMapState { - /** The map is in flux and objects can be added and removed. - - Example: map underlying the open ledger. + /** + * The map is in flux and objects can be added and removed. + * + * Example: map underlying the open ledger. */ Modifying = 0, - /** The map is set in stone and cannot be changed. - - Example: a map underlying a given closed ledger. + /** + * The map is set in stone and cannot be changed. + * + * Example: a map underlying a given closed ledger. */ Immutable = 1, - /** The map's hash is fixed but valid nodes may be missing and can be added. - - Example: a map that's syncing a given peer's closing ledger. + /** + * The map's hash is fixed but valid nodes may be missing and can be added. + * + * Example: a map that's syncing a given peer's closing ledger. */ Synching = 2, - /** The map is known to not be valid. - - Example: usually synching a corrupt ledger. + /** + * The map is known to not be valid. + * + * Example: usually synching a corrupt ledger. */ Invalid = 3, }; -/** A SHAMap is both a radix tree with a fan-out of 16 and a Merkle tree. - - A radix tree is a tree with two properties: - - 1. The key for a node is represented by the node's position in the tree - (the "prefix property"). - 2. A node with only one child is merged with that child - (the "merge property") - - These properties result in a significantly smaller memory footprint for - a radix tree. - - A fan-out of 16 means that each node in the tree has at most 16 - children. See https://en.wikipedia.org/wiki/Radix_tree - - A Merkle tree is a tree where each non-leaf node is labelled with the hash - of the combined labels of its children nodes. - - A key property of a Merkle tree is that testing for node inclusion is - O(log(N)) where N is the number of nodes in the tree. - - See https://en.wikipedia.org/wiki/Merkle_tree +/** + * A SHAMap is both a radix tree with a fan-out of 16 and a Merkle tree. + * + * A radix tree is a tree with two properties: + * + * 1. The key for a node is represented by the node's position in the tree + * (the "prefix property"). + * 2. A node with only one child is merged with that child + * (the "merge property") + * + * These properties result in a significantly smaller memory footprint for + * a radix tree. + * + * A fan-out of 16 means that each node in the tree has at most 16 + * children. See https://en.wikipedia.org/wiki/Radix_tree + * + * A Merkle tree is a tree where each non-leaf node is labelled with the hash + * of the combined labels of its children nodes. + * + * A key property of a Merkle tree is that testing for node inclusion is + * O(log(N)) where N is the number of nodes in the tree. + * + * See https://en.wikipedia.org/wiki/Merkle_tree */ class SHAMap { @@ -94,10 +101,14 @@ private: Family& f_; beast::Journal journal_; - /** ID to distinguish this map for all others we're sharing nodes with. */ + /** + * ID to distinguish this map for all others we're sharing nodes with. + */ std::uint32_t cowid_ = 1; - /** The sequence of the ledger that this map references, if any. */ + /** + * The sequence of the ledger that this map references, if any. + */ std::uint32_t ledgerSeq_ = 0; SHAMapTreeNodePtr root_; @@ -107,11 +118,15 @@ private: mutable bool full_ = false; // Map is believed complete in database public: - /** Number of children each non-leaf node has (the 'radix tree' part of the - * map) */ + /** + * Number of children each non-leaf node has (the 'radix tree' part of the + * map) + */ static constexpr unsigned int kBranchFactor = SHAMapInnerNode::kBranchFactor; - /** The depth of the hash map: data is only present in the leaves */ + /** + * The depth of the hash map: data is only present in the leaves + */ static constexpr unsigned int kLeafDepth = 64; using DeltaItem = @@ -147,10 +162,11 @@ public: //-------------------------------------------------------------------------- - /** Iterator to a SHAMap's leaves - This is always a const iterator. - Meets the requirements of ForwardRange. - */ + /** + * Iterator to a SHAMap's leaves + * This is always a const iterator. + * Meets the requirements of ForwardRange. + */ class ConstIterator; ConstIterator @@ -180,7 +196,9 @@ public: // normal hash access functions - /** Does the tree have an item with the given ID? */ + /** + * Does the tree have an item with the given ID? + */ bool hasItem(uint256 const& id) const; @@ -208,60 +226,66 @@ public: peekItem(uint256 const& id, SHAMapHash& hash) const; // traverse functions - /** Find the first item after the given item. - - @param id the identifier of the item. - - @note The item does not need to exist. + /** + * Find the first item after the given item. + * + * @param id the identifier of the item. + * + * @note The item does not need to exist. */ ConstIterator upperBound(uint256 const& id) const; - /** Find the object with the greatest object id smaller than the input id. - - @param id the identifier of the item. - - @note The item does not need to exist. + /** + * Find the object with the greatest object id smaller than the input id. + * + * @param id the identifier of the item. + * + * @note The item does not need to exist. */ ConstIterator lowerBound(uint256 const& id) const; - /** Visit every node in this SHAMap - - @param function called with every node visited. - If function returns false, visitNodes exits. - */ + /** + * Visit every node in this SHAMap + * + * @param function called with every node visited. + * If function returns false, visitNodes exits. + */ void visitNodes(std::function const& function) const; - /** Visit every node in this SHAMap that - is not present in the specified SHAMap - - @param function called with every node visited. - If function returns false, visitDifferences exits. - */ + /** + * Visit every node in this SHAMap that + * is not present in the specified SHAMap + * + * @param function called with every node visited. + * If function returns false, visitDifferences exits. + */ void visitDifferences(SHAMap const* have, std::function const&) const; - /** Visit every leaf node in this SHAMap - - @param function called with every non inner node visited. - */ + /** + * Visit every leaf node in this SHAMap + * + * @param function called with every non inner node visited. + */ void visitLeaves(std::function const&)> const&) const; // comparison/sync functions - /** Check for nodes in the SHAMap not available - - Traverse the SHAMap efficiently, maximizing I/O - concurrency, to discover nodes referenced in the - SHAMap but not available locally. - - @param maxNodes The maximum number of found nodes to return - @param filter The filter to use when retrieving nodes - @param return The nodes known to be missing - */ + /** + * Check for nodes in the SHAMap not available + * + * Traverse the SHAMap efficiently, maximizing I/O + * concurrency, to discover nodes referenced in the + * SHAMap but not available locally. + * + * @param maxNodes The maximum number of found nodes to return + * @param filter The filter to use when retrieving nodes + * @param return The nodes known to be missing + */ std::vector> getMissingNodes(int maxNodes, SHAMapSyncFilter const* filter); @@ -291,7 +315,9 @@ public: static bool verifyProofPath(uint256 const& rootHash, uint256 const& key, std::vector const& path); - /** Serializes the root in a format appropriate for sending over the wire */ + /** + * Serializes the root in a format appropriate for sending over the wire + */ void serializeRoot(Serializer& s) const; @@ -317,11 +343,15 @@ public: bool compare(SHAMap const& otherMap, Delta& differences, int maxCount) const; - /** Convert any modified nodes to shared. */ + /** + * Convert any modified nodes to shared. + */ int unshare(); - /** Flush modified nodes to the nodestore and convert them to shared. */ + /** + * Flush modified nodes to the nodestore and convert them to shared. + */ int flushDirty(NodeObjectType t); @@ -364,30 +394,42 @@ private: SHAMapTreeNodePtr checkFilter(SHAMapHash const& hash, SHAMapSyncFilter const* filter) const; - /** Update hashes up to the root */ + /** + * Update hashes up to the root + */ void dirtyUp(SharedPtrNodeStack& stack, uint256 const& target, SHAMapTreeNodePtr terminal); - /** Walk towards the specified id, returning the node. Caller must check - if the return is nullptr, and if not, if the node->peekItem()->key() == - id */ + /** + * Walk towards the specified id, returning the node. Caller must check + * if the return is nullptr, and if not, if the node->peekItem()->key() == + * id + */ SHAMapLeafNode* walkTowardsKey(uint256 const& id, SharedPtrNodeStack* stack = nullptr) const; - /** Return nullptr if key not found */ + /** + * Return nullptr if key not found + */ SHAMapLeafNode* findKey(uint256 const& id) const; - /** Unshare the node, allowing it to be modified */ + /** + * Unshare the node, allowing it to be modified + */ template intr_ptr::SharedPtr unshareNode(intr_ptr::SharedPtr, SHAMapNodeID const& nodeID); - /** prepare a node to be modified before flushing */ + /** + * prepare a node to be modified before flushing + */ template intr_ptr::SharedPtr preFlushNode(intr_ptr::SharedPtr node) const; - /** write and canonicalize modified node */ + /** + * write and canonicalize modified node + */ SHAMapTreeNodePtr writeNode(NodeObjectType t, SHAMapTreeNodePtr node) const; @@ -442,7 +484,9 @@ private: SHAMapTreeNodePtr descendNoStore(SHAMapInnerNode&, int branch) const; - /** If there is only one leaf below this node, get its contents */ + /** + * If there is only one leaf below this node, get its contents + */ boost::intrusive_ptr const& onlyBelow(SHAMapTreeNode*) const; diff --git a/include/xrpl/shamap/SHAMapAccountStateLeafNode.h b/include/xrpl/shamap/SHAMapAccountStateLeafNode.h index ee81107f61..96c853eb28 100644 --- a/include/xrpl/shamap/SHAMapAccountStateLeafNode.h +++ b/include/xrpl/shamap/SHAMapAccountStateLeafNode.h @@ -15,7 +15,9 @@ namespace xrpl { -/** A leaf node for a state object. */ +/** + * A leaf node for a state object. + */ class SHAMapAccountStateLeafNode final : public SHAMapLeafNode, public CountedObject { diff --git a/include/xrpl/shamap/SHAMapInnerNode.h b/include/xrpl/shamap/SHAMapInnerNode.h index 0fb4e24077..44d3bd6279 100644 --- a/include/xrpl/shamap/SHAMapInnerNode.h +++ b/include/xrpl/shamap/SHAMapInnerNode.h @@ -18,63 +18,72 @@ namespace xrpl { class SHAMapInnerNode final : public SHAMapTreeNode, public CountedObject { public: - /** Each inner node has 16 children (the 'radix tree' part of the map) */ + /** + * Each inner node has 16 children (the 'radix tree' part of the map) + */ static constexpr unsigned int kBranchFactor = 16; private: - /** Opaque type that contains the `hashes` array (array of type - `SHAMapHash`) and the `children` array (array of type - `intr_ptr::SharedPtr`). + /** + * Opaque type that contains the `hashes` array (array of type + * `SHAMapHash`) and the `children` array (array of type + * `intr_ptr::SharedPtr`). */ TaggedPointer hashesAndChildren_; std::uint32_t fullBelowGen_ = 0; std::uint16_t isBranch_ = 0; - /** A bitlock for the children of this node, with one bit per child */ + /** + * A bitlock for the children of this node, with one bit per child + */ mutable std::atomic lock_ = 0; - /** Convert arrays stored in `hashesAndChildren_` so they can store the - requested number of children. - - @param toAllocate allocate space for at least this number of children - (must be <= branchFactor) - - @note the arrays may allocate more than the requested value in - `toAllocate`. This is due to the implementation of TagPointer, which - only supports allocating arrays of 4 different sizes. + /** + * Convert arrays stored in `hashesAndChildren_` so they can store the + * requested number of children. + * + * @param toAllocate allocate space for at least this number of children + * (must be <= branchFactor) + * + * @note the arrays may allocate more than the requested value in + * `toAllocate`. This is due to the implementation of TagPointer, which + * only supports allocating arrays of 4 different sizes. */ void resizeChildArrays(std::uint8_t toAllocate); - /** Get the child's index inside the `hashes` or `children` array (stored in - `hashesAndChildren_`. - - These arrays may or may not be sparse). The optional will be empty is an - empty branch is requested and the arrays are sparse. - - @param i index of the requested child + /** + * Get the child's index inside the `hashes` or `children` array (stored in + * `hashesAndChildren_`. + * + * These arrays may or may not be sparse). The optional will be empty is an + * empty branch is requested and the arrays are sparse. + * + * @param i index of the requested child */ std::optional getChildIndex(int i) const; - /** Call the `f` callback for all 16 (branchFactor) branches - even if - the branch is empty. - - @param f a one parameter callback function. The parameter is the - child's hash. - */ + /** + * Call the `f` callback for all 16 (branchFactor) branches - even if + * the branch is empty. + * + * @param f a one parameter callback function. The parameter is the + * child's hash. + */ template void iterChildren(F&& f) const; - /** Call the `f` callback for all non-empty branches. - - @param f a two parameter callback function. The first parameter is - the branch number, the second parameter is the index into the array. - For dense formats these are the same, for sparse they may be - different. - */ + /** + * Call the `f` callback for all non-empty branches. + * + * @param f a two parameter callback function. The first parameter is + * the branch number, the second parameter is the index into the array. + * For dense formats these are the same, for sparse they may be + * different. + */ template void iterNonEmptyChildIndexes(F&& f) const; @@ -149,7 +158,9 @@ public: void updateHash() override; - /** Recalculate the hash of all children and this node. */ + /** + * Recalculate the hash of all children and this node. + */ void updateHashDeep(); diff --git a/include/xrpl/shamap/SHAMapLeafNode.h b/include/xrpl/shamap/SHAMapLeafNode.h index 112c243131..26cfde9fe8 100644 --- a/include/xrpl/shamap/SHAMapLeafNode.h +++ b/include/xrpl/shamap/SHAMapLeafNode.h @@ -46,11 +46,12 @@ public: boost::intrusive_ptr const& peekItem() const; - /** Set the item that this node points to and update the node's hash. - - @param i the new item - @return false if the change was, effectively, a noop (that is, if the - hash was unchanged); true otherwise. + /** + * Set the item that this node points to and update the node's hash. + * + * @param i the new item + * @return false if the change was, effectively, a noop (that is, if the + * hash was unchanged); true otherwise. */ bool setItem(boost::intrusive_ptr i); diff --git a/include/xrpl/shamap/SHAMapNodeID.h b/include/xrpl/shamap/SHAMapNodeID.h index b812c10ca9..6094892091 100644 --- a/include/xrpl/shamap/SHAMapNodeID.h +++ b/include/xrpl/shamap/SHAMapNodeID.h @@ -12,7 +12,9 @@ namespace xrpl { -/** Identifies a node inside a SHAMap */ +/** + * Identifies a node inside a SHAMap + */ class SHAMapNodeID : public CountedObject { private: @@ -64,7 +66,9 @@ public: createID(int depth, uint256 const& key); // FIXME-C++20: use spaceship and operator synthesis - /** Comparison operators */ + /** + * Comparison operators + */ bool operator<(SHAMapNodeID const& n) const { @@ -117,7 +121,8 @@ operator<<(std::ostream& out, SHAMapNodeID const& node) return out << to_string(node); } -/** Return an object representing a serialized SHAMap Node ID +/** + * Return an object representing a serialized SHAMap Node ID * * @param s A string of bytes * @param data a non-null pointer to a buffer of @param size bytes. @@ -136,7 +141,9 @@ deserializeSHAMapNodeID(std::string_view s) } /** @} */ -/** Returns the branch that would contain the given hash */ +/** + * Returns the branch that would contain the given hash + */ [[nodiscard]] unsigned int selectBranch(SHAMapNodeID const& id, uint256 const& hash); diff --git a/include/xrpl/shamap/SHAMapSyncFilter.h b/include/xrpl/shamap/SHAMapSyncFilter.h index b6ce175915..60f340db3c 100644 --- a/include/xrpl/shamap/SHAMapSyncFilter.h +++ b/include/xrpl/shamap/SHAMapSyncFilter.h @@ -7,7 +7,9 @@ #include #include -/** Callback for filtering SHAMap during sync. */ +/** + * Callback for filtering SHAMap during sync. + */ namespace xrpl { class SHAMapSyncFilter diff --git a/include/xrpl/shamap/SHAMapTreeNode.h b/include/xrpl/shamap/SHAMapTreeNode.h index 1eebbaa17f..c8b242238a 100644 --- a/include/xrpl/shamap/SHAMapTreeNode.h +++ b/include/xrpl/shamap/SHAMapTreeNode.h @@ -39,18 +39,20 @@ class SHAMapTreeNode : public IntrusiveRefCounts protected: SHAMapHash hash_; - /** Determines the owning SHAMap, if any. Used for copy-on-write semantics. - - If this value is 0, the node is not dirty and does not need to be - flushed. It is eligible for sharing and may be included multiple - SHAMap instances. + /** + * Determines the owning SHAMap, if any. Used for copy-on-write semantics. + * + * If this value is 0, the node is not dirty and does not need to be + * flushed. It is eligible for sharing and may be included multiple + * SHAMap instances. */ std::uint32_t cowid_; - /** Construct a node - - @param cowid The identifier of a SHAMap. For more, see #cowid_ - @param hash The hash associated with this node, if any. + /** + * Construct a node + * + * @param cowid The identifier of a SHAMap. For more, see #cowid_ + * @param hash The hash associated with this node, if any. */ /** @{ */ explicit SHAMapTreeNode(std::uint32_t cowid) noexcept : cowid_(cowid) @@ -74,28 +76,30 @@ public: virtual void partialDestructor() {}; - /** \defgroup SHAMap Copy-on-Write Support - - By nature, a node may appear in multiple SHAMap instances. Rather - than actually duplicating these nodes, SHAMap opts to be memory - efficient and uses copy-on-write semantics for nodes. - - Only nodes that are not modified and don't need to be flushed back - can be shared. Once a node needs to be changed, it must first be - copied and the copy must marked as not shareable. - - Note that just because a node may not be *owned* by a given SHAMap - instance does not mean that the node is NOT a part of any SHAMap. It - only means that the node is not owned exclusively by any one SHAMap. - - For more on copy-on-write, check out: - https://en.wikipedia.org/wiki/Copy-on-write + /** + * @defgroup SHAMap Copy-on-Write Support + * + * By nature, a node may appear in multiple SHAMap instances. Rather + * than actually duplicating these nodes, SHAMap opts to be memory + * efficient and uses copy-on-write semantics for nodes. + * + * Only nodes that are not modified and don't need to be flushed back + * can be shared. Once a node needs to be changed, it must first be + * copied and the copy must marked as not shareable. + * + * Note that just because a node may not be *owned* by a given SHAMap + * instance does not mean that the node is NOT a part of any SHAMap. It + * only means that the node is not owned exclusively by any one SHAMap. + * + * For more on copy-on-write, check out: + * https://en.wikipedia.org/wiki/Copy-on-write */ /** @{ */ - /** Returns the SHAMap that owns this node. - - @return the ID of the SHAMap that owns this node, or 0 if the - node is not owned by any SHAMap and is a candidate for sharing. + /** + * Returns the SHAMap that owns this node. + * + * @return the ID of the SHAMap that owns this node, or 0 if the + * node is not owned by any SHAMap and is a candidate for sharing. */ std::uint32_t cowid() const @@ -103,10 +107,11 @@ public: return cowid_; } - /** If this node is shared with another map, mark it as no longer shared. - - Only nodes that are not modified and do not need to be flushed back - should be marked as unshared. + /** + * If this node is shared with another map, mark it as no longer shared. + * + * Only nodes that are not modified and do not need to be flushed back + * should be marked as unshared. */ void unshare() @@ -114,39 +119,55 @@ public: cowid_ = 0; } - /** Make a copy of this node, setting the owner. */ + /** + * Make a copy of this node, setting the owner. + */ virtual SHAMapTreeNodePtr clone(std::uint32_t cowid) const = 0; /** @} */ - /** Recalculate the hash of this node. */ + /** + * Recalculate the hash of this node. + */ virtual void updateHash() = 0; - /** Return the hash of this node. */ + /** + * Return the hash of this node. + */ SHAMapHash const& getHash() const { return hash_; } - /** Determines the type of node. */ + /** + * Determines the type of node. + */ virtual SHAMapNodeType getType() const = 0; - /** Determines if this is a leaf node. */ + /** + * Determines if this is a leaf node. + */ virtual bool isLeaf() const = 0; - /** Determines if this is an inner node. */ + /** + * Determines if this is an inner node. + */ virtual bool isInner() const = 0; - /** Serialize the node in a format appropriate for sending over the wire */ + /** + * Serialize the node in a format appropriate for sending over the wire + */ virtual void serializeForWire(Serializer&) const = 0; - /** Serialize the node in a format appropriate for hashing */ + /** + * Serialize the node in a format appropriate for hashing + */ virtual void serializeWithPrefix(Serializer&) const = 0; diff --git a/include/xrpl/shamap/SHAMapTxLeafNode.h b/include/xrpl/shamap/SHAMapTxLeafNode.h index 86186434f8..9b9ac2f996 100644 --- a/include/xrpl/shamap/SHAMapTxLeafNode.h +++ b/include/xrpl/shamap/SHAMapTxLeafNode.h @@ -15,7 +15,9 @@ namespace xrpl { -/** A leaf node for a transaction. No metadata is included. */ +/** + * A leaf node for a transaction. No metadata is included. + */ class SHAMapTxLeafNode final : public SHAMapLeafNode, public CountedObject { public: diff --git a/include/xrpl/shamap/SHAMapTxPlusMetaLeafNode.h b/include/xrpl/shamap/SHAMapTxPlusMetaLeafNode.h index 9e4573d45b..6f8a7ebfda 100644 --- a/include/xrpl/shamap/SHAMapTxPlusMetaLeafNode.h +++ b/include/xrpl/shamap/SHAMapTxPlusMetaLeafNode.h @@ -15,7 +15,9 @@ namespace xrpl { -/** A leaf node for a transaction and its associated metadata. */ +/** + * A leaf node for a transaction and its associated metadata. + */ class SHAMapTxPlusMetaLeafNode final : public SHAMapLeafNode, public CountedObject { diff --git a/include/xrpl/shamap/detail/TaggedPointer.h b/include/xrpl/shamap/detail/TaggedPointer.h index 79f3464d5b..509e6cc58d 100644 --- a/include/xrpl/shamap/detail/TaggedPointer.h +++ b/include/xrpl/shamap/detail/TaggedPointer.h @@ -12,111 +12,122 @@ namespace xrpl { -/** TaggedPointer is a combination of a pointer and a mask stored in the - lowest two bits. - - Since pointers do not have arbitrary alignment, the lowest bits in the - pointer are guaranteed to be zero. TaggedPointer stores information in these - low bits. When dereferencing the pointer, these low "tag" bits are set to - zero. When accessing the tag bits, the high "pointer" bits are set to zero. - - The "pointer" part points to the equivalent to an array of - `SHAMapHash` followed immediately by an array of - `shared_ptr`. The sizes of these arrays are - determined by the tag. The tag is an index into an array (`boundaries`, - defined in the cpp file) that specifies the size. Both arrays are the - same size. Note that the sizes may be smaller than the full 16 elements - needed to explicitly store all the children. In this case, the arrays - only store the non-empty children. The non-empty children are stored in - index order. For example, if only children `2` and `14` are non-empty, a - two-element array would store child `2` in array index 0 and child `14` - in array index 1. There are functions to convert between a child's tree - index and the child's index in a sparse array. - - The motivation for this class is saving RAM. A large percentage of inner - nodes only store a small number of children. Memory can be saved by - storing the inner node's children in sparse arrays. Measurements show - that on average a typical SHAMap's inner nodes can be stored using only - 25% of the original space. -*/ +/** + * TaggedPointer is a combination of a pointer and a mask stored in the + * lowest two bits. + * + * Since pointers do not have arbitrary alignment, the lowest bits in the + * pointer are guaranteed to be zero. TaggedPointer stores information in these + * low bits. When dereferencing the pointer, these low "tag" bits are set to + * zero. When accessing the tag bits, the high "pointer" bits are set to zero. + * + * The "pointer" part points to the equivalent to an array of + * `SHAMapHash` followed immediately by an array of + * `shared_ptr`. The sizes of these arrays are + * determined by the tag. The tag is an index into an array (`boundaries`, + * defined in the cpp file) that specifies the size. Both arrays are the + * same size. Note that the sizes may be smaller than the full 16 elements + * needed to explicitly store all the children. In this case, the arrays + * only store the non-empty children. The non-empty children are stored in + * index order. For example, if only children `2` and `14` are non-empty, a + * two-element array would store child `2` in array index 0 and child `14` + * in array index 1. There are functions to convert between a child's tree + * index and the child's index in a sparse array. + * + * The motivation for this class is saving RAM. A large percentage of inner + * nodes only store a small number of children. Memory can be saved by + * storing the inner node's children in sparse arrays. Measurements show + * that on average a typical SHAMap's inner nodes can be stored using only + * 25% of the original space. + */ class TaggedPointer { private: static_assert( alignof(SHAMapHash) >= 4, "Bad alignment: Tag pointer requires low two bits to be zero."); - /** Upper bits are the pointer, lowest two bits are the tag - A moved-from object will have a tp_ of zero. - */ + /** + * Upper bits are the pointer, lowest two bits are the tag + * A moved-from object will have a tp_ of zero. + */ std::uintptr_t tp_ = 0; - /** bit-and with this mask to get the tag bits (lowest two bits) */ + /** + * bit-and with this mask to get the tag bits (lowest two bits) + */ static constexpr std::uintptr_t kTagMask = 3; - /** bit-and with this mask to get the pointer bits (mask out the tag) */ + /** + * bit-and with this mask to get the pointer bits (mask out the tag) + */ static constexpr std::uintptr_t kPtrMask = ~kTagMask; - /** Deallocate memory and run destructors */ + /** + * Deallocate memory and run destructors + */ void destroyHashesAndChildren(); struct RawAllocateTag { }; - /** This constructor allocates space for the hashes and children, but - does not run constructors. - - @param RawAllocateTag used to select overload only - - @param numChildren allocate space for at least this number of children - (must be <= branchFactor) - - @note Since the hashes/children destructors are always run in the - TaggedPointer destructor, this means those constructors _must_ be run - after this constructor is run. This constructor is private and only used - in places where the hashes/children constructor are subsequently run. - */ + /** + * This constructor allocates space for the hashes and children, but + * does not run constructors. + * + * @param RawAllocateTag used to select overload only + * + * @param numChildren allocate space for at least this number of children + * (must be <= branchFactor) + * + * @note Since the hashes/children destructors are always run in the + * TaggedPointer destructor, this means those constructors _must_ be run + * after this constructor is run. This constructor is private and only used + * in places where the hashes/children constructor are subsequently run. + */ explicit TaggedPointer(RawAllocateTag, std::uint8_t numChildren); public: TaggedPointer() = delete; explicit TaggedPointer(std::uint8_t numChildren); - /** Constructor is used change the number of allocated children. - - Existing children from `other` are copied (toAllocate must be >= the - number of children). The motivation for making this a constructor is it - saves unneeded copying and zeroing out of hashes if this were - implemented directly in the SHAMapInnerNode class. - - @param other children and hashes are moved from this param - - @param isBranch bitset of non-empty children in `other` - - @param toAllocate allocate space for at least this number of children - (must be <= branchFactor) - */ + /** + * Constructor is used change the number of allocated children. + * + * Existing children from `other` are copied (toAllocate must be >= the + * number of children). The motivation for making this a constructor is it + * saves unneeded copying and zeroing out of hashes if this were + * implemented directly in the SHAMapInnerNode class. + * + * @param other children and hashes are moved from this param + * + * @param isBranch bitset of non-empty children in `other` + * + * @param toAllocate allocate space for at least this number of children + * (must be <= branchFactor) + */ explicit TaggedPointer(TaggedPointer&& other, std::uint16_t isBranch, std::uint8_t toAllocate); - /** Given `other` with the specified children in `srcBranches`, create a - new TaggedPointer with the allocated number of children and the - children specified in `dstBranches`. - - @param other children and hashes are moved from this param - - @param srcBranches bitset of non-empty children in `other` - - @param dstBranches bitset of children to copy from `other` (or space to - leave in a sparse array - see note below) - - @param toAllocate allocate space for at least this number of children - (must be <= branchFactor) - - @note a child may be absent in srcBranches but present in dstBranches - (if dst has a sparse representation, space for the new child will be - left in the sparse array). Typically, srcBranches and dstBranches will - differ by at most one bit. The function works correctly if they differ - by more, but there are likely more efficient algorithms to consider if - this becomes a common use-case. - */ + /** + * Given `other` with the specified children in `srcBranches`, create a + * new TaggedPointer with the allocated number of children and the + * children specified in `dstBranches`. + * + * @param other children and hashes are moved from this param + * + * @param srcBranches bitset of non-empty children in `other` + * + * @param dstBranches bitset of children to copy from `other` (or space to + * leave in a sparse array - see note below) + * + * @param toAllocate allocate space for at least this number of children + * (must be <= branchFactor) + * + * @note a child may be absent in srcBranches but present in dstBranches + * (if dst has a sparse representation, space for the new child will be + * left in the sparse array). Typically, srcBranches and dstBranches will + * differ by at most one bit. The function works correctly if they differ + * by more, but there are likely more efficient algorithms to consider if + * this becomes a common use-case. + */ explicit TaggedPointer( TaggedPointer&& other, std::uint16_t srcBranches, @@ -132,68 +143,81 @@ public: ~TaggedPointer(); - /** Decode the tagged pointer into its tag and pointer */ + /** + * Decode the tagged pointer into its tag and pointer + */ [[nodiscard]] std::pair decode() const; - /** Get the number of elements allocated for each array */ + /** + * Get the number of elements allocated for each array + */ [[nodiscard]] std::uint8_t capacity() const; - /** Check if the arrays have a dense format. - - @note The dense format is when there is an array element for all 16 - (branchFactor) possible children. - */ + /** + * Check if the arrays have a dense format. + * + * @note The dense format is when there is an array element for all 16 + * (branchFactor) possible children. + */ [[nodiscard]] bool isDense() const; - /** Get the number of elements in each array and a pointer to the start - of each array. - */ + /** + * Get the number of elements in each array and a pointer to the start + * of each array. + */ [[nodiscard]] std::tuple getHashesAndChildren() const; - /** Get the `hashes` array */ + /** + * Get the `hashes` array + */ [[nodiscard]] SHAMapHash* getHashes() const; - /** Get the `children` array */ + /** + * Get the `children` array + */ [[nodiscard]] SHAMapTreeNodePtr* getChildren() const; - /** Call the `f` callback for all 16 (branchFactor) branches - even if - the branch is empty. - - @param isBranch bitset of non-empty children - - @param f a one parameter callback function. The parameter is the - child's hash. + /** + * Call the `f` callback for all 16 (branchFactor) branches - even if + * the branch is empty. + * + * @param isBranch bitset of non-empty children + * + * @param f a one parameter callback function. The parameter is the + * child's hash. */ template void iterChildren(std::uint16_t isBranch, F&& f) const; - /** Call the `f` callback for all non-empty branches. - - @param isBranch bitset of non-empty children - - @param f a two parameter callback function. The first parameter is - the branch number, the second parameter is the index into the array. - For dense formats these are the same, for sparse they may be - different. + /** + * Call the `f` callback for all non-empty branches. + * + * @param isBranch bitset of non-empty children + * + * @param f a two parameter callback function. The first parameter is + * the branch number, the second parameter is the index into the array. + * For dense formats these are the same, for sparse they may be + * different. */ template void iterNonEmptyChildIndexes(std::uint16_t isBranch, F&& f) const; - /** Get the child's index inside the `hashes` or `children` array (which - may or may not be sparse). The optional will be empty if an empty - branch is requested and the children are sparse. - - @param isBranch bitset of non-empty children - - @param i index of the requested child + /** + * Get the child's index inside the `hashes` or `children` array (which + * may or may not be sparse). The optional will be empty if an empty + * branch is requested and the children are sparse. + * + * @param isBranch bitset of non-empty children + * + * @param i index of the requested child */ [[nodiscard]] std::optional getChildIndex(std::uint16_t isBranch, int i) const; diff --git a/include/xrpl/tx/ApplyContext.h b/include/xrpl/tx/ApplyContext.h index f64957dd35..472afdf624 100644 --- a/include/xrpl/tx/ApplyContext.h +++ b/include/xrpl/tx/ApplyContext.h @@ -21,7 +21,9 @@ namespace xrpl { -/** State information when applying a tx. */ +/** + * State information when applying a tx. + */ class ApplyContext { public: @@ -83,7 +85,9 @@ public: return flags_; } - /** Sets the DeliveredAmount field in the metadata */ + /** + * Sets the DeliveredAmount field in the metadata + */ void deliver(STAmount const& amount) { @@ -91,18 +95,26 @@ public: view_->deliver(amount); } - /** Discard changes and start fresh. */ + /** + * Discard changes and start fresh. + */ void discard(); - /** Apply the transaction result to the base. */ + /** + * Apply the transaction result to the base. + */ std::optional apply(TER); - /** Get the number of unapplied changes. */ + /** + * Get the number of unapplied changes. + */ std::size_t size(); - /** Visit unapplied changes. */ + /** + * Visit unapplied changes. + */ void visit( std::functionrawDestroyXRP(fee); } - /** Applies all invariant checkers one by one. - - @param result the result generated by processing this transaction. - @param fee the fee charged for this transaction - @return the result code that should be returned for this transaction. + /** + * Applies all invariant checkers one by one. + * + * @param result the result generated by processing this transaction. + * @param fee the fee charged for this transaction + * @return the result code that should be returned for this transaction. */ TER checkInvariants(TER const result, XRPAmount const fee); diff --git a/include/xrpl/tx/Transactor.h b/include/xrpl/tx/Transactor.h index bdb51b06ec..a71285f70e 100644 --- a/include/xrpl/tx/Transactor.h +++ b/include/xrpl/tx/Transactor.h @@ -31,7 +31,9 @@ namespace xrpl { -/** State information when preflighting a tx. */ +/** + * State information when preflighting a tx. + */ struct PreflightContext { public: @@ -74,7 +76,9 @@ public: operator=(PreflightContext const&) = delete; }; -/** State information when determining if a tx is likely to claim a fee. */ +/** + * State information when determining if a tx is likely to claim a fee. + */ struct PreclaimContext { public: @@ -161,7 +165,9 @@ public: enum class ConsequencesFactoryType { Normal, Blocker, Custom }; - /** Process the transaction. */ + /** + * Process the transaction. + */ ApplyResult operator()(); @@ -177,16 +183,17 @@ public: return ctx_.view(); } - /** Check all invariants for the current transaction. + /** + * Check all invariants for the current transaction. * - * Runs transaction-specific invariants first (visitInvariantEntry + - * finalizeInvariants), then protocol-level invariants. Both layers - * always run; the worst failure code is returned. + * Runs transaction-specific invariants first (visitInvariantEntry + + * finalizeInvariants), then protocol-level invariants. Both layers + * always run; the worst failure code is returned. * - * @param result the tentative TER from transaction processing. - * @param fee the fee consumed by the transaction. + * @param result the tentative TER from transaction processing. + * @param fee the fee consumed by the transaction. * - * @return the final TER after all invariant checks. + * @return the final TER after all invariant checks. */ [[nodiscard]] TER checkInvariants(TER result, XRPAmount fee); @@ -341,40 +348,42 @@ protected: virtual TER doApply() = 0; - /** Inspect a single ledger entry modified by this transaction. + /** + * Inspect a single ledger entry modified by this transaction. * - * Called once for every SLE created, modified, or deleted by the - * transaction, before finalizeInvariants. Implementations should - * accumulate whatever state they need to verify transaction-specific - * post-conditions. + * Called once for every SLE created, modified, or deleted by the + * transaction, before finalizeInvariants. Implementations should + * accumulate whatever state they need to verify transaction-specific + * post-conditions. * - * @param isDelete true if the entry was erased from the ledger. - * @param before the entry's state before the transaction (nullptr - * for newly created entries). - * @param after the entry's state as supplied by the apply logic - * for this transaction. For deletions, this is the - * SLE being erased and is not guaranteed to be null; - * callers must use isDelete rather than after == nullptr - * to detect deletions. + * @param isDelete true if the entry was erased from the ledger. + * @param before the entry's state before the transaction (nullptr + * for newly created entries). + * @param after the entry's state as supplied by the apply logic + * for this transaction. For deletions, this is the + * SLE being erased and is not guaranteed to be null; + * callers must use isDelete rather than after == nullptr + * to detect deletions. */ virtual void visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) = 0; - /** Check transaction-specific post-conditions after all entries have - * been visited. + /** + * Check transaction-specific post-conditions after all entries have + * been visited. * - * Called once after every modified ledger entry has been passed to - * visitInvariantEntry. Returns true if all transaction-specific - * invariants hold, or false to fail the transaction with - * tecINVARIANT_FAILED. + * Called once after every modified ledger entry has been passed to + * visitInvariantEntry. Returns true if all transaction-specific + * invariants hold, or false to fail the transaction with + * tecINVARIANT_FAILED. * - * @param tx the transaction being applied. - * @param result the tentative TER result so far. - * @param fee the fee consumed by the transaction. - * @param view read-only view of the ledger after the transaction. - * @param j journal for logging invariant failures. + * @param tx the transaction being applied. + * @param result the tentative TER result so far. + * @param fee the fee consumed by the transaction. + * @param view read-only view of the ledger after the transaction. + * @param j journal for logging invariant failures. * - * @return true if all invariants pass; false otherwise. + * @return true if all invariants pass; false otherwise. */ [[nodiscard]] virtual bool finalizeInvariants( @@ -384,14 +393,15 @@ protected: ReadView const& view, beast::Journal const& j) = 0; - /** Compute the minimum fee required to process a transaction - with a given baseFee based on the current server load. - - @param registry The service registry. - @param baseFee The base fee of a candidate transaction - @see xrpl::calculateBaseFee - @param fees Fee settings from the current ledger - @param flags Transaction processing fees + /** + * Compute the minimum fee required to process a transaction + * with a given baseFee based on the current server load. + * + * @param registry The service registry. + * @param baseFee The base fee of a candidate transaction + * @see xrpl::calculateBaseFee + * @param fees Fee settings from the current ledger + * @param flags Transaction processing fees */ static XRPAmount minimumFee(ServiceRegistry& registry, XRPAmount baseFee, Fees const& fees, ApplyFlags flags); @@ -440,12 +450,16 @@ protected: unit::ValueUnit max, unit::ValueUnit min = unit::ValueUnit{}); - /// Minimum will usually be zero. + /** + * Minimum will usually be zero. + */ template static bool validNumericMinimum(std::optional value, T min = T{}); - /// Minimum will usually be zero. + /** + * Minimum will usually be zero. + */ template static bool validNumericMinimum( @@ -493,44 +507,48 @@ private: void trapTransaction(uint256) const; - /** Performs early sanity checks on the account and fee fields. - - (And passes flagMask to preflight0) - - Do not try to call preflight1 from preflight() in derived classes. See - the description of invokePreflight for details. - */ + /** + * Performs early sanity checks on the account and fee fields. + * + * (And passes flagMask to preflight0) + * + * Do not try to call preflight1 from preflight() in derived classes. See + * the description of invokePreflight for details. + */ static NotTEC preflight1(PreflightContext const& ctx, std::uint32_t flagMask); - /** Checks whether the signature appears valid - - Do not try to call preflight2 from preflight() in derived classes. See - the description of invokePreflight for details. - */ + /** + * Checks whether the signature appears valid + * + * Do not try to call preflight2 from preflight() in derived classes. See + * the description of invokePreflight for details. + */ static NotTEC preflight2(PreflightContext const& ctx); - /** Universal validations - - Valid MPTAmount and XRPAmount - - Do not try to call preflightUniversal from preflight() in derived classes. See - the description of invokePreflight for details. - */ + /** + * Universal validations + * - Valid MPTAmount and XRPAmount + * + * Do not try to call preflightUniversal from preflight() in derived classes. See + * the description of invokePreflight for details. + */ static NotTEC preflightUniversal(PreflightContext const& ctx); - /** Check transaction-specific invariants only. + /** + * Check transaction-specific invariants only. * - * Walks every modified ledger entry via visitInvariantEntry, then - * calls finalizeInvariants on the derived transactor. Returns - * tecINVARIANT_FAILED if any transaction invariant is violated. + * Walks every modified ledger entry via visitInvariantEntry, then + * calls finalizeInvariants on the derived transactor. Returns + * tecINVARIANT_FAILED if any transaction invariant is violated. * - * @param result the tentative TER from transaction processing. - * @param fee the fee consumed by the transaction. + * @param result the tentative TER from transaction processing. + * @param fee the fee consumed by the transaction. * - * @return the original result if all invariants pass, or - * tecINVARIANT_FAILED otherwise. + * @return the original result if all invariants pass, or + * tecINVARIANT_FAILED otherwise. */ [[nodiscard]] TER checkTransactionInvariants(TER result, XRPAmount fee); @@ -542,20 +560,24 @@ Transactor::checkExtraFeatures(PreflightContext const& ctx) return true; } -/** Performs early sanity checks on the txid and flags */ +/** + * Performs early sanity checks on the txid and flags + */ NotTEC preflight0(PreflightContext const& ctx, std::uint32_t flagMask); namespace detail { -/** Checks the validity of the transactor signing key. +/** + * Checks the validity of the transactor signing key. * * Normally called from preflight1 with ctx.tx. */ NotTEC preflightCheckSigningKey(STObject const& sigObject, beast::Journal j); -/** Checks the special signing key state needed for simulation +/** + * Checks the special signing key state needed for simulation * * Normally called from preflight2 with ctx.tx. */ diff --git a/include/xrpl/tx/apply.h b/include/xrpl/tx/apply.h index 55d365c31b..8c11d870f1 100644 --- a/include/xrpl/tx/apply.h +++ b/include/xrpl/tx/apply.h @@ -16,88 +16,98 @@ namespace xrpl { class HashRouter; class ServiceRegistry; -/** Describes the pre-processing validity of a transaction. - - @see checkValidity, forceValidity -*/ +/** + * Describes the pre-processing validity of a transaction. + * + * @see checkValidity, forceValidity + */ enum class Validity { - /// Signature is bad. Didn't do local checks. + /** + * Signature is bad. Didn't do local checks. + */ SigBad, - /// Signature is good, but local checks fail. + /** + * Signature is good, but local checks fail. + */ SigGoodOnly, - /// Signature and local checks are good / passed. + /** + * Signature and local checks are good / passed. + */ Valid }; -/** Checks transaction signature and local checks. - - @return A `Validity` enum representing how valid the - `STTx` is and, if not `Valid`, a reason string. - - @note Results are cached internally, so tests will not be - repeated over repeated calls, unless cache expires. - - @return `std::pair`, where `.first` is the status, and - `.second` is the reason if appropriate. - - @see Validity -*/ +/** + * Checks transaction signature and local checks. + * + * @return A `Validity` enum representing how valid the + * `STTx` is and, if not `Valid`, a reason string. + * + * @note Results are cached internally, so tests will not be + * repeated over repeated calls, unless cache expires. + * + * @return `std::pair`, where `.first` is the status, and + * `.second` is the reason if appropriate. + * + * @see Validity + */ std::pair checkValidity(HashRouter& router, STTx const& tx, Rules const& rules); -/** Sets the validity of a given transaction in the cache. - - @warning Use with extreme care. - - @note Can only raise the validity to a more valid state, - and can not override anything cached bad. - - @see checkValidity, Validity -*/ +/** + * Sets the validity of a given transaction in the cache. + * + * @warning Use with extreme care. + * + * @note Can only raise the validity to a more valid state, + * and can not override anything cached bad. + * + * @see checkValidity, Validity + */ void forceValidity(HashRouter& router, uint256 const& txid, Validity validity); -/** Apply a transaction to an `OpenView`. - - This function is the canonical way to apply a transaction - to a ledger. It rolls the validation and application - steps into one function. To do the steps manually, the - correct calling order is: - @code{.cpp} - preflight -> preclaim -> doApply - @endcode - The result of one function must be passed to the next. - The `preflight` result can be safely cached and reused - asynchronously, but `preclaim` and `doApply` must be called - in the same thread and with the same view. - - @note Does not throw. - - For open ledgers, the `Transactor` will catch exceptions - and return `tefEXCEPTION`. For closed ledgers, the - `Transactor` will attempt to only charge a fee, - and return `tecFAILED_PROCESSING`. - - If the `Transactor` gets an exception while trying - to charge the fee, it will be caught and - turned into `tefEXCEPTION`. - - For network health, a `Transactor` makes its - best effort to at least charge a fee if the - ledger is closed. - - @param app The current running `Application`. - @param view The open ledger that the transaction - will attempt to be applied to. - @param tx The transaction to be checked. - @param flags `ApplyFlags` describing processing options. - @param journal A journal. - - @see preflight, preclaim, doApply - - @return A pair with the `TER` and a `bool` indicating - whether or not the transaction was applied. -*/ +/** + * Apply a transaction to an `OpenView`. + * + * This function is the canonical way to apply a transaction + * to a ledger. It rolls the validation and application + * steps into one function. To do the steps manually, the + * correct calling order is: + * @code + * preflight -> preclaim -> doApply + * @endcode + * The result of one function must be passed to the next. + * The `preflight` result can be safely cached and reused + * asynchronously, but `preclaim` and `doApply` must be called + * in the same thread and with the same view. + * + * @note Does not throw. + * + * For open ledgers, the `Transactor` will catch exceptions + * and return `tefEXCEPTION`. For closed ledgers, the + * `Transactor` will attempt to only charge a fee, + * and return `tecFAILED_PROCESSING`. + * + * If the `Transactor` gets an exception while trying + * to charge the fee, it will be caught and + * turned into `tefEXCEPTION`. + * + * For network health, a `Transactor` makes its + * best effort to at least charge a fee if the + * ledger is closed. + * + * @param app The current running `Application`. + * @param view The open ledger that the transaction + * will attempt to be applied to. + * @param tx The transaction to be checked. + * @param flags `ApplyFlags` describing processing options. + * @param journal A journal. + * + * @see preflight, preclaim, doApply + * + * @return A pair with the `TER` and a `bool` indicating + * whether or not the transaction was applied. + */ ApplyResult apply( ServiceRegistry& registry, @@ -106,26 +116,34 @@ apply( ApplyFlags flags, beast::Journal journal); -/** Enum class for return value from `applyTransaction` - - @see applyTransaction -*/ +/** + * Enum class for return value from `applyTransaction` + * + * @see applyTransaction + */ enum class ApplyTransactionResult { - /// Applied to this ledger + /** + * Applied to this ledger + */ Success, - /// Should not be retried in this ledger + /** + * Should not be retried in this ledger + */ Fail, - /// Should be retried in this ledger + /** + * Should be retried in this ledger + */ Retry }; -/** Transaction application helper - - Provides more detailed logging and decodes the - correct behavior based on the `TER` type - - @see ApplyTransactionResult -*/ +/** + * Transaction application helper + * + * Provides more detailed logging and decodes the + * correct behavior based on the `TER` type + * + * @see ApplyTransactionResult + */ ApplyTransactionResult applyTransaction( ServiceRegistry& registry, diff --git a/include/xrpl/tx/applySteps.h b/include/xrpl/tx/applySteps.h index 3298e49192..bd495481f2 100644 --- a/include/xrpl/tx/applySteps.h +++ b/include/xrpl/tx/applySteps.h @@ -33,8 +33,9 @@ struct ApplyResult } }; -/** Return true if the transaction can claim a fee (tec), - and the `ApplyFlags` do not allow soft failures. +/** + * Return true if the transaction can claim a fee (tec), + * and the `ApplyFlags` do not allow soft failures. */ inline bool isTecClaimHardFail(TER ter, ApplyFlags flags) @@ -42,34 +43,51 @@ isTecClaimHardFail(TER ter, ApplyFlags flags) return isTecClaim(ter) && ((flags & TapRetry) == 0u); } -/** Class describing the consequences to the account - of applying a transaction if the transaction consumes - the maximum XRP allowed. -*/ +/** + * Class describing the consequences to the account + * of applying a transaction if the transaction consumes + * the maximum XRP allowed. + */ class TxConsequences { public: - /// Describes how the transaction affects subsequent - /// transactions + /** + * Describes how the transaction affects subsequent + * transactions + */ enum class Category { - /// Moves currency around, creates offers, etc. + /** + * Moves currency around, creates offers, etc. + */ Normal = 0, - /// Affects the ability of subsequent transactions - /// to claim a fee. Eg. `SetRegularKey` + /** + * Affects the ability of subsequent transactions + * to claim a fee. Eg. `SetRegularKey` + */ Blocker }; private: - /// Describes how the transaction affects subsequent - /// transactions + /** + * Describes how the transaction affects subsequent + * transactions + */ bool isBlocker_; - /// Transaction fee + /** + * Transaction fee + */ XRPAmount fee_; - /// Does NOT include the fee. + /** + * Does NOT include the fee. + */ XRPAmount potentialSpend_; - /// SeqProxy of transaction. + /** + * SeqProxy of transaction. + */ SeqProxy seqProx_; - /// Number of sequences consumed. + /** + * Number of sequences consumed. + */ std::uint32_t sequencesConsumed_; public: @@ -77,58 +95,84 @@ public: // Asserts if tesSUCCESS is passed. explicit TxConsequences(NotTEC pfResult); - /// Constructor if the STTx has no notable consequences for the TxQ. + /** + * Constructor if the STTx has no notable consequences for the TxQ. + */ explicit TxConsequences(STTx const& tx); - /// Constructor for a blocker. + /** + * Constructor for a blocker. + */ TxConsequences(STTx const& tx, Category category); - /// Constructor for an STTx that may consume more XRP than the fee. + /** + * Constructor for an STTx that may consume more XRP than the fee. + */ TxConsequences(STTx const& tx, XRPAmount potentialSpend); - /// Constructor for an STTx that consumes more than the usual sequences. + /** + * Constructor for an STTx that consumes more than the usual sequences. + */ TxConsequences(STTx const& tx, std::uint32_t sequencesConsumed); - /// Copy constructor + /** + * Copy constructor + */ TxConsequences(TxConsequences const&) = default; - /// Copy assignment operator + /** + * Copy assignment operator + */ TxConsequences& operator=(TxConsequences const&) = default; - /// Move constructor + /** + * Move constructor + */ TxConsequences(TxConsequences&&) = default; - /// Move assignment operator + /** + * Move assignment operator + */ TxConsequences& operator=(TxConsequences&&) = default; - /// Fee + /** + * Fee + */ [[nodiscard]] XRPAmount fee() const { return fee_; } - /// Potential Spend + /** + * Potential Spend + */ [[nodiscard]] XRPAmount const& potentialSpend() const { return potentialSpend_; } - /// SeqProxy + /** + * SeqProxy + */ [[nodiscard]] SeqProxy seqProxy() const { return seqProx_; } - /// Sequences consumed + /** + * Sequences consumed + */ [[nodiscard]] std::uint32_t sequencesConsumed() const { return sequencesConsumed_; } - /// Returns true if the transaction is a blocker. + /** + * Returns true if the transaction is a blocker. + */ [[nodiscard]] bool isBlocker() const { @@ -145,32 +189,49 @@ public: } }; -/** Describes the results of the `preflight` check - - @note All members are const to make it more difficult - to "fake" a result without calling `preflight`. - @see preflight, preclaim, doApply, apply -*/ +/** + * Describes the results of the `preflight` check + * + * @note All members are const to make it more difficult + * to "fake" a result without calling `preflight`. + * @see preflight, preclaim, doApply, apply + */ struct PreflightResult { public: - /// From the input - the transaction + /** + * From the input - the transaction + */ STTx const& tx; - /// From the input - the batch identifier, if part of a batch + /** + * From the input - the batch identifier, if part of a batch + */ std::optional const parentBatchId; - /// From the input - the rules + /** + * From the input - the rules + */ Rules const rules; - /// Consequences of the transaction + /** + * Consequences of the transaction + */ TxConsequences const consequences; - /// From the input - the flags + /** + * From the input - the flags + */ ApplyFlags const flags; - /// From the input - the journal + /** + * From the input - the journal + */ beast::Journal const j; - /// Intermediate transaction result + /** + * Intermediate transaction result + */ NotTEC const ter; - /// Constructor + /** + * Constructor + */ template PreflightResult(Context const& ctx, std::pair const& result) : tx(ctx.tx) @@ -184,39 +245,58 @@ public: } PreflightResult(PreflightResult const&) = default; - /// Deleted copy assignment operator + /** + * Deleted copy assignment operator + */ PreflightResult& operator=(PreflightResult const&) = delete; }; -/** Describes the results of the `preclaim` check - - @note All members are const to make it more difficult - to "fake" a result without calling `preclaim`. - @see preflight, preclaim, doApply, apply -*/ +/** + * Describes the results of the `preclaim` check + * + * @note All members are const to make it more difficult + * to "fake" a result without calling `preclaim`. + * @see preflight, preclaim, doApply, apply + */ struct PreclaimResult { public: - /// From the input - the ledger view + /** + * From the input - the ledger view + */ ReadView const& view; - /// From the input - the transaction + /** + * From the input - the transaction + */ STTx const& tx; - /// From the input - the batch identifier, if part of a batch + /** + * From the input - the batch identifier, if part of a batch + */ std::optional const parentBatchId; - /// From the input - the flags + /** + * From the input - the flags + */ ApplyFlags const flags; - /// From the input - the journal + /** + * From the input - the journal + */ beast::Journal const j; - /// Intermediate transaction result + /** + * Intermediate transaction result + */ TER const ter; - /// Success flag - whether the transaction is likely to - /// claim a fee + /** + * Success flag - whether the transaction is likely to + * claim a fee + */ bool const likelyToClaimFee{}; - /// Constructor + /** + * Constructor + */ template PreclaimResult(Context const& ctx, TER ter) : view(ctx.view) @@ -230,27 +310,30 @@ public: } PreclaimResult(PreclaimResult const&) = default; - /// Deleted copy assignment operator + /** + * Deleted copy assignment operator + */ PreclaimResult& operator=(PreclaimResult const&) = delete; }; -/** Gate a transaction based on static information. - - The transaction is checked against all possible - validity constraints that do not require a ledger. - - @param app The current running `Application`. - @param rules The `Rules` in effect at the time of the check. - @param tx The transaction to be checked. - @param flags `ApplyFlags` describing processing options. - @param j A journal. - - @see PreflightResult, preclaim, doApply, apply - - @return A `PreflightResult` object containing, among - other things, the `TER` code. -*/ +/** + * Gate a transaction based on static information. + * + * The transaction is checked against all possible + * validity constraints that do not require a ledger. + * + * @param app The current running `Application`. + * @param rules The `Rules` in effect at the time of the check. + * @param tx The transaction to be checked. + * @param flags `ApplyFlags` describing processing options. + * @param j A journal. + * + * @see PreflightResult, preclaim, doApply, apply + * + * @return A `PreflightResult` object containing, among + * other things, the `TER` code. + */ /** @{ */ PreflightResult preflight( @@ -270,86 +353,90 @@ preflight( beast::Journal j); /** @} */ -/** Gate a transaction based on static ledger information. - - The transaction is checked against all possible - validity constraints that DO require a ledger. - - If preclaim succeeds, then the transaction is very - likely to claim a fee. This will determine if the - transaction is safe to relay without being applied - to the open ledger. - - "Succeeds" in this case is defined as returning a - `tes` or `tec`, since both lead to claiming a fee. - - @pre The transaction has been checked - and validated using `preflight` - - @param preflightResult The result of a previous - call to `preflight` for the transaction. - @param app The current running `Application`. - @param view The open ledger that the transaction - will attempt to be applied to. - - @see PreclaimResult, preflight, doApply, apply - - @return A `PreclaimResult` object containing, among - other things the `TER` code and the base fee value for - this transaction. -*/ +/** + * Gate a transaction based on static ledger information. + * + * The transaction is checked against all possible + * validity constraints that DO require a ledger. + * + * If preclaim succeeds, then the transaction is very + * likely to claim a fee. This will determine if the + * transaction is safe to relay without being applied + * to the open ledger. + * + * "Succeeds" in this case is defined as returning a + * `tes` or `tec`, since both lead to claiming a fee. + * + * @pre The transaction has been checked + * and validated using `preflight` + * + * @param preflightResult The result of a previous + * call to `preflight` for the transaction. + * @param app The current running `Application`. + * @param view The open ledger that the transaction + * will attempt to be applied to. + * + * @see PreclaimResult, preflight, doApply, apply + * + * @return A `PreclaimResult` object containing, among + * other things the `TER` code and the base fee value for + * this transaction. + */ PreclaimResult preclaim(PreflightResult const& preflightResult, ServiceRegistry& registry, OpenView const& view); -/** Compute only the expected base fee for a transaction. - - Base fees are transaction specific, so any calculation - needing them must get the base fee for each transaction. - - No validation is done or implied by this function. - - Caller is responsible for handling any exceptions. - Since none should be thrown, that will usually - mean terminating. - - @param view The current open ledger. - @param tx The transaction to be checked. - - @return The base fee. -*/ +/** + * Compute only the expected base fee for a transaction. + * + * Base fees are transaction specific, so any calculation + * needing them must get the base fee for each transaction. + * + * No validation is done or implied by this function. + * + * Caller is responsible for handling any exceptions. + * Since none should be thrown, that will usually + * mean terminating. + * + * @param view The current open ledger. + * @param tx The transaction to be checked. + * + * @return The base fee. + */ XRPAmount calculateBaseFee(ReadView const& view, STTx const& tx); -/** Return the minimum fee that an "ordinary" transaction would pay. - - When computing the FeeLevel for a transaction the TxQ sometimes needs - the know what an "ordinary" or reference transaction would be required - to pay. - - @param view The current open ledger. - @param tx The transaction so the correct multisigner count is used. - - @return The base fee in XRPAmount. -*/ +/** + * Return the minimum fee that an "ordinary" transaction would pay. + * + * When computing the FeeLevel for a transaction the TxQ sometimes needs + * the know what an "ordinary" or reference transaction would be required + * to pay. + * + * @param view The current open ledger. + * @param tx The transaction so the correct multisigner count is used. + * + * @return The base fee in XRPAmount. + */ XRPAmount calculateDefaultBaseFee(ReadView const& view, STTx const& tx); -/** Apply a prechecked transaction to an OpenView. - - @pre The transaction has been checked - and validated using `preflight` and `preclaim` - - @param preclaimResult The result of a previous - call to `preclaim` for the transaction. - @param registry The service registry. - @param view The open ledger that the transaction - will attempt to be applied to. - - @see preflight, preclaim, apply - - @return A pair with the `TER` and a `bool` indicating - whether or not the transaction was applied. -*/ +/** + * Apply a prechecked transaction to an OpenView. + * + * @pre The transaction has been checked + * and validated using `preflight` and `preclaim` + * + * @param preclaimResult The result of a previous + * call to `preclaim` for the transaction. + * @param registry The service registry. + * @param view The open ledger that the transaction + * will attempt to be applied to. + * + * @see preflight, preclaim, apply + * + * @return A pair with the `TER` and a `bool` indicating + * whether or not the transaction was applied. + */ ApplyResult doApply(PreclaimResult const& preclaimResult, ServiceRegistry& registry, OpenView& view); diff --git a/include/xrpl/tx/invariants/InvariantCheck.h b/include/xrpl/tx/invariants/InvariantCheck.h index 0e6b2a361d..bb51105de2 100644 --- a/include/xrpl/tx/invariants/InvariantCheck.h +++ b/include/xrpl/tx/invariants/InvariantCheck.h @@ -376,7 +376,8 @@ public: finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); }; -/** Verify that MPT/XRP STAmounts are canonical in any ledger entries left after the +/** + * Verify that MPT/XRP STAmounts are canonical in any ledger entries left after the * transaction applies. */ class ValidAmounts diff --git a/include/xrpl/tx/invariants/MPTInvariant.h b/include/xrpl/tx/invariants/MPTInvariant.h index 1d39fd13d8..9a546fa400 100644 --- a/include/xrpl/tx/invariants/MPTInvariant.h +++ b/include/xrpl/tx/invariants/MPTInvariant.h @@ -31,16 +31,22 @@ class ValidMPTIssuance // MPToken by an issuer bool mptCreatedByIssuer_ = false; - /// sfReferenceHolding is intended to be set exactly once at vault - /// creation and immutable thereafter; true when that rule was violated. + /** + * sfReferenceHolding is intended to be set exactly once at vault + * creation and immutable thereafter; true when that rule was violated. + */ bool referenceHoldingSetOnCreate_ = false; - /// True when sfReferenceHolding was mutated on an existing MPTokenIssuance. + /** + * True when sfReferenceHolding was mutated on an existing MPTokenIssuance. + */ bool referenceHoldingMutated_ = false; - /// MPTokens and RippleStates deleted during apply. finalize() checks each - /// holder's AccountRoot to detect vault pseudo-account holdings deleted - /// outside VaultDelete. All these checks are gated on fixCleanup3_2_0. + /** + * MPTokens and RippleStates deleted during apply. finalize() checks each + * holder's AccountRoot to detect vault pseudo-account holdings deleted + * outside VaultDelete. All these checks are gated on fixCleanup3_2_0. + */ std::vector> deletedHoldings_; public: diff --git a/include/xrpl/tx/invariants/VaultInvariant.h b/include/xrpl/tx/invariants/VaultInvariant.h index bc8246b234..136c6c4a25 100644 --- a/include/xrpl/tx/invariants/VaultInvariant.h +++ b/include/xrpl/tx/invariants/VaultInvariant.h @@ -21,7 +21,7 @@ namespace xrpl { -/* +/** * @brief Invariants: Vault object and MPTokenIssuance for vault shares * * - vault deleted and vault created is empty @@ -96,7 +96,7 @@ private: * * @param vaultDelta Delta of the vault's asset balance for this transaction. * @param rules Active ledger rules (used to check the amendment). - * @returns The minimum scale to apply when rounding vault-related amounts. + * @return The minimum scale to apply when rounding vault-related amounts. */ [[nodiscard]] std::int32_t computeVaultMinScale(DeltaInfo const& vaultDelta, Rules const& rules) const; @@ -109,7 +109,7 @@ private: * to the vault asset held by @p id. * * @param id Account whose asset delta is requested. - * @returns The delta, or @c std::nullopt if the entry was not touched. + * @return The delta, or @c std::nullopt if the entry was not touched. */ [[nodiscard]] std::optional deltaAssets(AccountID const& id) const; @@ -124,8 +124,8 @@ private: * * @param tx The transaction being applied. * @param fee Fee charged by this transaction. - * @returns The fee-adjusted delta, or @c std::nullopt if the net delta is - * zero or the account entry was not touched. + * @return The fee-adjusted delta, or @c std::nullopt if the net delta is + * zero or the account entry was not touched. */ [[nodiscard]] std::optional deltaAssetsTxAccount(STTx const& tx, XRPAmount fee) const; @@ -138,7 +138,7 @@ private: * returned. * * @param id Account whose share delta is requested. - * @returns The delta, or @c std::nullopt if the entry was not touched. + * @return The delta, or @c std::nullopt if the entry was not touched. */ [[nodiscard]] std::optional deltaShares(AccountID const& id) const; @@ -147,8 +147,8 @@ private: * @brief Check whether a vault holds no assets. * * @param vault Snapshot of the vault to test. - * @returns @c true when both @c assetsAvailable and @c assetsTotal are - * zero. + * @return @c true when both @c assetsAvailable and @c assetsTotal are + * zero. */ [[nodiscard]] static bool isVaultEmpty(Vault const& vault); diff --git a/include/xrpl/tx/paths/AMMLiquidity.h b/include/xrpl/tx/paths/AMMLiquidity.h index bf62155b61..1904445554 100644 --- a/include/xrpl/tx/paths/AMMLiquidity.h +++ b/include/xrpl/tx/paths/AMMLiquidity.h @@ -17,7 +17,8 @@ namespace xrpl { template class AMMOffer; -/** AMMLiquidity class provides AMM offers to BookStep class. +/** + * AMMLiquidity class provides AMM offers to BookStep class. * The offers are generated in two ways. If there are multiple * paths specified to the payment transaction then the offers * are generated based on the Fibonacci sequence with @@ -59,7 +60,8 @@ public: AMMLiquidity& operator=(AMMLiquidity const&) = delete; - /** Generate AMM offer. Returns nullopt if clobQuality is provided + /** + * Generate AMM offer. Returns nullopt if clobQuality is provided * and it is better than AMM offer quality. Otherwise returns AMM offer. * If clobQuality is provided then AMM offer size is set based on the * quality. @@ -104,12 +106,14 @@ public: } private: - /** Fetches current AMM balances. + /** + * Fetches current AMM balances. */ [[nodiscard]] TAmounts fetchBalances(ReadView const& view) const; - /** Generate AMM offers with the offer size based on Fibonacci sequence. + /** + * Generate AMM offers with the offer size based on Fibonacci sequence. * The sequence corresponds to the payment engine iterations with AMM * liquidity. Iterations that don't consume AMM offers don't count. * The number of iterations with AMM offers is limited. @@ -119,7 +123,8 @@ private: [[nodiscard]] TAmounts generateFibSeqOffer(TAmounts const& balances) const; - /** Generate max offer. + /** + * Generate max offer. * If `fixAMMOverflowOffer` is active, the offer is generated as: * takerGets = 99% * balances.out takerPays = swapOut(takerGets). * Return nullopt if takerGets is 0 or takerGets == balances.out. diff --git a/include/xrpl/tx/paths/AMMOffer.h b/include/xrpl/tx/paths/AMMOffer.h index 6e7a5ceca5..8e7ffedc10 100644 --- a/include/xrpl/tx/paths/AMMOffer.h +++ b/include/xrpl/tx/paths/AMMOffer.h @@ -20,7 +20,8 @@ template class AMMLiquidity; class QualityFunction; -/** Represents synthetic AMM offer in BookStep. AMMOffer mirrors TOffer +/** + * Represents synthetic AMM offer in BookStep. AMMOffer mirrors TOffer * methods for use in generic BookStep methods. AMMOffer amounts * are changed indirectly in BookStep limiting steps. */ @@ -87,14 +88,16 @@ public: return consumed_; } - /** Limit out of the provided offer. If one-path then swapOut + /** + * Limit out of the provided offer. If one-path then swapOut * using current balances. If multi-path then ceil_out using * current quality. */ [[nodiscard]] TAmounts limitOut(TAmounts const& offerAmount, TOut const& limit, bool roundUp) const; - /** Limit in of the provided offer. If one-path then swapIn + /** + * Limit in of the provided offer. If one-path then swapIn * using current balances. If multi-path then ceil_in using * current quality. */ @@ -104,7 +107,8 @@ public: [[nodiscard]] QualityFunction getQualityFunc() const; - /** Send funds without incurring the transfer fee + /** + * Send funds without incurring the transfer fee */ template static TER @@ -131,7 +135,8 @@ public: return {ofrInRate, QUALITY_ONE}; } - /** Check the new pool product is greater or equal to the old pool + /** + * Check the new pool product is greater or equal to the old pool * product or if decreases then within some threshold. */ [[nodiscard]] bool diff --git a/include/xrpl/tx/paths/BookTip.h b/include/xrpl/tx/paths/BookTip.h index bad007ca5b..0a7e3c343e 100644 --- a/include/xrpl/tx/paths/BookTip.h +++ b/include/xrpl/tx/paths/BookTip.h @@ -11,10 +11,11 @@ namespace xrpl { class Logs; -/** Iterates and consumes raw offers in an order book. - Offers are presented from highest quality to lowest quality. This will - return all offers present including missing, invalid, unfunded, etc. -*/ +/** + * Iterates and consumes raw offers in an order book. + * Offers are presented from highest quality to lowest quality. This will + * return all offers present including missing, invalid, unfunded, etc. + */ class BookTip { private: @@ -28,7 +29,9 @@ private: Quality quality_{}; public: - /** Create the iterator. */ + /** + * Create the iterator. + */ BookTip(ApplyView& view, Book const& book); [[nodiscard]] uint256 const& @@ -55,10 +58,11 @@ public: return entry_; } - /** Erases the current offer and advance to the next offer. - Complexity: Constant - @return `true` if there is a next offer - */ + /** + * Erases the current offer and advance to the next offer. + * Complexity: Constant + * @return `true` if there is a next offer + */ bool step(beast::Journal j); }; diff --git a/include/xrpl/tx/paths/Flow.h b/include/xrpl/tx/paths/Flow.h index f73b9a3440..af056ce2fe 100644 --- a/include/xrpl/tx/paths/Flow.h +++ b/include/xrpl/tx/paths/Flow.h @@ -18,25 +18,25 @@ struct FlowDebugInfo; } // namespace path::detail /** - Make a payment from the src account to the dst account - - @param view Trust lines and balances - @param deliver Amount to deliver to the dst account - @param src Account providing input funds for the payment - @param dst Account receiving the payment - @param paths Set of paths to explore for liquidity - @param defaultPaths Include defaultPaths in the path set - @param partialPayment If the payment cannot deliver the entire - requested amount, deliver as much as possible, given the constraints - @param ownerPaysTransferFee If true then owner, not sender, pays fee - @param offerCrossing If Yes or Sell then flow is executing offer crossing, not - payments - @param limitQuality Do not use liquidity below this quality threshold - @param sendMax Do not spend more than this amount - @param j Journal to write journal messages to - @param flowDebugInfo If non-null a pointer to FlowDebugInfo for debugging - @return Actual amount in and out, and the result code -*/ + * Make a payment from the src account to the dst account + * + * @param view Trust lines and balances + * @param deliver Amount to deliver to the dst account + * @param src Account providing input funds for the payment + * @param dst Account receiving the payment + * @param paths Set of paths to explore for liquidity + * @param defaultPaths Include defaultPaths in the path set + * @param partialPayment If the payment cannot deliver the entire + * requested amount, deliver as much as possible, given the constraints + * @param ownerPaysTransferFee If true then owner, not sender, pays fee + * @param offerCrossing If Yes or Sell then flow is executing offer crossing, not + * payments + * @param limitQuality Do not use liquidity below this quality threshold + * @param sendMax Do not spend more than this amount + * @param j Journal to write journal messages to + * @param flowDebugInfo If non-null a pointer to FlowDebugInfo for debugging + * @return Actual amount in and out, and the result code + */ path::RippleCalc::Output flow( PaymentSandbox& view, diff --git a/include/xrpl/tx/paths/Offer.h b/include/xrpl/tx/paths/Offer.h index d7daa30cab..7f368fc2dd 100644 --- a/include/xrpl/tx/paths/Offer.h +++ b/include/xrpl/tx/paths/Offer.h @@ -44,38 +44,44 @@ public: TOffer(SLE::pointer entry, Quality quality); - /** Returns the quality of the offer. - Conceptually, the quality is the ratio of output to input currency. - The implementation calculates it as the ratio of input to output - currency (so it sorts ascending). The quality is computed at the time - the offer is placed, and never changes for the lifetime of the offer. - This is an important business rule that maintains accuracy when an - offer is partially filled; Subsequent partial fills will use the - original quality. - */ + /** + * Returns the quality of the offer. + * Conceptually, the quality is the ratio of output to input currency. + * The implementation calculates it as the ratio of input to output + * currency (so it sorts ascending). The quality is computed at the time + * the offer is placed, and never changes for the lifetime of the offer. + * This is an important business rule that maintains accuracy when an + * offer is partially filled; Subsequent partial fills will use the + * original quality. + */ [[nodiscard]] Quality quality() const noexcept { return quality_; } - /** Returns the account id of the offer's owner. */ + /** + * Returns the account id of the offer's owner. + */ [[nodiscard]] AccountID const& owner() const { return accountID_; } - /** Returns the in and out amounts. - Some or all of the out amount may be unfunded. - */ + /** + * Returns the in and out amounts. + * Some or all of the out amount may be unfunded. + */ [[nodiscard]] TAmounts const& amount() const { return amounts_; } - /** Returns `true` if no more funds can flow through this offer. */ + /** + * Returns `true` if no more funds can flow through this offer. + */ [[nodiscard]] bool fullyConsumed() const { @@ -86,7 +92,9 @@ public: return false; } - /** Adjusts the offer to indicate that we consumed some (or all) of it. */ + /** + * Adjusts the offer to indicate that we consumed some (or all) of it. + */ void consume(ApplyView& view, TAmounts const& consumed) { @@ -142,7 +150,8 @@ public: return {ofrInRate, ofrOutRate}; } - /** Check any required invariant. Limit order book offer + /** + * Check any required invariant. Limit order book offer * always returns true. */ [[nodiscard]] bool diff --git a/include/xrpl/tx/paths/OfferStream.h b/include/xrpl/tx/paths/OfferStream.h index 8ecd495d1a..28eefb5d66 100644 --- a/include/xrpl/tx/paths/OfferStream.h +++ b/include/xrpl/tx/paths/OfferStream.h @@ -85,10 +85,11 @@ public: virtual ~TOfferStreamBase() = default; - /** Returns the offer at the tip of the order book. - Offers are always presented in decreasing quality. - Only valid if step() returned `true`. - */ + /** + * Returns the offer at the tip of the order book. + * Offers are always presented in decreasing quality. + * Only valid if step() returned `true`. + */ [[nodiscard]] TOffer& tip() const { @@ -96,13 +97,14 @@ public: return const_cast(this)->offer_; } - /** Advance to the next valid offer. - This automatically removes: - - Offers with missing ledger entries - - Offers found unfunded - - expired offers - @return `true` if there is a valid offer. - */ + /** + * Advance to the next valid offer. + * This automatically removes: + * - Offers with missing ledger entries + * - Offers found unfunded + * - expired offers + * @return `true` if there is a valid offer. + */ bool step(); @@ -114,23 +116,24 @@ public: } }; -/** Presents and consumes the offers in an order book. - - The `view_' ` `ApplyView` accumulates changes to the ledger. - The `cancelView_` is used to determine if an offer is found - unfunded or became unfunded. - The `permToRemove` collection identifies offers that should be - removed even if the strand associated with this OfferStream - is not applied. - - Certain invalid offers are added to the `permToRemove` collection: - - Offers with missing ledger entries - - Offers that expired - - Offers found unfunded: - An offer is found unfunded when the corresponding balance is zero - and the caller has not modified the balance. This is accomplished - by also looking up the balance in the cancel view. -*/ +/** + * Presents and consumes the offers in an order book. + * + * The `view_' ` `ApplyView` accumulates changes to the ledger. + * The `cancelView_` is used to determine if an offer is found + * unfunded or became unfunded. + * The `permToRemove` collection identifies offers that should be + * removed even if the strand associated with this OfferStream + * is not applied. + * + * Certain invalid offers are added to the `permToRemove` collection: + * - Offers with missing ledger entries + * - Offers that expired + * - Offers found unfunded: + * An offer is found unfunded when the corresponding balance is zero + * and the caller has not modified the balance. This is accomplished + * by also looking up the balance in the cancel view. + */ template class FlowOfferStream : public TOfferStreamBase { diff --git a/include/xrpl/tx/paths/RippleCalc.h b/include/xrpl/tx/paths/RippleCalc.h index 62c966d384..c3ee9165a6 100644 --- a/include/xrpl/tx/paths/RippleCalc.h +++ b/include/xrpl/tx/paths/RippleCalc.h @@ -20,11 +20,12 @@ namespace detail { struct FlowDebugInfo; } // namespace detail -/** RippleCalc calculates the quality of a payment path. - - Quality is the amount of input required to produce a given output along a - specified path - another name for this is exchange rate. -*/ +/** + * RippleCalc calculates the quality of a payment path. + * + * Quality is the amount of input required to produce a given output along a + * specified path - another name for this is exchange rate. + */ class RippleCalc { public: diff --git a/include/xrpl/tx/paths/detail/FlatSets.h b/include/xrpl/tx/paths/detail/FlatSets.h index c0fc8fa417..267c0499a2 100644 --- a/include/xrpl/tx/paths/detail/FlatSets.h +++ b/include/xrpl/tx/paths/detail/FlatSets.h @@ -4,11 +4,12 @@ namespace xrpl { -/** Given two flat sets dst and src, compute dst = dst union src - - @param dst set to store the resulting union, and also a source of elements - for the union - @param src second source of elements for the union +/** + * Given two flat sets dst and src, compute dst = dst union src + * + * @param dst set to store the resulting union, and also a source of elements + * for the union + * @param src second source of elements for the union */ template void diff --git a/include/xrpl/tx/paths/detail/Steps.h b/include/xrpl/tx/paths/detail/Steps.h index 7eb6b938a5..8ee37c026c 100644 --- a/include/xrpl/tx/paths/detail/Steps.h +++ b/include/xrpl/tx/paths/detail/Steps.h @@ -50,31 +50,31 @@ issues(DebtDirection dir) } /** - A step in a payment path - - There are five concrete step classes: - DirectStepI is an IOU step between accounts - BookStepII is an IOU/IOU offer book - BookStepIX is an IOU/XRP offer book - BookStepXI is an XRP/IOU offer book - XRPEndpointStep is the source or destination account for XRP - MPTEndpointStep is the source or destination account for MPT - - Amounts may be transformed through a step in either the forward or the - reverse direction. In the forward direction, the function `fwd` is used to - find the amount the step would output given an input amount. In the reverse - direction, the function `rev` is used to find the amount of input needed to - produce the desired output. - - Amounts are always transformed using liquidity with the same quality (quality - is the amount out/amount in). For example, a BookStep may use multiple offers - when executing `fwd` or `rev`, but all those offers will be from the same - quality directory. - - A step may not have enough liquidity to transform the entire requested - amount. Both `fwd` and `rev` return a pair of amounts (one for input amount, - one for output amount) that show how much of the requested amount the step - was actually able to use. + * A step in a payment path + * + * There are five concrete step classes: + * DirectStepI is an IOU step between accounts + * BookStepII is an IOU/IOU offer book + * BookStepIX is an IOU/XRP offer book + * BookStepXI is an XRP/IOU offer book + * XRPEndpointStep is the source or destination account for XRP + * MPTEndpointStep is the source or destination account for MPT + * + * Amounts may be transformed through a step in either the forward or the + * reverse direction. In the forward direction, the function `fwd` is used to + * find the amount the step would output given an input amount. In the reverse + * direction, the function `rev` is used to find the amount of input needed to + * produce the desired output. + * + * Amounts are always transformed using liquidity with the same quality (quality + * is the amount out/amount in). For example, a BookStep may use multiple offers + * when executing `fwd` or `rev`, but all those offers will be from the same + * quality directory. + * + * A step may not have enough liquidity to transform the entire requested + * amount. Both `fwd` and `rev` return a pair of amounts (one for input amount, + * one for output amount) that show how much of the requested amount the step + * was actually able to use. */ class Step { @@ -82,17 +82,17 @@ public: virtual ~Step() = default; /** - Find the amount we need to put into the step to get the requested out - subject to liquidity limits - - @param sb view with the strand's state of balances and offers - @param afView view the state of balances before the strand runs - this determines if an offer becomes unfunded or is found unfunded - @param ofrsToRm offers found unfunded or in an error state are added to - this collection - @param out requested step output - @return actual step input and output - */ + * Find the amount we need to put into the step to get the requested out + * subject to liquidity limits + * + * @param sb view with the strand's state of balances and offers + * @param afView view the state of balances before the strand runs + * this determines if an offer becomes unfunded or is found unfunded + * @param ofrsToRm offers found unfunded or in an error state are added to + * this collection + * @param out requested step output + * @return actual step input and output + */ virtual std::pair rev(PaymentSandbox& sb, ApplyView& afView, @@ -100,17 +100,17 @@ public: EitherAmount const& out) = 0; /** - Find the amount we get out of the step given the input - subject to liquidity limits - - @param sb view with the strand's state of balances and offers - @param afView view the state of balances before the strand runs - this determines if an offer becomes unfunded or is found unfunded - @param ofrsToRm offers found unfunded or in an error state are added to - this collection - @param in requested step input - @return actual step input and output - */ + * Find the amount we get out of the step given the input + * subject to liquidity limits + * + * @param sb view with the strand's state of balances and offers + * @param afView view the state of balances before the strand runs + * this determines if an offer becomes unfunded or is found unfunded + * @param ofrsToRm offers found unfunded or in an error state are added to + * this collection + * @param in requested step input + * @return actual step input and output + */ virtual std::pair fwd(PaymentSandbox& sb, ApplyView& afView, @@ -118,23 +118,23 @@ public: EitherAmount const& in) = 0; /** - Amount of currency computed coming into the Step the last time the - step ran in reverse. - */ + * Amount of currency computed coming into the Step the last time the + * step ran in reverse. + */ [[nodiscard]] virtual std::optional cachedIn() const = 0; /** - Amount of currency computed coming out of the Step the last time the - step ran in reverse. - */ + * Amount of currency computed coming out of the Step the last time the + * step ran in reverse. + */ [[nodiscard]] virtual std::optional cachedOut() const = 0; /** - If this step is DirectStepI (IOU->IOU direct step), return the src - account. This is needed for checkNoRipple. - */ + * If this step is DirectStepI (IOU->IOU direct step), return the src + * account. This is needed for checkNoRipple. + */ [[nodiscard]] virtual std::optional directStepSrcAcct() const { @@ -150,19 +150,19 @@ public: } /** - If this step is a DirectStepI and the src redeems to the dst, return - true, otherwise return false. If this step is a BookStep, return false if - the owner pays the transfer fee, otherwise return true. - - @param sb view with the strand's state of balances and offers - @param dir reverse -> called from rev(); forward -> called from fwd(). - */ + * If this step is a DirectStepI and the src redeems to the dst, return + * true, otherwise return false. If this step is a BookStep, return false if + * the owner pays the transfer fee, otherwise return true. + * + * @param sb view with the strand's state of balances and offers + * @param dir reverse -> called from rev(); forward -> called from fwd(). + */ [[nodiscard]] virtual DebtDirection debtDirection(ReadView const& sb, StrandDirection dir) const = 0; /** - If this step is a DirectStepI, return the quality in of the dst account. - */ + * If this step is a DirectStepI, return the quality in of the dst account. + */ [[nodiscard]] virtual std::uint32_t lineQualityIn(ReadView const&) const { @@ -170,22 +170,23 @@ public: } /** - Find an upper bound of quality for the step - - @param v view to query the ledger state from - @param prevStepDir Set to DebtDirection::redeems if the previous step redeems. - @return A pair. The first element is the upper bound of quality for the step, or std::nullopt - if the step is dry. The second element will be set to DebtDirection::redeems if this - steps redeems, DebtDirection:issues if this step issues. - @note It is an upper bound because offers on the books may be unfunded. If there is always a - funded offer at the tip of the book, then we could rename this `theoreticalQuality` - rather than `qualityUpperBound`. It could still differ from the actual quality, but - except for "dust" amounts, it should be a good estimate for the actual quality. - */ + * Find an upper bound of quality for the step + * + * @param v view to query the ledger state from + * @param prevStepDir Set to DebtDirection::redeems if the previous step redeems. + * @return A pair. The first element is the upper bound of quality for the step, or std::nullopt + * if the step is dry. The second element will be set to DebtDirection::redeems if this + * steps redeems, DebtDirection:issues if this step issues. + * @note It is an upper bound because offers on the books may be unfunded. If there is always a + * funded offer at the tip of the book, then we could rename this `theoreticalQuality` + * rather than `qualityUpperBound`. It could still differ from the actual quality, but + * except for "dust" amounts, it should be a good estimate for the actual quality. + */ [[nodiscard]] virtual std::pair, DebtDirection> qualityUpperBound(ReadView const& v, DebtDirection prevStepDir) const = 0; - /** Get QualityFunction. Used in one path optimization where + /** + * Get QualityFunction. Used in one path optimization where * the quality function is non-constant (has AMM) and there is * limitQuality. QualityFunction allows calculation of * required path output given requested limitQuality. @@ -195,12 +196,13 @@ public: [[nodiscard]] virtual std::pair, DebtDirection> getQualityFunc(ReadView const& v, DebtDirection prevStepDir) const; - /** Return the number of offers consumed or partially consumed the last time - the step ran, including expired and unfunded offers. - - N.B. This this not the total number offers consumed by this step for the - entire payment, it is only the number the last time it ran. Offers may - be partially consumed multiple times during a payment. + /** + * Return the number of offers consumed or partially consumed the last time + * the step ran, including expired and unfunded offers. + * + * N.B. This this not the total number offers consumed by this step for the + * entire payment, it is only the number the last time it ran. Offers may + * be partially consumed multiple times during a payment. */ [[nodiscard]] virtual std::uint32_t offersUsed() const @@ -209,8 +211,8 @@ public: } /** - If this step is a BookStep, return the book. - */ + * If this step is a BookStep, return the book. + */ [[nodiscard]] virtual std::optional bookStepBook() const { @@ -218,15 +220,15 @@ public: } /** - Check if amount is zero - */ + * Check if amount is zero + */ [[nodiscard]] virtual bool isZero(EitherAmount const& out) const = 0; /** - Return true if the step should be considered inactive. - A strand that has additional liquidity may be marked inactive if a step - has consumed too many offers. + * Return true if the step should be considered inactive. + * A strand that has additional liquidity may be marked inactive if a step + * has consumed too many offers. */ [[nodiscard]] virtual bool inactive() const @@ -235,55 +237,59 @@ public: } /** - Return true if Out of lhs == Out of rhs. - */ + * Return true if Out of lhs == Out of rhs. + */ [[nodiscard]] virtual bool equalOut(EitherAmount const& lhs, EitherAmount const& rhs) const = 0; /** - Return true if In of lhs == In of rhs. - */ + * Return true if In of lhs == In of rhs. + */ [[nodiscard]] virtual bool equalIn(EitherAmount const& lhs, EitherAmount const& rhs) const = 0; /** - Check that the step can correctly execute in the forward direction - - @param sb view with the strands state of balances and offers - @param afView view the state of balances before the strand runs - this determines if an offer becomes unfunded or is found unfunded - @param in requested step input - @return first element is true if step is valid, second element is out - amount - */ + * Check that the step can correctly execute in the forward direction + * + * @param sb view with the strands state of balances and offers + * @param afView view the state of balances before the strand runs + * this determines if an offer becomes unfunded or is found unfunded + * @param in requested step input + * @return first element is true if step is valid, second element is out + * amount + */ virtual std::pair validFwd(PaymentSandbox& sb, ApplyView& afView, EitherAmount const& in) = 0; - /** Return true if lhs == rhs. - - @param lhs Step to compare. - @param rhs Step to compare. - @return true if lhs == rhs. - */ + /** + * Return true if lhs == rhs. + * + * @param lhs Step to compare. + * @param rhs Step to compare. + * @return true if lhs == rhs. + */ friend bool operator==(Step const& lhs, Step const& rhs) { return lhs.equal(rhs); } - /** Return true if lhs != rhs. - - @param lhs Step to compare. - @param rhs Step to compare. - @return true if lhs != rhs. - */ + /** + * Return true if lhs != rhs. + * + * @param lhs Step to compare. + * @param rhs Step to compare. + * @return true if lhs != rhs. + */ friend bool operator!=(Step const& lhs, Step const& rhs) { return !(lhs == rhs); } - /** Streaming operator for a Step. */ + /** + * Streaming operator for a Step. + */ friend std::ostream& operator<<(std::ostream& stream, Step const& step) { @@ -308,7 +314,7 @@ Step::getQualityFunc(ReadView const& v, DebtDirection prevStepDir) const return {std::nullopt, res.second}; } -/// @cond INTERNAL +/** @cond INTERNAL */ using Strand = std::vector>; inline std::uint32_t @@ -322,9 +328,9 @@ offersUsed(Strand const& strand) } return r; } -/// @endcond +/** @endcond */ -/// @cond INTERNAL +/** @cond INTERNAL */ inline bool operator==(Strand const& lhs, Strand const& rhs) { @@ -337,21 +343,21 @@ operator==(Strand const& lhs, Strand const& rhs) } return true; } -/// @endcond +/** @endcond */ -/* - Normalize a path by inserting implied accounts and offers - - @param src Account that is sending assets - @param dst Account that is receiving assets - @param deliver Asset the dst account will receive - (if issuer of deliver == dst, then accept any issuer) - @param sendMax Optional asset to send. - @param path Liquidity sources to use for this strand of the payment. The path - contains an ordered collection of the offer books to use and - accounts to ripple through. - @return error code and normalized path -*/ +/** + * Normalize a path by inserting implied accounts and offers + * + * @param src Account that is sending assets + * @param dst Account that is receiving assets + * @param deliver Asset the dst account will receive + * (if issuer of deliver == dst, then accept any issuer) + * @param sendMax Optional asset to send. + * @param path Liquidity sources to use for this strand of the payment. The path + * contains an ordered collection of the offer books to use and + * accounts to ripple through. + * @return error code and normalized path + */ std::pair normalizePath( AccountID const& src, @@ -361,29 +367,29 @@ normalizePath( STPath const& path); /** - Create a Strand for the specified path - - @param sb view for trust lines, balances, and attributes like auth and freeze - @param src Account that is sending assets - @param dst Account that is receiving assets - @param deliver Asset the dst account will receive - (if issuer of deliver == dst, then accept any issuer) - @param limitQuality Offer crossing BookSteps use this value in an - optimization. If, during direct offer crossing, the - quality of the tip of the book drops below this value, - then evaluating the strand can stop. - @param sendMaxAsset Optional asset to send. - @param path Liquidity sources to use for this strand of the payment. The path - contains an ordered collection of the offer books to use and - accounts to ripple through. - @param ownerPaysTransferFee false -> charge sender; true -> charge offer - owner - @param offerCrossing false -> payment; true -> offer crossing - @param ammContext counts iterations with AMM offers - @param domainID the domain that order books will use - @param j Journal for logging messages - @return Error code and constructed Strand -*/ + * Create a Strand for the specified path + * + * @param sb view for trust lines, balances, and attributes like auth and freeze + * @param src Account that is sending assets + * @param dst Account that is receiving assets + * @param deliver Asset the dst account will receive + * (if issuer of deliver == dst, then accept any issuer) + * @param limitQuality Offer crossing BookSteps use this value in an + * optimization. If, during direct offer crossing, the + * quality of the tip of the book drops below this value, + * then evaluating the strand can stop. + * @param sendMaxAsset Optional asset to send. + * @param path Liquidity sources to use for this strand of the payment. The path + * contains an ordered collection of the offer books to use and + * accounts to ripple through. + * @param ownerPaysTransferFee false -> charge sender; true -> charge offer + * owner + * @param offerCrossing false -> payment; true -> offer crossing + * @param ammContext counts iterations with AMM offers + * @param domainID the domain that order books will use + * @param j Journal for logging messages + * @return Error code and constructed Strand + */ std::pair toStrand( ReadView const& sb, @@ -400,31 +406,31 @@ toStrand( beast::Journal j); /** - Create a Strand for each specified path (including the default path, if - indicated) - - @param sb View for trust lines, balances, and attributes like auth and freeze - @param src Account that is sending assets - @param dst Account that is receiving assets - @param deliver Asset the dst account will receive - (if issuer of deliver == dst, then accept any issuer) - @param limitQuality Offer crossing BookSteps use this value in an - optimization. If, during direct offer crossing, the - quality of the tip of the book drops below this value, - then evaluating the strand can stop. - @param sendMax Optional asset to send. - @param paths Paths to use to fulfill the payment. Each path in the pathset - contains an ordered collection of the offer books to use and - accounts to ripple through. - @param addDefaultPath Determines if the default path should be included - @param ownerPaysTransferFee false -> charge sender; true -> charge offer - owner - @param offerCrossing false -> payment; true -> offer crossing - @param ammContext counts iterations with AMM offers - @param domainID the domain that order books will use - @param j Journal for logging messages - @return error code and collection of strands -*/ + * Create a Strand for each specified path (including the default path, if + * indicated) + * + * @param sb View for trust lines, balances, and attributes like auth and freeze + * @param src Account that is sending assets + * @param dst Account that is receiving assets + * @param deliver Asset the dst account will receive + * (if issuer of deliver == dst, then accept any issuer) + * @param limitQuality Offer crossing BookSteps use this value in an + * optimization. If, during direct offer crossing, the + * quality of the tip of the book drops below this value, + * then evaluating the strand can stop. + * @param sendMax Optional asset to send. + * @param paths Paths to use to fulfill the payment. Each path in the pathset + * contains an ordered collection of the offer books to use and + * accounts to ripple through. + * @param addDefaultPath Determines if the default path should be included + * @param ownerPaysTransferFee false -> charge sender; true -> charge offer + * owner + * @param offerCrossing false -> payment; true -> offer crossing + * @param ammContext counts iterations with AMM offers + * @param domainID the domain that order books will use + * @param j Journal for logging messages + * @return error code and collection of strands + */ std::pair> toStrands( ReadView const& sb, @@ -441,7 +447,7 @@ toStrands( std::optional const& domainID, beast::Journal j); -/// @cond INTERNAL +/** @cond INTERNAL */ template struct StepImp : public Step { @@ -490,9 +496,9 @@ public: } friend TDerived; }; -/// @endcond +/** @endcond */ -/// @cond INTERNAL +/** @cond INTERNAL */ // Thrown when unexpected errors occur class FlowException : public std::runtime_error { @@ -507,9 +513,9 @@ public: { } }; -/// @endcond +/** @endcond */ -/// @cond INTERNAL +/** @cond INTERNAL */ // Check equal with tolerance bool checkNear(IOUAmount const& expected, IOUAmount const& actual); @@ -523,10 +529,10 @@ checkNear(XRPAmount const& expected, XRPAmount const& actual) { return expected == actual; } -/// @endcond +/** @endcond */ /** - Context needed to build Strand Steps and for error checking + * Context needed to build Strand Steps and for error checking */ struct StrandContext { @@ -541,25 +547,30 @@ struct StrandContext OfferCrossing const offerCrossing; ///< Yes/Sell if offer crossing, not payment bool const isDefaultPath; ///< true if Strand is default path size_t const strandSize; ///< Length of Strand - /** The previous step in the strand. Needed to check the no ripple - constraint + /** + * The previous step in the strand. Needed to check the no ripple + * constraint */ Step const* const prevStep = nullptr; - /** A strand may not include the same account node more than once - in the same currency. In a direct step, an account will show up - at most twice: once as a src and once as a dst (hence the two element - array). The strandSrc and strandDst will only show up once each. - */ + /** + * A strand may not include the same account node more than once + * in the same currency. In a direct step, an account will show up + * at most twice: once as a src and once as a dst (hence the two element + * array). The strandSrc and strandDst will only show up once each. + */ std::array, 2>& seenDirectAssets; - /** A strand may not include an offer that output the same issue more - than once - */ + /** + * A strand may not include an offer that output the same issue more + * than once + */ boost::container::flat_set& seenBookOuts; AMMContext& ammContext; std::optional domainID; // the domain the order book will use beast::Journal const j; - /** StrandContext constructor. */ + /** + * StrandContext constructor. + */ StrandContext( ReadView const& view, std::vector> const& strand, @@ -581,7 +592,7 @@ struct StrandContext beast::Journal j); ///< Journal for logging }; -/// @cond INTERNAL +/** @cond INTERNAL */ namespace test { // Needed for testing bool @@ -659,6 +670,6 @@ isDirectXrpToXrp(Strand const& strand) return false; } } -/// @endcond +/** @endcond */ } // namespace xrpl diff --git a/include/xrpl/tx/paths/detail/StrandFlow.h b/include/xrpl/tx/paths/detail/StrandFlow.h index fe657b2100..c932c49cca 100644 --- a/include/xrpl/tx/paths/detail/StrandFlow.h +++ b/include/xrpl/tx/paths/detail/StrandFlow.h @@ -39,7 +39,9 @@ namespace xrpl { -/** Result of flow() execution of a single Strand. */ +/** + * Result of flow() execution of a single Strand. + */ template struct StrandResult { @@ -56,7 +58,9 @@ struct StrandResult bool inactive = false; ///< Strand should not considered as a further ///< source of liquidity (dry) - /** Strand result constructor */ + /** + * Strand result constructor + */ StrandResult() = default; StrandResult( @@ -83,15 +87,15 @@ struct StrandResult }; /** - Request `out` amount from a strand - - @param baseView Trust lines and balances - @param strand Steps of Accounts to ripple through and offer books to use - @param maxIn Max amount of input allowed - @param out Amount of output requested from the strand - @param j Journal to write log messages to - @return Actual amount in and out from the strand, errors, offers to remove, - and payment sandbox + * Request `out` amount from a strand + * + * @param baseView Trust lines and balances + * @param strand Steps of Accounts to ripple through and offer books to use + * @param maxIn Max amount of input allowed + * @param out Amount of output requested from the strand + * @param j Journal to write log messages to + * @return Actual amount in and out from the strand, errors, offers to remove, + * and payment sandbox */ template StrandResult @@ -296,7 +300,7 @@ flow( } } -/// @cond INTERNAL +/** @cond INTERNAL */ template struct FlowResult { @@ -335,9 +339,9 @@ struct FlowResult { } }; -/// @endcond +/** @endcond */ -/// @cond INTERNAL +/** @cond INTERNAL */ inline std::optional qualityUpperBound(ReadView const& v, Strand const& strand) { @@ -357,10 +361,11 @@ qualityUpperBound(ReadView const& v, Strand const& strand) } return q; }; -/// @endcond +/** @endcond */ -/// @cond INTERNAL -/** Limit remaining out only if one strand and limitQuality is included. +/** @cond INTERNAL */ +/** + * Limit remaining out only if one strand and limitQuality is included. * Targets one path payment with AMM where the average quality is linear * and instant quality is quadratic function of output. Calculating quality * function for the whole strand enables figuring out required output @@ -428,9 +433,9 @@ limitOut( return remainingOut; return std::min(out, remainingOut); }; -/// @endcond +/** @endcond */ -/// @cond INTERNAL +/** @cond INTERNAL */ /* Track the non-dry strands flow will search the non-dry strands (stored in `cur_`) for the best @@ -545,28 +550,28 @@ public: return cur_.size(); } }; -/// @endcond +/** @endcond */ /** - Request `out` amount from a collection of strands - - Attempt to fulfill the payment by using liquidity from the strands in order - from least expensive to most expensive - - @param baseView Trust lines and balances - @param strands Each strand contains the steps of accounts to ripple through - and offer books to use - @param outReq Amount of output requested from the strand - @param partialPayment If true allow less than the full payment - @param offerCrossing If true offer crossing, not handling a standard payment - @param limitQuality If present, the minimum quality for any strand taken - @param sendMaxST If present, the maximum STAmount to send - @param j Journal to write journal messages to - @param ammContext counts iterations with AMM offers - @param flowDebugInfo If pointer is non-null, write flow debug info here - @return Actual amount in and out from the strands, errors, and payment - sandbox -*/ + * Request `out` amount from a collection of strands + * + * Attempt to fulfill the payment by using liquidity from the strands in order + * from least expensive to most expensive + * + * @param baseView Trust lines and balances + * @param strands Each strand contains the steps of accounts to ripple through + * and offer books to use + * @param outReq Amount of output requested from the strand + * @param partialPayment If true allow less than the full payment + * @param offerCrossing If true offer crossing, not handling a standard payment + * @param limitQuality If present, the minimum quality for any strand taken + * @param sendMaxST If present, the maximum STAmount to send + * @param j Journal to write journal messages to + * @param ammContext counts iterations with AMM offers + * @param flowDebugInfo If pointer is non-null, write flow debug info here + * @return Actual amount in and out from the strands, errors, and payment + * sandbox + */ template FlowResult flow( diff --git a/include/xrpl/tx/transactors/account/SignerListSet.h b/include/xrpl/tx/transactors/account/SignerListSet.h index da2274a1de..9f7872a18e 100644 --- a/include/xrpl/tx/transactors/account/SignerListSet.h +++ b/include/xrpl/tx/transactors/account/SignerListSet.h @@ -20,9 +20,9 @@ namespace xrpl { /** -See the README.md for an overview of the SignerListSet transaction that -this class implements. -*/ + * See the README.md for an overview of the SignerListSet transaction that + * this class implements. + */ class SignerListSet : public Transactor { private: diff --git a/include/xrpl/tx/transactors/dex/AMMBid.h b/include/xrpl/tx/transactors/dex/AMMBid.h index fa257696e8..9d8eb8578b 100644 --- a/include/xrpl/tx/transactors/dex/AMMBid.h +++ b/include/xrpl/tx/transactors/dex/AMMBid.h @@ -11,7 +11,8 @@ namespace xrpl { -/** AMMBid implements AMM bid Transactor. +/** + * AMMBid implements AMM bid Transactor. * This is a mechanism for an AMM instance to auction-off * the trading advantages to users (arbitrageurs) at a discounted * TradingFee for a 24 hour slot. Any account that owns corresponding diff --git a/include/xrpl/tx/transactors/dex/AMMClawback.h b/include/xrpl/tx/transactors/dex/AMMClawback.h index 54eb3c9f27..3f3f2c421a 100644 --- a/include/xrpl/tx/transactors/dex/AMMClawback.h +++ b/include/xrpl/tx/transactors/dex/AMMClawback.h @@ -56,7 +56,8 @@ private: TER applyGuts(Sandbox& view); - /** Withdraw both assets by providing maximum amount of asset1, + /** + * Withdraw both assets by providing maximum amount of asset1, * asset2's amount will be calculated according to the current proportion. * Since it is two-asset withdrawal, tfee is omitted. * @param view diff --git a/include/xrpl/tx/transactors/dex/AMMContext.h b/include/xrpl/tx/transactors/dex/AMMContext.h index 65954044be..b878e4f10b 100644 --- a/include/xrpl/tx/transactors/dex/AMMContext.h +++ b/include/xrpl/tx/transactors/dex/AMMContext.h @@ -6,7 +6,8 @@ namespace xrpl { -/** Maintains AMM info per overall payment engine execution and +/** + * Maintains AMM info per overall payment engine execution and * individual iteration. * Only one instance of this class is created in Flow.cpp::flow(). * The reference is percolated through calls to AMMLiquidity class, @@ -84,7 +85,8 @@ public: return accountID_; } - /** Strand execution may fail. Reset the flag at the start + /** + * Strand execution may fail. Reset the flag at the start * of each payment engine iteration. */ void diff --git a/include/xrpl/tx/transactors/dex/AMMCreate.h b/include/xrpl/tx/transactors/dex/AMMCreate.h index 188af8d4ef..5260cf31cd 100644 --- a/include/xrpl/tx/transactors/dex/AMMCreate.h +++ b/include/xrpl/tx/transactors/dex/AMMCreate.h @@ -11,34 +11,35 @@ namespace xrpl { -/** AMMCreate implements Automatic Market Maker(AMM) creation Transactor. - * It creates a new AMM instance with two tokens. Any trader, or Liquidity - * Provider (LP), can create the AMM instance and receive in return shares - * of the AMM pool in the form of LPTokens. The number of tokens that LP gets - * are determined by LPTokens = sqrt(A * B), where A and B is the current - * composition of the AMM pool. LP can add (AMMDeposit) or withdraw - * (AMMWithdraw) tokens from AMM and - * AMM can be used transparently in the payment or offer crossing transactions. - * Trading fee is charged to the traders for the trades executed against - * AMM instance. The fee is added to the AMM pool and distributed to the LPs - * in proportion to the LPTokens upon liquidity removal. The fee can be voted - * on by LP's (AMMVote). LP's can continuously bid (AMMBid) for the 24 hour - * auction slot, which enables LP's to trade at zero trading fee. - * AMM instance creates AccountRoot object with disabled master key - * for book-keeping of XRP balance if one of the tokens - * is XRP, a trustline for each IOU token, a trustline to keep track - * of LPTokens, and ltAMM ledger object. AccountRoot ID is generated - * internally from the parent's hash. ltAMM's object ID is +/** + * AMMCreate implements Automatic Market Maker(AMM) creation Transactor. + * It creates a new AMM instance with two tokens. Any trader, or Liquidity + * Provider (LP), can create the AMM instance and receive in return shares + * of the AMM pool in the form of LPTokens. The number of tokens that LP gets + * are determined by LPTokens = sqrt(A * B), where A and B is the current + * composition of the AMM pool. LP can add (AMMDeposit) or withdraw + * (AMMWithdraw) tokens from AMM and + * AMM can be used transparently in the payment or offer crossing transactions. + * Trading fee is charged to the traders for the trades executed against + * AMM instance. The fee is added to the AMM pool and distributed to the LPs + * in proportion to the LPTokens upon liquidity removal. The fee can be voted + * on by LP's (AMMVote). LP's can continuously bid (AMMBid) for the 24 hour + * auction slot, which enables LP's to trade at zero trading fee. + * AMM instance creates AccountRoot object with disabled master key + * for book-keeping of XRP balance if one of the tokens + * is XRP, a trustline for each IOU token, a trustline to keep track + * of LPTokens, and ltAMM ledger object. AccountRoot ID is generated + * internally from the parent's hash. ltAMM's object ID is * hash{token1.currency, token1.issuer, token2.currency, token2.issuer}, where * issue1 < issue2. ltAMM object provides mapping from the hash to AccountRoot * ID and contains: AMMAccount - AMM AccountRoot ID. TradingFee - AMM voted * TradingFee. VoteSlots - Array of VoteEntry, contains fee vote information. - * AuctionSlot - Auction slot, contains discounted fee bid information. - * LPTokenBalance - LPTokens outstanding balance. - * AMMToken - currency/issuer information for AMM tokens. - * AMMDeposit, AMMWithdraw, AMMVote, and AMMBid transactions use the hash - * to access AMM instance. - * @see [XLS30d:Creating AMM instance on + * AuctionSlot - Auction slot, contains discounted fee bid information. + * LPTokenBalance - LPTokens outstanding balance. + * AMMToken - currency/issuer information for AMM tokens. + * AMMDeposit, AMMWithdraw, AMMVote, and AMMBid transactions use the hash + * to access AMM instance. + * @see [XLS30d:Creating AMM instance on * XRPL](https://github.com/XRPLF/XRPL-Standards/discussions/78) */ class AMMCreate : public Transactor @@ -62,7 +63,9 @@ public: static TER preclaim(PreclaimContext const& ctx); - /** Attempt to create the AMM instance. */ + /** + * Attempt to create the AMM instance. + */ TER doApply() override; diff --git a/include/xrpl/tx/transactors/dex/AMMDelete.h b/include/xrpl/tx/transactors/dex/AMMDelete.h index 4a0905fe10..abf800c4d1 100644 --- a/include/xrpl/tx/transactors/dex/AMMDelete.h +++ b/include/xrpl/tx/transactors/dex/AMMDelete.h @@ -11,7 +11,8 @@ namespace xrpl { -/** AMMDelete implements AMM delete transactor. This is a mechanism to +/** + * AMMDelete implements AMM delete transactor. This is a mechanism to * delete AMM in an empty state when the number of LP tokens is 0. * AMMDelete deletes the trustlines up to configured maximum. If all * trustlines are deleted then AMM ltAMM and root account are deleted. diff --git a/include/xrpl/tx/transactors/dex/AMMDeposit.h b/include/xrpl/tx/transactors/dex/AMMDeposit.h index b87db19b2d..2959f1b10a 100644 --- a/include/xrpl/tx/transactors/dex/AMMDeposit.h +++ b/include/xrpl/tx/transactors/dex/AMMDeposit.h @@ -20,7 +20,8 @@ namespace xrpl { class Sandbox; -/** AMMDeposit implements AMM deposit Transactor. +/** + * AMMDeposit implements AMM deposit Transactor. * The deposit transaction is used to add liquidity to the AMM instance pool, * thus obtaining some share of the instance's pools in the form of LPTokens. * If the trader deposits proportional values of both assets without changing @@ -92,7 +93,8 @@ private: std::pair applyGuts(Sandbox& view); - /** Deposit requested assets and token amount into LP account. + /** + * Deposit requested assets and token amount into LP account. * Return new total LPToken balance. * @param view * @param ammAccount @@ -121,7 +123,8 @@ private: std::optional const& lpTokensDepositMin, std::uint16_t tfee); - /** Equal asset deposit (LPTokens) for the specified share of + /** + * Equal asset deposit (LPTokens) for the specified share of * the AMM instance pools. The trading fee is not charged. * @param view * @param ammAccount @@ -146,7 +149,8 @@ private: std::optional const& deposit2Min, std::uint16_t tfee); - /** Equal asset deposit (Asset1In, Asset2In) with the constraint on + /** + * Equal asset deposit (Asset1In, Asset2In) with the constraint on * the maximum amount of both assets that the trader is willing to deposit. * The trading fee is not charged. * @param view @@ -172,7 +176,8 @@ private: std::optional const& lpTokensDepositMin, std::uint16_t tfee); - /** Single asset deposit (Asset1In) by the amount. + /** + * Single asset deposit (Asset1In) by the amount. * The trading fee is charged. * @param view * @param ammAccount @@ -193,7 +198,8 @@ private: std::optional const& lpTokensDepositMin, std::uint16_t tfee); - /** Single asset deposit (Asset1In, LPTokens) by the tokens. + /** + * Single asset deposit (Asset1In, LPTokens) by the tokens. * The trading fee is charged. * @param view * @param ammAccount @@ -214,7 +220,8 @@ private: STAmount const& lpTokensDeposit, std::uint16_t tfee); - /** Single asset deposit (Asset1In, EPrice) with two constraints. + /** + * Single asset deposit (Asset1In, EPrice) with two constraints. * The trading fee is charged. * @param view * @param ammAccount @@ -235,7 +242,8 @@ private: STAmount const& ePrice, std::uint16_t tfee); - /** Equal deposit in empty AMM state (LP tokens balance is 0) + /** + * Equal deposit in empty AMM state (LP tokens balance is 0) * @param view * @param ammAccount * @param amount requested asset1 deposit amount diff --git a/include/xrpl/tx/transactors/dex/AMMVote.h b/include/xrpl/tx/transactors/dex/AMMVote.h index 10ad284bb3..4d63a98534 100644 --- a/include/xrpl/tx/transactors/dex/AMMVote.h +++ b/include/xrpl/tx/transactors/dex/AMMVote.h @@ -11,7 +11,8 @@ namespace xrpl { -/** AMMVote implements AMM vote Transactor. +/** + * AMMVote implements AMM vote Transactor. * This transactor allows for the TradingFee of the AMM instance be a votable * parameter. Any account (LP) that holds the corresponding LPTokens can cast * a vote using the new AMMVote transaction. VoteSlots array in ltAMM object diff --git a/include/xrpl/tx/transactors/dex/AMMWithdraw.h b/include/xrpl/tx/transactors/dex/AMMWithdraw.h index 8f6700037d..7004dd57c1 100644 --- a/include/xrpl/tx/transactors/dex/AMMWithdraw.h +++ b/include/xrpl/tx/transactors/dex/AMMWithdraw.h @@ -22,7 +22,8 @@ namespace xrpl { class Sandbox; -/** AMMWithdraw implements AMM withdraw Transactor. +/** + * AMMWithdraw implements AMM withdraw Transactor. * The withdraw transaction is used to remove liquidity from the AMM instance * pool, thus redeeming some share of the pools that one owns in the form * of LPTokens. If the trader withdraws proportional values of both assets @@ -96,7 +97,8 @@ public: ReadView const& view, beast::Journal const& j) override; - /** Equal-asset withdrawal (LPTokens) of some AMM instance pools + /** + * Equal-asset withdrawal (LPTokens) of some AMM instance pools * shares represented by the number of LPTokens . * The trading fee is not charged. * @param view @@ -129,7 +131,8 @@ public: XRPAmount const& priorBalance, beast::Journal const& journal); - /** Withdraw requested assets and token from AMM into LP account. + /** + * Withdraw requested assets and token from AMM into LP account. * Return new total LPToken balance and the withdrawn amounts for both * assets. * @param view @@ -173,15 +176,18 @@ public: beast::Journal const& journal); private: - /** Returns IgnoreFreeze when the withdrawer is the issuer of a pool - * asset (post-fixCleanup3_3_0), ZeroIfFrozen otherwise. */ + /** + * Returns IgnoreFreeze when the withdrawer is the issuer of a pool + * asset (post-fixCleanup3_3_0), ZeroIfFrozen otherwise. + */ [[nodiscard]] FreezeHandling issuerFreezeHandling() const; std::pair applyGuts(Sandbox& view); - /** Withdraw requested assets and token from AMM into LP account. + /** + * Withdraw requested assets and token from AMM into LP account. * Return new total LPToken balance. * @param view * @param ammSle AMM ledger entry @@ -205,7 +211,8 @@ private: STAmount const& lpTokensWithdraw, std::uint16_t tfee); - /** Equal-asset withdrawal (LPTokens) of some AMM instance pools + /** + * Equal-asset withdrawal (LPTokens) of some AMM instance pools * shares represented by the number of LPTokens . * The trading fee is not charged. * @param view @@ -230,7 +237,8 @@ private: STAmount const& lpTokensWithdraw, std::uint16_t tfee); - /** Withdraw both assets (Asset1Out, Asset2Out) with the constraints + /** + * Withdraw both assets (Asset1Out, Asset2Out) with the constraints * on the maximum amount of each asset that the trader is willing * to withdraw. The trading fee is not charged. * @param view @@ -255,7 +263,8 @@ private: STAmount const& amount2, std::uint16_t tfee); - /** Single asset withdrawal (Asset1Out) equivalent to the amount specified + /** + * Single asset withdrawal (Asset1Out) equivalent to the amount specified * in Asset1Out. The trading fee is charged. * @param view * @param ammAccount @@ -275,7 +284,8 @@ private: STAmount const& amount, std::uint16_t tfee); - /** Single asset withdrawal (Asset1Out, LPTokens) proportional + /** + * Single asset withdrawal (Asset1Out, LPTokens) proportional * to the share specified by tokens. The trading fee is charged. * @param view * @param ammAccount @@ -297,7 +307,8 @@ private: STAmount const& lpTokensWithdraw, std::uint16_t tfee); - /** Withdraw single asset (Asset1Out, EPrice) with two constraints. + /** + * Withdraw single asset (Asset1Out, EPrice) with two constraints. * The trading fee is charged. * @param view * @param ammAccount @@ -319,7 +330,9 @@ private: STAmount const& ePrice, std::uint16_t tfee); - /** Check from the flags if it's withdraw all */ + /** + * Check from the flags if it's withdraw all + */ static WithdrawAll isWithdrawAll(STTx const& tx); }; diff --git a/include/xrpl/tx/transactors/dex/OfferCreate.h b/include/xrpl/tx/transactors/dex/OfferCreate.h index a3bf7626f9..0ce34646dd 100644 --- a/include/xrpl/tx/transactors/dex/OfferCreate.h +++ b/include/xrpl/tx/transactors/dex/OfferCreate.h @@ -28,13 +28,17 @@ namespace xrpl { class PaymentSandbox; class Sandbox; -/** Transactor specialized for creating offers in the ledger. */ +/** + * Transactor specialized for creating offers in the ledger. + */ class OfferCreate : public Transactor { public: static constexpr auto kConsequencesFactory = ConsequencesFactoryType::Custom; - /** Construct a Transactor subclass that creates an offer in the ledger. */ + /** + * Construct a Transactor subclass that creates an offer in the ledger. + */ explicit OfferCreate(ApplyContext& ctx) : Transactor(ctx) { } @@ -48,15 +52,21 @@ public: static std::uint32_t getFlagsMask(PreflightContext const& ctx); - /** Enforce constraints beyond those of the Transactor base class. */ + /** + * Enforce constraints beyond those of the Transactor base class. + */ static NotTEC preflight(PreflightContext const& ctx); - /** Enforce constraints beyond those of the Transactor base class. */ + /** + * Enforce constraints beyond those of the Transactor base class. + */ static TER preclaim(PreclaimContext const& ctx); - /** Precondition: fee collection is likely. Attempt to create the offer. */ + /** + * Precondition: fee collection is likely. Attempt to create the offer. + */ TER doApply() override; diff --git a/include/xrpl/tx/transactors/lending/LoanManage.h b/include/xrpl/tx/transactors/lending/LoanManage.h index c8a5584131..e641e03dd1 100644 --- a/include/xrpl/tx/transactors/lending/LoanManage.h +++ b/include/xrpl/tx/transactors/lending/LoanManage.h @@ -36,7 +36,8 @@ public: static TER preclaim(PreclaimContext const& ctx); - /** Helper function that might be needed by other transactors + /** + * Helper function that might be needed by other transactors */ static TER defaultLoan( @@ -47,7 +48,8 @@ public: Asset const& vaultAsset, beast::Journal j); - /** Helper function that might be needed by other transactors + /** + * Helper function that might be needed by other transactors */ static TER impairLoan( @@ -57,7 +59,8 @@ public: Asset const& vaultAsset, beast::Journal j); - /** Helper function that might be needed by other transactors + /** + * Helper function that might be needed by other transactors */ [[nodiscard]] static TER unimpairLoan( diff --git a/include/xrpl/tx/transactors/oracle/OracleDelete.h b/include/xrpl/tx/transactors/oracle/OracleDelete.h index f9e9230527..d44065d7bb 100644 --- a/include/xrpl/tx/transactors/oracle/OracleDelete.h +++ b/include/xrpl/tx/transactors/oracle/OracleDelete.h @@ -14,13 +14,13 @@ namespace xrpl { /** - Price Oracle is a system that acts as a bridge between - a blockchain network and the external world, providing off-chain price data - to decentralized applications (dApps) on the blockchain. This implementation - conforms to the requirements specified in the XLS-47d. - - The OracleDelete transactor implements the deletion of Oracle objects. -*/ + * Price Oracle is a system that acts as a bridge between + * a blockchain network and the external world, providing off-chain price data + * to decentralized applications (dApps) on the blockchain. This implementation + * conforms to the requirements specified in the XLS-47d. + * + * The OracleDelete transactor implements the deletion of Oracle objects. + */ class OracleDelete : public Transactor { diff --git a/include/xrpl/tx/transactors/oracle/OracleSet.h b/include/xrpl/tx/transactors/oracle/OracleSet.h index e95970923b..b6aed3f4fc 100644 --- a/include/xrpl/tx/transactors/oracle/OracleSet.h +++ b/include/xrpl/tx/transactors/oracle/OracleSet.h @@ -12,13 +12,13 @@ namespace xrpl { /** - Price Oracle is a system that acts as a bridge between - a blockchain network and the external world, providing off-chain price data - to decentralized applications (dApps) on the blockchain. This implementation - conforms to the requirements specified in the XLS-47d. - - The OracleSet transactor implements creating or updating Oracle objects. -*/ + * Price Oracle is a system that acts as a bridge between + * a blockchain network and the external world, providing off-chain price data + * to decentralized applications (dApps) on the blockchain. This implementation + * conforms to the requirements specified in the XLS-47d. + * + * The OracleSet transactor implements creating or updating Oracle objects. + */ class OracleSet : public Transactor { diff --git a/include/xrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.h b/include/xrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.h index 5a07262a3b..6bd68a0142 100644 --- a/include/xrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.h +++ b/include/xrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.h @@ -26,7 +26,9 @@ public: static TER preclaim(PreclaimContext const& ctx); - /** Attempt to delete the Permissioned Domain. */ + /** + * Attempt to delete the Permissioned Domain. + */ TER doApply() override; diff --git a/include/xrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.h b/include/xrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.h index 38de800284..cb5d341c50 100644 --- a/include/xrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.h +++ b/include/xrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.h @@ -29,7 +29,9 @@ public: static TER preclaim(PreclaimContext const& ctx); - /** Attempt to create the Permissioned Domain. */ + /** + * Attempt to create the Permissioned Domain. + */ TER doApply() override; diff --git a/include/xrpl/tx/transactors/system/TicketCreate.h b/include/xrpl/tx/transactors/system/TicketCreate.h index 2a1036c732..f249fd7db2 100644 --- a/include/xrpl/tx/transactors/system/TicketCreate.h +++ b/include/xrpl/tx/transactors/system/TicketCreate.h @@ -57,15 +57,21 @@ public: static TxConsequences makeTxConsequences(PreflightContext const& ctx); - /** Enforce constraints beyond those of the Transactor base class. */ + /** + * Enforce constraints beyond those of the Transactor base class. + */ static NotTEC preflight(PreflightContext const& ctx); - /** Enforce constraints beyond those of the Transactor base class. */ + /** + * Enforce constraints beyond those of the Transactor base class. + */ static TER preclaim(PreclaimContext const& ctx); - /** Precondition: fee collection is likely. Attempt to create ticket(s). */ + /** + * Precondition: fee collection is likely. Attempt to create ticket(s). + */ TER doApply() override; diff --git a/src/libxrpl/basics/Number.cpp b/src/libxrpl/basics/Number.cpp index 0a1d558421..d08fd23016 100644 --- a/src/libxrpl/basics/Number.cpp +++ b/src/libxrpl/basics/Number.cpp @@ -186,35 +186,36 @@ divu10(uint128_t& u) template concept UnsignedMantissa = std::is_unsigned_v || std::is_same_v; -/** Guard - - The Guard class is used to temporarily add extra digits of - precision to an operation. This enables the final result - to be correctly rounded to the internal precision of Number. - - At its core, the Guard really only needs three pieces of information to determine how to round: - 1. The rounding mode - 2. The last digit dropped from the mantissa (i.e. the first digit after the decimal point). - (first byte of digits_) - 3. Whether any other non-zero digits were dropped from the mantissa. (remaining bytes of digits_ - and xbit_) - - Upward and Downward rounding modes round the unsigned mantissa toward or away from zero - depending on whether the sign is negative (sbit_). For positive values, Upward is away, and - Downward is toward. For negative values, that's reversed. For simplicity, I'm going to describe - the logic using "TowardZero" and "AwayFromZero". - - * TowardZero is the easiest rounding mode. It always rounds down. digits_ and xbit_ are - irrelevant. - * AwayFromZero is almost as simple. If both "digits_" and "xbit_" are zero (0), it rounds down. - Else it rounds up. - * ToNearest is only a little more complicated. If the last dropped digit is < 5, then round - down. If it is > 5, round up. If it is exactly 5, and there are _any_ other digits (the - remainder of "digits_" or "xbit_"), round up, else round to even. - - The current implementation stores 16 digits in "digits_" so that digits can be "pop"ped back - out if needed during subtraction (negative addition) operations. -*/ +/** + * Guard + * + * The Guard class is used to temporarily add extra digits of + * precision to an operation. This enables the final result + * to be correctly rounded to the internal precision of Number. + * + * At its core, the Guard really only needs three pieces of information to determine how to round: + * 1. The rounding mode + * 2. The last digit dropped from the mantissa (i.e. the first digit after the decimal point). + * (first byte of digits_) + * 3. Whether any other non-zero digits were dropped from the mantissa. (remaining bytes of digits_ + * and xbit_) + * + * Upward and Downward rounding modes round the unsigned mantissa toward or away from zero + * depending on whether the sign is negative (sbit_). For positive values, Upward is away, and + * Downward is toward. For negative values, that's reversed. For simplicity, I'm going to describe + * the logic using "TowardZero" and "AwayFromZero". + * + * TowardZero is the easiest rounding mode. It always rounds down. digits_ and xbit_ are + * irrelevant. + * AwayFromZero is almost as simple. If both "digits_" and "xbit_" are zero (0), it rounds down. + * Else it rounds up. + * ToNearest is only a little more complicated. If the last dropped digit is < 5, then round + * down. If it is > 5, round up. If it is exactly 5, and there are _any_ other digits (the + * remainder of "digits_" or "xbit_"), round up, else round to even. + * + * The current implementation stores 16 digits in "digits_" so that digits can be "pop"ped back + * out if needed during subtraction (negative addition) operations. + */ class Number::Guard { std::uint64_t digits_{0}; // 16 decimal guard digits @@ -263,13 +264,14 @@ public: [[nodiscard]] bool empty() const noexcept; - /** Drop a digit from the mantissa, and increment the exponent, storing the dropped digit in + /** + * Drop a digit from the mantissa, and increment the exponent, storing the dropped digit in * this Guard. * * Substitute for: - push(mantissa % 10); - mantissa /= 10; - ++exponent; + * push(mantissa % 10); + * mantissa /= 10; + * ++exponent; */ template void diff --git a/src/libxrpl/basics/ResolverAsio.cpp b/src/libxrpl/basics/ResolverAsio.cpp index 7e1b56f87a..25e95b7fc5 100644 --- a/src/libxrpl/basics/ResolverAsio.cpp +++ b/src/libxrpl/basics/ResolverAsio.cpp @@ -33,10 +33,11 @@ namespace xrpl { -/** Mix-in to track when all pending I/O is complete. - Derived classes must be callable with this signature: - void asyncHandlersComplete() -*/ +/** + * Mix-in to track when all pending I/O is complete. + * Derived classes must be callable with this signature: + * void asyncHandlersComplete() + */ template class AsyncObject { @@ -51,10 +52,11 @@ public: XRPL_ASSERT(pending_.load() == 0, "xrpl::AsyncObject::~AsyncObject : nothing pending"); } - /** RAII container that maintains the count of pending I/O. - Bind this into the argument list of every handler passed - to an initiating function. - */ + /** + * RAII container that maintains the count of pending I/O. + * Bind this into the argument list of every handler passed + * to an initiating function. + */ class CompletionCounter { public: diff --git a/src/libxrpl/basics/base64.cpp b/src/libxrpl/basics/base64.cpp index 541ddd0839..c980a08669 100644 --- a/src/libxrpl/basics/base64.cpp +++ b/src/libxrpl/basics/base64.cpp @@ -76,32 +76,37 @@ getInverse() return &kTab[0]; } -/// Returns max chars needed to encode a base64 string +/** + * Returns max chars needed to encode a base64 string + */ constexpr std::size_t encodedSize(std::size_t n) { return 4 * ((n + 2) / 3); } -/// Returns max bytes needed to decode a base64 string +/** + * Returns max bytes needed to decode a base64 string + */ constexpr std::size_t decodedSize(std::size_t n) { return ((n / 4) * 3) + 2; } -/** Encode a series of octets as a padded, base64 string. - - The resulting string will not be null terminated. - - @par Requires - - The memory pointed to by `out` points to valid memory - of at least `encoded_size(len)` bytes. - - @return The number of characters written to `out`. This - will exclude any null termination. -*/ +/** + * Encode a series of octets as a padded, base64 string. + * + * The resulting string will not be null terminated. + * + * @par Requires + * + * The memory pointed to by `out` points to valid memory + * of at least `encoded_size(len)` bytes. + * + * @return The number of characters written to `out`. This + * will exclude any null termination. + */ std::size_t encode(void* dest, void const* src, std::size_t len) { @@ -142,17 +147,18 @@ encode(void* dest, void const* src, std::size_t len) return out - static_cast(dest); } -/** Decode a padded base64 string into a series of octets. - - @par Requires - - The memory pointed to by `out` points to valid memory - of at least `decoded_size(len)` bytes. - - @return The number of octets written to `out`, and - the number of characters read from the input string, - expressed as a pair. -*/ +/** + * Decode a padded base64 string into a series of octets. + * + * @par Requires + * + * The memory pointed to by `out` points to valid memory + * of at least `decoded_size(len)` bytes. + * + * @return The number of octets written to `out`, and + * the number of characters read from the input string, + * expressed as a pair. + */ std::pair decode(void* dest, char const* src, std::size_t len) { diff --git a/src/libxrpl/basics/make_SSLContext.cpp b/src/libxrpl/basics/make_SSLContext.cpp index 165d36076b..3fca0c0d77 100644 --- a/src/libxrpl/basics/make_SSLContext.cpp +++ b/src/libxrpl/basics/make_SSLContext.cpp @@ -30,35 +30,37 @@ namespace xrpl { namespace openssl::detail { -/** The default strength of self-signed RSA certificates. - - Per NIST Special Publication 800-57 Part 3, 2048-bit RSA is still - considered acceptably secure. Generally, we would want to go above - and beyond such recommendations (e.g. by using 3072 or 4096 bits) - but there is a computational cost associated with that may not - be worth paying, considering that: - - - We regenerate a new ephemeral certificate and a securely generated - random private key every time the server is started; and - - There should not be any truly secure information (e.g. seeds or private - keys) that gets relayed to the server anyways over these RPCs. - - @note If you increase the number of bits you need to generate new - default DH parameters and update defaultDH accordingly. - * */ +/** + * The default strength of self-signed RSA certificates. + * + * Per NIST Special Publication 800-57 Part 3, 2048-bit RSA is still + * considered acceptably secure. Generally, we would want to go above + * and beyond such recommendations (e.g. by using 3072 or 4096 bits) + * but there is a computational cost associated with that may not + * be worth paying, considering that: + * + * - We regenerate a new ephemeral certificate and a securely generated + * random private key every time the server is started; and + * - There should not be any truly secure information (e.g. seeds or private + * keys) that gets relayed to the server anyways over these RPCs. + * + * @note If you increase the number of bits you need to generate new + * default DH parameters and update defaultDH accordingly. + */ int gDefaultRsaKeyBits = 2048; -/** The default DH parameters. - - These were generated using the OpenSSL command: `openssl dhparam 2048` - by Nik Bougalis on May, 29, 2022. - - It is safe to use this, but if you want you can generate different - parameters and put them here. There's no easy way to change this - via the config file at this time. - - @note If you increase the number of bits you need to update - defaultRSAKeyBits accordingly. +/** + * The default DH parameters. + * + * These were generated using the OpenSSL command: `openssl dhparam 2048` + * by Nik Bougalis on May, 29, 2022. + * + * It is safe to use this, but if you want you can generate different + * parameters and put them here. There's no easy way to change this + * via the config file at this time. + * + * @note If you increase the number of bits you need to update + * defaultRSAKeyBits accordingly. */ static constexpr char kDefaultDh[] = "-----BEGIN DH PARAMETERS-----\n" @@ -70,19 +72,20 @@ static constexpr char kDefaultDh[] = "9yqY3xXZID240RRcaJ25+U4lszFPqP+CEwIBAg==\n" "-----END DH PARAMETERS-----"; -/** The default list of ciphers we accept over TLS. - - Generally we include cipher suites that are part of TLS v1.2, but - we specifically exclude: - - - the DSS cipher suites (!DSS); - - cipher suites using pre-shared keys (!PSK); - - cipher suites that don't offer encryption (!eNULL); and - - cipher suites that don't offer authentication (!aNULL). - - @note Server administrators can override this default list, on either a - global or per-port basis, using the `ssl_ciphers` directive in the - config file. +/** + * The default list of ciphers we accept over TLS. + * + * Generally we include cipher suites that are part of TLS v1.2, but + * we specifically exclude: + * + * - the DSS cipher suites (!DSS); + * - cipher suites using pre-shared keys (!PSK); + * - cipher suites that don't offer encryption (!eNULL); and + * - cipher suites that don't offer authentication (!aNULL). + * + * @note Server administrators can override this default list, on either a + * global or per-port basis, using the `ssl_ciphers` directive in the + * config file. */ std::string const kDefaultCipherList = "TLSv1.2:!CBC:!DSS:!PSK:!eNULL:!aNULL"; diff --git a/src/libxrpl/core/detail/LoadMonitor.cpp b/src/libxrpl/core/detail/LoadMonitor.cpp index 95e0e7d3b9..92eff61285 100644 --- a/src/libxrpl/core/detail/LoadMonitor.cpp +++ b/src/libxrpl/core/detail/LoadMonitor.cpp @@ -101,10 +101,11 @@ LoadMonitor::addLoadSample(LoadEvent const& s) addSamples(1, latency); } -/* Add multiple samples - @param count The number of samples to add - @param latencyMS The total number of milliseconds -*/ +/** + * Add multiple samples + * @param count The number of samples to add + * @param latencyMS The total number of milliseconds + */ void LoadMonitor::addSamples(int count, std::chrono::milliseconds latency) { diff --git a/src/libxrpl/crypto/RFC1751.cpp b/src/libxrpl/crypto/RFC1751.cpp index 41e29ee00c..4b17e1443c 100644 --- a/src/libxrpl/crypto/RFC1751.cpp +++ b/src/libxrpl/crypto/RFC1751.cpp @@ -379,14 +379,15 @@ RFC1751::etob(std::string& strData, std::vector vsHuman) return 1; } -/** Convert words separated by spaces into a 128 bit key in big-endian format. - - @return - 1 if succeeded - 0 if word not in dictionary - -1 if badly formed string - -2 if words are okay but parity is wrong. -*/ +/** + * Convert words separated by spaces into a 128 bit key in big-endian format. + * + * @return + * 1 if succeeded + * 0 if word not in dictionary + * -1 if badly formed string + * -2 if words are okay but parity is wrong. + */ int RFC1751::getKeyFromEnglish(std::string& strKey, std::string const& strHuman) { @@ -415,7 +416,8 @@ RFC1751::getKeyFromEnglish(std::string& strKey, std::string const& strHuman) return rc; } -/** Convert to human from a 128 bit key in big-endian format +/** + * Convert to human from a 128 bit key in big-endian format */ void RFC1751::getEnglishFromKey(std::string& strHuman, std::string const& strKey) diff --git a/src/libxrpl/json/Writer.cpp b/src/libxrpl/json/Writer.cpp index b21013cda7..4c922a0e33 100644 --- a/src/libxrpl/json/Writer.cpp +++ b/src/libxrpl/json/Writer.cpp @@ -199,15 +199,21 @@ private: // JSON collections are either arrays, or objects. struct Collection { - /** What type of collection are we in? */ + /** + * What type of collection are we in? + */ Writer::CollectionType type = Writer::CollectionType::Array; - /** Is this the first entry in a collection? - * If false, we have to emit a , before we write the next entry. */ + /** + * Is this the first entry in a collection? + * If false, we have to emit a , before we write the next entry. + */ bool isFirst = true; #ifndef NDEBUG - /** What tags have we already seen in this collection? */ + /** + * What tags have we already seen in this collection? + */ std::set tags{}; // NOLINT(readability-redundant-member-init) #endif }; diff --git a/src/libxrpl/json/json_value.cpp b/src/libxrpl/json/json_value.cpp index ce208418de..cf95d873fb 100644 --- a/src/libxrpl/json/json_value.cpp +++ b/src/libxrpl/json/json_value.cpp @@ -174,7 +174,8 @@ Value::CZString::isStaticString() const // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// -/*! \internal Default constructor initialization must be equivalent to: +/** + * @internal Default constructor initialization must be equivalent to: * memset( this, 0, sizeof(Value) ) * This optimization is used in ValueInternalMap fast allocator. */ @@ -794,7 +795,9 @@ Value::isConvertibleTo(ValueType other) const return false; // unreachable; } -/// Number of values in array or object +/** + * Number of values in array or object + */ Value::UInt Value::size() const { diff --git a/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp b/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp index c67ab6775d..589e49d335 100644 --- a/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp +++ b/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp @@ -258,7 +258,9 @@ changeTokenURI( return tesSUCCESS; } -/** Insert the token in the owner's token directory. */ +/** + * Insert the token in the owner's token directory. + */ TER insertToken(ApplyView& view, AccountID owner, STObject&& nft) { @@ -347,7 +349,9 @@ mergePages(ApplyView& view, SLE::ref p1, SLE::ref p2) return true; } -/** Remove the token from the owner's token directory. */ +/** + * Remove the token from the owner's token directory. + */ TER removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID) { @@ -360,7 +364,9 @@ removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID) return removeToken(view, owner, nftokenID, page); } -/** Remove the token from the owner's token directory. */ +/** + * Remove the token from the owner's token directory. + */ TER removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID, SLE::ref curr) { diff --git a/src/libxrpl/nodestore/backend/NullFactory.cpp b/src/libxrpl/nodestore/backend/NullFactory.cpp index e36b13a2e1..0c76cb9938 100644 --- a/src/libxrpl/nodestore/backend/NullFactory.cpp +++ b/src/libxrpl/nodestore/backend/NullFactory.cpp @@ -81,7 +81,9 @@ public: { } - /** Returns the number of file descriptors the backend expects to need */ + /** + * Returns the number of file descriptors the backend expects to need + */ [[nodiscard]] int fdRequired() const override { diff --git a/src/libxrpl/nodestore/backend/RocksDBFactory.cpp b/src/libxrpl/nodestore/backend/RocksDBFactory.cpp index bcf4ba4a49..673b0daae0 100644 --- a/src/libxrpl/nodestore/backend/RocksDBFactory.cpp +++ b/src/libxrpl/nodestore/backend/RocksDBFactory.cpp @@ -419,7 +419,9 @@ public: storeBatch(batch); } - /** Returns the number of file descriptors the backend expects to need */ + /** + * Returns the number of file descriptors the backend expects to need + */ [[nodiscard]] int fdRequired() const override { diff --git a/src/libxrpl/protocol/AccountID.cpp b/src/libxrpl/protocol/AccountID.cpp index 6050144a8e..c6a5226566 100644 --- a/src/libxrpl/protocol/AccountID.cpp +++ b/src/libxrpl/protocol/AccountID.cpp @@ -21,7 +21,9 @@ namespace xrpl { namespace detail { -/** Caches the base58 representations of AccountIDs */ +/** + * Caches the base58 representations of AccountIDs + */ class AccountIdCache { private: diff --git a/src/libxrpl/protocol/Feature.cpp b/src/libxrpl/protocol/Feature.cpp index 059abe2996..2a8476dea8 100644 --- a/src/libxrpl/protocol/Feature.cpp +++ b/src/libxrpl/protocol/Feature.cpp @@ -154,7 +154,9 @@ public: uint256 registerFeature(std::string const& name, Supported support, VoteBehavior vote); - /** Tell FeatureCollections when registration is complete. */ + /** + * Tell FeatureCollections when registration is complete. + */ bool registrationIsDone(); @@ -167,30 +169,38 @@ public: std::string featureToName(uint256 const& f) const; - /** All amendments that are registered within the table. */ + /** + * All amendments that are registered within the table. + */ std::map const& allAmendments() const { return all_; } - /** Amendments that this server supports. - Whether they are enabled depends on the Rules defined in the validated - ledger */ + /** + * Amendments that this server supports. + * Whether they are enabled depends on the Rules defined in the validated + * ledger + */ std::map const& supportedAmendments() const { return supported_; } - /** Amendments that this server WON'T vote for by default. */ + /** + * Amendments that this server WON'T vote for by default. + */ std::size_t numDownVotedAmendments() const { return downVotes_; } - /** Amendments that this server WILL vote for by default. */ + /** + * Amendments that this server WILL vote for by default. + */ std::size_t numUpVotedAmendments() const { @@ -271,7 +281,9 @@ FeatureCollections::registerFeature(std::string const& name, Supported support, logicError("Duplicate feature registration"); } -/** Tell FeatureCollections when registration is complete. */ +/** + * Tell FeatureCollections when registration is complete. + */ bool FeatureCollections::registrationIsDone() { @@ -313,30 +325,38 @@ FeatureCollections gFeatureCollections; } // namespace -/** All amendments libxrpl knows of. */ +/** + * All amendments libxrpl knows of. + */ std::map const& allAmendments() { return gFeatureCollections.allAmendments(); } -/** Amendments that this server supports. - Whether they are enabled depends on the Rules defined in the validated - ledger */ +/** + * Amendments that this server supports. + * Whether they are enabled depends on the Rules defined in the validated + * ledger + */ std::map const& detail::supportedAmendments() { return gFeatureCollections.supportedAmendments(); } -/** Amendments that this server won't vote for by default. */ +/** + * Amendments that this server won't vote for by default. + */ std::size_t detail::numDownVotedAmendments() { return gFeatureCollections.numDownVotedAmendments(); } -/** Amendments that this server will vote for by default. */ +/** + * Amendments that this server will vote for by default. + */ std::size_t detail::numUpVotedAmendments() { @@ -365,7 +385,9 @@ retireFeature(std::string const& name) return registerFeature(name, Supported::Yes, VoteBehavior::Obsolete); } -/** Tell FeatureCollections when registration is complete. */ +/** + * Tell FeatureCollections when registration is complete. + */ bool registrationIsDone() { diff --git a/src/libxrpl/protocol/Indexes.cpp b/src/libxrpl/protocol/Indexes.cpp index ced18d4db6..02a4932526 100644 --- a/src/libxrpl/protocol/Indexes.cpp +++ b/src/libxrpl/protocol/Indexes.cpp @@ -32,23 +32,24 @@ namespace xrpl { -/** Type-specific prefix for calculating ledger indices. - - The identifier for a given object within the ledger is calculated based - on some object-specific parameters. To ensure that different types of - objects have different indices, even if they happen to use the same set - of parameters, we use "tagged hashing" by adding a type-specific prefix. - - @note These values are part of the protocol and *CANNOT* be arbitrarily - changed. If they were, on-ledger objects may no longer be able to - be located or addressed. - - Additions to this list are OK, but changing existing entries to - assign them a different values should never be needed. - - Entries that are removed should be moved to the bottom of the enum - and marked as [[deprecated]] to prevent accidental reuse. -*/ +/** + * Type-specific prefix for calculating ledger indices. + * + * The identifier for a given object within the ledger is calculated based + * on some object-specific parameters. To ensure that different types of + * objects have different indices, even if they happen to use the same set + * of parameters, we use "tagged hashing" by adding a type-specific prefix. + * + * @note These values are part of the protocol and *CANNOT* be arbitrarily + * changed. If they were, on-ledger objects may no longer be able to + * be located or addressed. + * + * Additions to this list are OK, but changing existing entries to + * assign them a different values should never be needed. + * + * Entries that are removed should be moved to the bottom of the enum + * and marked as [[deprecated]] to prevent accidental reuse. + */ enum class LedgerNameSpace : std::uint16_t { Account = 'a', DirNode = 'd', diff --git a/src/libxrpl/protocol/PublicKey.cpp b/src/libxrpl/protocol/PublicKey.cpp index 97948fcae3..cb6ea9e851 100644 --- a/src/libxrpl/protocol/PublicKey.cpp +++ b/src/libxrpl/protocol/PublicKey.cpp @@ -96,18 +96,19 @@ sliceToHex(Slice const& slice) return s; } -/** Determine whether a signature is canonical. - Canonical signatures are important to protect against signature morphing - attacks. - @param vSig the signature data - @param sigLen the length of the signature - @param strict_param whether to enforce strictly canonical semantics - - @note For more details please see: - https://xrpl.org/transaction-malleability.html - https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623 - https://github.com/sipa/bitcoin/commit/58bc86e37fda1aec270bccb3df6c20fbd2a6591c -*/ +/** + * Determine whether a signature is canonical. + * Canonical signatures are important to protect against signature morphing + * attacks. + * @param vSig the signature data + * @param sigLen the length of the signature + * @param strict_param whether to enforce strictly canonical semantics + * + * @note For more details please see: + * https://xrpl.org/transaction-malleability.html + * https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623 + * https://github.com/sipa/bitcoin/commit/58bc86e37fda1aec270bccb3df6c20fbd2a6591c + */ std::optional ecdsaCanonicality(Slice const& sig) { diff --git a/src/libxrpl/protocol/SecretKey.cpp b/src/libxrpl/protocol/SecretKey.cpp index f33b1871e1..7713911ab4 100644 --- a/src/libxrpl/protocol/SecretKey.cpp +++ b/src/libxrpl/protocol/SecretKey.cpp @@ -99,23 +99,24 @@ deriveDeterministicRootKey(Seed const& seed) } //------------------------------------------------------------------------------ -/** Produces a sequence of secp256k1 key pairs. - - The reference implementation of the XRP Ledger uses a custom derivation - algorithm which enables the derivation of an entire family of secp256k1 - keypairs from a single 128-bit seed. The algorithm predates widely-used - standards like BIP-32 and BIP-44. - - Important note to implementers: - - Using this algorithm is not required: all valid secp256k1 keypairs will - work correctly. Third party implementations can use whatever mechanisms - they prefer. However, implementers of wallets or other tools that allow - users to use existing accounts should consider at least supporting this - derivation technique to make it easier for users to 'import' accounts. - - For more details, please check out: - https://xrpl.org/cryptographic-keys.html#secp256k1-key-derivation +/** + * Produces a sequence of secp256k1 key pairs. + * + * The reference implementation of the XRP Ledger uses a custom derivation + * algorithm which enables the derivation of an entire family of secp256k1 + * keypairs from a single 128-bit seed. The algorithm predates widely-used + * standards like BIP-32 and BIP-44. + * + * Important note to implementers: + * + * Using this algorithm is not required: all valid secp256k1 keypairs will + * work correctly. Third party implementations can use whatever mechanisms + * they prefer. However, implementers of wallets or other tools that allow + * users to use existing accounts should consider at least supporting this + * derivation technique to make it easier for users to 'import' accounts. + * + * For more details, please check out: + * https://xrpl.org/cryptographic-keys.html#secp256k1-key-derivation */ class Generator { @@ -177,7 +178,9 @@ public: secureErase(generator_.data(), generator_.size()); } - /** Generate the nth key pair. */ + /** + * Generate the nth key pair. + */ std::pair operator()(std::size_t ordinal) const { diff --git a/src/libxrpl/protocol/tokens.cpp b/src/libxrpl/protocol/tokens.cpp index 21984c67e7..bd0f54c3ae 100644 --- a/src/libxrpl/protocol/tokens.cpp +++ b/src/libxrpl/protocol/tokens.cpp @@ -160,15 +160,16 @@ digest2(Args const&... args) return digest(digest(args...)); } -/** Calculate a 4-byte checksum of the data - - The checksum is calculated as the first 4 bytes - of the SHA256 digest of the message. This is added - to the base58 encoding of identifiers to detect - user error in data entry. - - @note This checksum algorithm is part of the client API -*/ +/** + * Calculate a 4-byte checksum of the data + * + * The checksum is calculated as the first 4 bytes + * of the SHA256 digest of the message. This is added + * to the base58 encoding of identifiers to detect + * user error in data entry. + * + * @note This checksum algorithm is part of the client API + */ static void checksum(void* out, void const* message, std::size_t size) { diff --git a/src/libxrpl/rdb/SociDB.cpp b/src/libxrpl/rdb/SociDB.cpp index 08c826b210..7354ef9b23 100644 --- a/src/libxrpl/rdb/SociDB.cpp +++ b/src/libxrpl/rdb/SociDB.cpp @@ -188,14 +188,15 @@ convert(std::string const& from, soci::blob& to) namespace { -/** Run a thread to checkpoint the write ahead log (wal) for - the given soci::session every 1000 pages. This is only implemented - for sqlite databases. - - Note: According to: https://www.sqlite.org/wal.html#ckpt this - is the default behavior of sqlite. We may be able to remove this - class. -*/ +/** + * Run a thread to checkpoint the write ahead log (wal) for + * the given soci::session every 1000 pages. This is only implemented + * for sqlite databases. + * + * Note: According to: https://www.sqlite.org/wal.html#ckpt this + * is the default behavior of sqlite. We may be able to remove this + * class. + */ class WALCheckpointer : public Checkpointer { diff --git a/src/libxrpl/shamap/SHAMap.cpp b/src/libxrpl/shamap/SHAMap.cpp index 0df0430a5f..2483e6f6e1 100644 --- a/src/libxrpl/shamap/SHAMap.cpp +++ b/src/libxrpl/shamap/SHAMap.cpp @@ -919,17 +919,18 @@ SHAMap::fetchRoot(SHAMapHash const& hash, SHAMapSyncFilter const* filter) return false; } -/** Replace a node with a shareable node. - - This code handles two cases: - - 1) An unshared, unshareable node needs to be made shareable - so immutable SHAMap's can have references to it. - 2) An unshareable node is shared. This happens when you make - a mutable snapshot of a mutable SHAMap. - - @note The node must have already been unshared by having the caller - first call SHAMapTreeNode::unshare(). +/** + * Replace a node with a shareable node. + * + * This code handles two cases: + * + * 1) An unshared, unshareable node needs to be made shareable + * so immutable SHAMap's can have references to it. + * 2) An unshareable node is shared. This happens when you make + * a mutable snapshot of a mutable SHAMap. + * + * @note The node must have already been unshared by having the caller + * first call SHAMapTreeNode::unshare(). */ SHAMapTreeNodePtr SHAMap::writeNode(NodeObjectType t, SHAMapTreeNodePtr node) const diff --git a/src/libxrpl/shamap/SHAMapSync.cpp b/src/libxrpl/shamap/SHAMapSync.cpp index 1f38049abe..cc30426f9d 100644 --- a/src/libxrpl/shamap/SHAMapSync.cpp +++ b/src/libxrpl/shamap/SHAMapSync.cpp @@ -300,10 +300,11 @@ SHAMap::gmnProcessDeferredReads(MissingNodes& mn) mn.deferred = 0; } -/** Get a list of node IDs and hashes for nodes that are part of this SHAMap - but not available locally. The filter can hold alternate sources of - nodes that are not permanently stored locally -*/ +/** + * Get a list of node IDs and hashes for nodes that are part of this SHAMap + * but not available locally. The filter can hold alternate sources of + * nodes that are not permanently stored locally + */ std::vector> SHAMap::getMissingNodes(int max, SHAMapSyncFilter const* filter) { @@ -720,7 +721,8 @@ SHAMap::deepCompare(SHAMap& other) const return true; } -/** Does this map have this inner node? +/** + * Does this map have this inner node? */ bool SHAMap::hasInnerNode(SHAMapNodeID const& targetNodeID, SHAMapHash const& targetNodeHash) const @@ -742,7 +744,8 @@ SHAMap::hasInnerNode(SHAMapNodeID const& targetNodeID, SHAMapHash const& targetN return (node->isInner()) && (node->getHash() == targetNodeHash); } -/** Does this map have this leaf node? +/** + * Does this map have this leaf node? */ bool SHAMap::hasLeafNode(uint256 const& tag, SHAMapHash const& targetNodeHash) const diff --git a/src/libxrpl/tx/Transactor.cpp b/src/libxrpl/tx/Transactor.cpp index 065dead1fd..4b562692d7 100644 --- a/src/libxrpl/tx/Transactor.cpp +++ b/src/libxrpl/tx/Transactor.cpp @@ -56,7 +56,9 @@ namespace xrpl { -/** Performs early sanity checks on the txid */ +/** + * Performs early sanity checks on the txid + */ NotTEC preflight0(PreflightContext const& ctx, std::uint32_t flagMask) { @@ -111,7 +113,8 @@ preflight0(PreflightContext const& ctx, std::uint32_t flagMask) namespace detail { -/** Checks the validity of the transactor signing key. +/** + * Checks the validity of the transactor signing key. * * Normally called from preflight1. */ @@ -221,7 +224,9 @@ preflight1Sponsor(PreflightContext const& ctx) return tesSUCCESS; } -/** Performs early sanity checks on the account and fee fields */ +/** + * Performs early sanity checks on the account and fee fields + */ NotTEC Transactor::preflight1(PreflightContext const& ctx, std::uint32_t flagMask) { @@ -290,7 +295,9 @@ Transactor::preflight1(PreflightContext const& ctx, std::uint32_t flagMask) return tesSUCCESS; } -/** Checks whether the signature appears valid */ +/** + * Checks whether the signature appears valid + */ NotTEC Transactor::preflight2(PreflightContext const& ctx) { @@ -1276,10 +1283,11 @@ removeDeletedTrustLines( } } -/** Reset the context, discarding any changes made and adjust the fee. - - @param fee The transaction fee to be charged. - @return A pair containing the transaction result and the actual fee charged. +/** + * Reset the context, discarding any changes made and adjust the fee. + * + * @param fee The transaction fee to be charged. + * @return A pair containing the transaction result and the actual fee charged. */ std::pair Transactor::reset(XRPAmount fee) diff --git a/src/libxrpl/tx/paths/BookStep.cpp b/src/libxrpl/tx/paths/BookStep.cpp index bf24c66ee1..71902ce8b9 100644 --- a/src/libxrpl/tx/paths/BookStep.cpp +++ b/src/libxrpl/tx/paths/BookStep.cpp @@ -66,13 +66,14 @@ protected: bool const ownerPaysTransferFee_; // Mark as inactive (dry) if too many offers are consumed bool inactive_ = false; - /** Number of offers consumed or partially consumed the last time - the step ran, including expired and unfunded offers. - - N.B. This is not the total number offers consumed by this step for the - entire payment, it is only the number the last time it ran. Offers may - be partially consumed multiple times during a payment. - */ + /** + * Number of offers consumed or partially consumed the last time + * the step ran, including expired and unfunded offers. + * + * N.B. This is not the total number offers consumed by this step for the + * entire payment, it is only the number the last time it ran. Offers may + * be partially consumed multiple times during a payment. + */ std::uint32_t offersUsed_ = 0; // If set, AMM liquidity might be available // if AMM offer quality is better than CLOB offer diff --git a/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp b/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp index 92a8dccdb9..e03cb56fd5 100644 --- a/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp +++ b/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp @@ -238,35 +238,35 @@ claimHelper( } /** - Handle a new attestation event. - - Attempt to add the given attestation and reconcile with the current - signer's list. Attestations that are not part of the current signer's - list will be removed. - - @param claimAtt New attestation to add. It will be added if it is not - already part of the collection, or attests to a larger value. - - @param quorum Min weight required for a quorum - - @param signersList Map from signer's account id (derived from public keys) - to the weight of that key. - - @return optional reward accounts. If after handling the new attestation - there is a quorum for the amount specified on the new attestation, then - return the reward accounts for that amount, otherwise return a nullopt. - Note that if the signer's list changes and there have been `commit` - transactions of different amounts then there may be a different subset that - has reached quorum. However, to "trigger" that subset would require adding - (or re-adding) an attestation that supports that subset. - - The reason for using a nullopt instead of an empty vector when a quorum is - not reached is to allow for an interface where a quorum is reached but no - rewards are distributed. - - @note This function is not called `add` because it does more than just - add the new attestation (in fact, it may not add the attestation at - all). Instead, it handles the event of a new attestation. + * Handle a new attestation event. + * + * Attempt to add the given attestation and reconcile with the current + * signer's list. Attestations that are not part of the current signer's + * list will be removed. + * + * @param claimAtt New attestation to add. It will be added if it is not + * already part of the collection, or attests to a larger value. + * + * @param quorum Min weight required for a quorum + * + * @param signersList Map from signer's account id (derived from public keys) + * to the weight of that key. + * + * @return optional reward accounts. If after handling the new attestation + * there is a quorum for the amount specified on the new attestation, then + * return the reward accounts for that amount, otherwise return a nullopt. + * Note that if the signer's list changes and there have been `commit` + * transactions of different amounts then there may be a different subset that + * has reached quorum. However, to "trigger" that subset would require adding + * (or re-adding) an attestation that supports that subset. + * + * The reason for using a nullopt instead of an empty vector when a quorum is + * not reached is to allow for an interface where a quorum is reached but no + * rewards are distributed. + * + * @note This function is not called `add` because it does more than just + * add the new attestation (in fact, it may not add the attestation at + * all). Instead, it handles the event of a new attestation. */ struct OnNewAttestationResult { @@ -364,26 +364,27 @@ struct TransferHelperSubmittingAccountInfo STAmount postFeeBalance; }; -/** Transfer funds from the src account to the dst account - - @param psb The payment sandbox. - @param src The source of funds. - @param dst The destination for funds. - @param dstTag Integer destination tag. Used to check if funds should be - transferred to an account with a `RequireDstTag` flag set. - @param claimOwner Owner of the claim ledger object. - @param amt Amount to transfer from the src account to the dst account. - @param canCreate Flag to determine if accounts may be created using this - transfer. - @param depositAuthPolicy Flag to determine if dst can bypass deposit auth if - it is also the claim owner. - @param submittingAccountInfo If the transaction is allowed to dip into the - reserve to pay fees, then this optional will be seated ("commit" - transactions support this, other transactions should not). - @param j Log - - @return tesSUCCESS if payment succeeds, otherwise the error code for the - failure reason. +/** + * Transfer funds from the src account to the dst account + * + * @param psb The payment sandbox. + * @param src The source of funds. + * @param dst The destination for funds. + * @param dstTag Integer destination tag. Used to check if funds should be + * transferred to an account with a `RequireDstTag` flag set. + * @param claimOwner Owner of the claim ledger object. + * @param amt Amount to transfer from the src account to the dst account. + * @param canCreate Flag to determine if accounts may be created using this + * transfer. + * @param depositAuthPolicy Flag to determine if dst can bypass deposit auth if + * it is also the claim owner. + * @param submittingAccountInfo If the transaction is allowed to dip into the + * reserve to pay fees, then this optional will be seated ("commit" + * transactions support this, other transactions should not). + * @param j Log + * + * @return tesSUCCESS if payment succeeds, otherwise the error code for the + * failure reason. */ TER @@ -504,21 +505,28 @@ transferHelper( return tecXCHAIN_PAYMENT_FAILED; } -/** Action to take when the transfer from the door account to the dst fails - - @note This is useful to prevent a failed "create account" transaction from - blocking subsequent "create account" transactions. -*/ +/** + * Action to take when the transfer from the door account to the dst fails + * + * @note This is useful to prevent a failed "create account" transaction from + * blocking subsequent "create account" transactions. + */ enum class OnTransferFail { - /** Remove the claim even if the transfer fails */ + /** + * Remove the claim even if the transfer fails + */ RemoveClaim, - /** Keep the claim if the transfer fails */ + /** + * Keep the claim if the transfer fails + */ KeepClaim }; struct FinalizeClaimHelperResult { - /// TER for transfering the payment funds + /** + * TER for transfering the payment funds + */ std::optional mainFundsTer; // TER for transfering the reward funds std::optional rewardTer; @@ -562,33 +570,34 @@ struct FinalizeClaimHelperResult } }; -/** Transfer funds from the door account to the dst and distribute rewards - - @param psb The payment sandbox. - @param bridgeSpc Bridge - @param dst The destination for funds. - @param dstTag Integer destination tag. Used to check if funds should be - transferred to an account with a `RequireDstTag` flag set. - @param claimOwner Owner of the claim ledger object. - @param sendingAmount Amount that was committed on the source chain. - @param rewardPoolSrc Source of the funds for the reward pool (claim owner). - @param rewardPool Amount to split among the rewardAccounts. - @param rewardAccounts Account to receive the reward pool. - @param srcChain Chain where the commit event occurred. - @param sleClaimID sle for the claim id (may be NULL or XChainClaimID or - XChainCreateAccountClaimID). Don't read fields that aren't in common - with those two types and always check for NULL. Remove on success (if - not null). Remove on fail if the onTransferFail flag is removeClaim. - @param onTransferFail Flag to determine if the claim is removed on transfer - failure. This is used for create account transactions where claims - are removed so they don't block future txns. - @param j Log - - @return FinalizeClaimHelperResult. See the comments in this struct for what - the fields mean. The individual ters need to be returned instead of - an overall ter because the caller needs this information if the - attestation list changed or not. -*/ +/** + * Transfer funds from the door account to the dst and distribute rewards + * + * @param psb The payment sandbox. + * @param bridgeSpc Bridge + * @param dst The destination for funds. + * @param dstTag Integer destination tag. Used to check if funds should be + * transferred to an account with a `RequireDstTag` flag set. + * @param claimOwner Owner of the claim ledger object. + * @param sendingAmount Amount that was committed on the source chain. + * @param rewardPoolSrc Source of the funds for the reward pool (claim owner). + * @param rewardPool Amount to split among the rewardAccounts. + * @param rewardAccounts Account to receive the reward pool. + * @param srcChain Chain where the commit event occurred. + * @param sleClaimID sle for the claim id (may be NULL or XChainClaimID or + * XChainCreateAccountClaimID). Don't read fields that aren't in common + * with those two types and always check for NULL. Remove on success (if + * not null). Remove on fail if the onTransferFail flag is removeClaim. + * @param onTransferFail Flag to determine if the claim is removed on transfer + * failure. This is used for create account transactions where claims + * are removed so they don't block future txns. + * @param j Log + * + * @return FinalizeClaimHelperResult. See the comments in this struct for what + * the fields mean. The individual ters need to be returned instead of + * an overall ter because the caller needs this information if the + * attestation list changed or not. + */ FinalizeClaimHelperResult finalizeClaimHelper( @@ -733,15 +742,16 @@ finalizeClaimHelper( return result; } -/** Get signers list corresponding to the account that owns the bridge - - @param view View to read the signer's list from. - @param sleBridge Sle of the bridge. - @param j Log - - @return map of the signer's list (AccountIDs and weights), the quorum, and - error code -*/ +/** + * Get signers list corresponding to the account that owns the bridge + * + * @param view View to read the signer's list from. + * @param sleBridge Sle of the bridge. + * @param j Log + * + * @return map of the signer's list (AccountIDs and weights), the quorum, and + * error code + */ std::tuple, std::uint32_t, TER> getSignersListAndQuorum(ReadView const& view, SLE const& sleBridge, beast::Journal j) { diff --git a/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp b/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp index 6a9600d060..0d1798babc 100644 --- a/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp @@ -680,7 +680,8 @@ adjustLPTokensOut( return adjustLPTokens(lptAMMBalance, lpTokensDeposit, IsDeposit::Yes); } -/** Proportional deposit of pools assets in exchange for the specified +/** + * Proportional deposit of pools assets in exchange for the specified * amount of LPTokens. */ std::pair @@ -728,7 +729,8 @@ AMMDeposit::equalDepositTokens( } } -/** Proportional deposit of pool assets with the constraints on the maximum +/** + * Proportional deposit of pool assets with the constraints on the maximum * amount of each asset that the trader is willing to deposit. * a = (t/T) * A (1) * b = (t/T) * B (2) @@ -829,7 +831,8 @@ AMMDeposit::equalDepositLimit( return {tecAMM_FAILED, STAmount{}}; } -/** Single asset deposit of the amount of asset specified by Asset1In. +/** + * Single asset deposit of the amount of asset specified by Asset1In. * t = T * (b / B - x) / (1 + x) (3) * where * f1 = (1 - 0.5 * tfee) / (1 - tfee) @@ -877,7 +880,8 @@ AMMDeposit::singleDeposit( tfee); } -/** Single asset asset1 is deposited to obtain some share of +/** + * Single asset asset1 is deposited to obtain some share of * the AMM instance's pools represented by amount of LPTokens. * Use equation 4 to compute the amount of asset1 to be deposited, * given t represented by amount of LPTokens. Equation 4 solves @@ -915,7 +919,8 @@ AMMDeposit::singleDepositTokens( tfee); } -/** Single asset deposit with two constraints. +/** + * Single asset deposit with two constraints. * a. Amount of asset1 if specified (not 0) in Asset1In specifies the maximum * amount of asset1 that the trader is willing to deposit. * b. The effective-price of the LPToken traded out does not exceed diff --git a/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp b/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp index ff27a58ad7..2baa7edfb4 100644 --- a/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp @@ -760,7 +760,8 @@ adjustLPTokensIn( return adjustLPTokens(lptAMMBalance, lpTokensWithdraw, IsDeposit::No); } -/** Proportional withdrawal of pool assets for the amount of LPTokens. +/** + * Proportional withdrawal of pool assets for the amount of LPTokens. */ std::pair AMMWithdraw::equalWithdrawTokens( @@ -824,7 +825,8 @@ AMMWithdraw::deleteAMMAccountIfEmpty( return {ter, true}; } -/** Proportional withdrawal of pool assets for the amount of LPTokens. +/** + * Proportional withdrawal of pool assets for the amount of LPTokens. */ std::tuple> AMMWithdraw::equalWithdrawTokens( @@ -910,7 +912,8 @@ AMMWithdraw::equalWithdrawTokens( // LCOV_EXCL_STOP } -/** All assets withdrawal with the constraints on the maximum amount +/** + * All assets withdrawal with the constraints on the maximum amount * of each asset that the trader is willing to withdraw. * a = (t/T) * A (5) * b = (t/T) * B (6) @@ -1000,7 +1003,8 @@ AMMWithdraw::equalWithdrawLimit( tfee); } -/** Withdraw single asset equivalent to the amount specified in Asset1Out. +/** + * Withdraw single asset equivalent to the amount specified in Asset1Out. * t = T * (c - sqrt(c**2 - 4*R))/2 * where R = b/B, c = R*fee + 2 - fee * Use equation 7 to compute the t, given the amount in Asset1Out. @@ -1046,7 +1050,8 @@ AMMWithdraw::singleWithdraw( tfee); } -/** withdrawal of single asset specified in Asset1Out proportional +/** + * withdrawal of single asset specified in Asset1Out proportional * to the share represented by the amount of LPTokens. * Use equation 8 to compute the amount of asset1, given the redeemed t * represented by LPTokens. Let this be Y. @@ -1090,7 +1095,8 @@ AMMWithdraw::singleWithdrawTokens( return {tecAMM_FAILED, STAmount{}}; } -/** Withdraw single asset with two constraints. +/** + * Withdraw single asset with two constraints. * a. amount of asset1 if specified (not 0) in Asset1Out specifies the minimum * amount of asset1 that the trader is willing to withdraw. * b. The effective price of asset traded out does not exceed the amount diff --git a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp index 9978d3e260..dc1b323482 100644 --- a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp +++ b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp @@ -42,7 +42,9 @@ PermissionedDomainDelete::preclaim(PreclaimContext const& ctx) return tesSUCCESS; } -/** Attempt to delete the Permissioned Domain. */ +/** + * Attempt to delete the Permissioned Domain. + */ TER PermissionedDomainDelete::doApply() { diff --git a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp index c2df114e48..61ebdcf9c7 100644 --- a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp +++ b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp @@ -73,7 +73,9 @@ PermissionedDomainSet::preclaim(PreclaimContext const& ctx) return tesSUCCESS; } -/** Attempt to create the Permissioned Domain. */ +/** + * Attempt to create the Permissioned Domain. + */ TER PermissionedDomainSet::doApply() { diff --git a/src/test/app/AMMCalc_test.cpp b/src/test/app/AMMCalc_test.cpp index 3c4a62bfd3..74080e669c 100644 --- a/src/test/app/AMMCalc_test.cpp +++ b/src/test/app/AMMCalc_test.cpp @@ -31,7 +31,8 @@ namespace xrpl::test { -/** AMM Calculator. Uses AMM formulas to simulate the payment engine +/** + * AMM Calculator. Uses AMM formulas to simulate the payment engine * expected results. Assuming the formulas are correct some unit-tests can * be verified. Currently supported operations are: * - swapIn, find out given in. in can flow through multiple AMM/Offer steps. diff --git a/src/test/app/AMMExtendedMPT_test.cpp b/src/test/app/AMMExtendedMPT_test.cpp index 63b7841fba..f04ea39f2b 100644 --- a/src/test/app/AMMExtendedMPT_test.cpp +++ b/src/test/app/AMMExtendedMPT_test.cpp @@ -2296,8 +2296,10 @@ private: // 1,400e12 - 56.3368e12*1.25 = 1400e12 - 70.4210e12 = // 1329.5789e12GBP env.require(Balance(alice_, gbp(1'329'578'947'368'420))); - //// 25% on 56.3368e12ETH is paid in tr fee 56.3368e12*1.25 - ///= 70.4210e12ETH + /** + * / 25% on 56.3368e12ETH is paid in tr fee 56.3368e12*1.25 + * = 70.4210e12ETH + */ // 56.3368e12GBP is swapped in for 53.3322e12ETH BEAST_EXPECT(amm.expectBalances( gbp(1'056'336'842'105'264), eth(946'667'729'591'836), amm.tokens())); diff --git a/src/test/app/AMMExtended_test.cpp b/src/test/app/AMMExtended_test.cpp index 563bb3ca33..bb532b361a 100644 --- a/src/test/app/AMMExtended_test.cpp +++ b/src/test/app/AMMExtended_test.cpp @@ -2420,8 +2420,10 @@ private: // 1,400 - 56.3368*1.25 = 1400 - 70.4210 = 1329.5789GBP BEAST_EXPECT( expectHolding(env, alice_, STAmount{GBP, UINT64_C(1'329'578947368421), -12})); - //// 25% on 56.3368EUR is paid in tr fee 56.3368*1.25 - ///= 70.4210EUR + /** + * / 25% on 56.3368EUR is paid in tr fee 56.3368*1.25 + * = 70.4210EUR + */ // 56.3368GBP is swapped in for 53.3322EUR BEAST_EXPECT(amm.expectBalances( STAmount{GBP, UINT64_C(1'056'336842105263), -12}, @@ -2435,8 +2437,10 @@ private: // 1,400 - 56.3368*1.25 = 1400 - 70.4210 = 1329.5789GBP BEAST_EXPECT( expectHolding(env, alice_, STAmount{GBP, UINT64_C(1'329'57894736842), -11})); - //// 25% on 56.3368EUR is paid in tr fee 56.3368*1.25 - ///= 70.4210EUR + /** + * / 25% on 56.3368EUR is paid in tr fee 56.3368*1.25 + * = 70.4210EUR + */ // 56.3368GBP is swapped in for 53.3322EUR BEAST_EXPECT(amm.expectBalances( STAmount{GBP, UINT64_C(1'056'336842105264), -12}, diff --git a/src/test/app/CheckMPT_test.cpp b/src/test/app/CheckMPT_test.cpp index 93e752154c..6370be7b6f 100644 --- a/src/test/app/CheckMPT_test.cpp +++ b/src/test/app/CheckMPT_test.cpp @@ -1411,7 +1411,8 @@ class CheckMPT_test : public beast::unit_test::Suite return acct.id(); } - /** Create MPTTester if it doesn't exist for the given MPT. + /** + * Create MPTTester if it doesn't exist for the given MPT. * Increment owners if created since it creates MPTokenIssuance */ MPT diff --git a/src/test/app/Invariants_test.cpp b/src/test/app/Invariants_test.cpp index 8a1f24e30b..461a151d8e 100644 --- a/src/test/app/Invariants_test.cpp +++ b/src/test/app/Invariants_test.cpp @@ -101,7 +101,8 @@ class Invariants_test : public beast::unit_test::Suite return xrpl::test::jtx::testableAmendments() | fixCleanup3_1_3 | fixCleanup3_2_0; } - /** Run a specific test case to put the ledger into a state that will be + /** + * Run a specific test case to put the ledger into a state that will be * detected by an invariant. Simulates the actions of a transaction that * would violate an invariant. * diff --git a/src/test/app/LedgerHistory_test.cpp b/src/test/app/LedgerHistory_test.cpp index 3d0e546678..f8688899e0 100644 --- a/src/test/app/LedgerHistory_test.cpp +++ b/src/test/app/LedgerHistory_test.cpp @@ -29,13 +29,13 @@ namespace xrpl::test { class LedgerHistory_test : public beast::unit_test::Suite { public: - /** Generate a new ledger by hand, applying a specific close time offset - and optionally inserting a transaction. - - If prev is nullptr, then the genesis ledger is made and no offset or - transaction is applied. - - */ + /** + * Generate a new ledger by hand, applying a specific close time offset + * and optionally inserting a transaction. + * + * If prev is nullptr, then the genesis ledger is made and no offset or + * transaction is applied. + */ static std::shared_ptr makeLedger( std::shared_ptr const& prev, diff --git a/src/test/app/Loan_test.cpp b/src/test/app/Loan_test.cpp index 569306e920..371fcae54f 100644 --- a/src/test/app/Loan_test.cpp +++ b/src/test/app/Loan_test.cpp @@ -324,7 +324,8 @@ protected: TenthBips32 const interestRate{}; }; - /** Helper class to compare the expected state of a loan and loan broker + /** + * Helper class to compare the expected state of a loan and loan broker * against the data in the ledger. */ struct VerifyLoanStatus @@ -344,7 +345,8 @@ protected: { } - /** Checks the expected broker state against the ledger + /** + * Checks the expected broker state against the ledger */ void checkBroker( @@ -415,7 +417,9 @@ protected: } } - /** Checks both the loan and broker expect states against the ledger */ + /** + * Checks both the loan and broker expect states against the ledger + */ void operator()( std::uint32_t previousPaymentDate, @@ -475,7 +479,9 @@ protected: } } - /** Checks both the loan and broker expect states against the ledger */ + /** + * Checks both the loan and broker expect states against the ledger + */ void operator()(LoanState const& state) const { @@ -541,7 +547,9 @@ protected: return {asset, keylet, vaultKeylet, params}; } - /// Get the state without checking anything + /** + * Get the state without checking anything + */ LoanState getCurrentState(jtx::Env const& env, BrokerInfo const& broker, Keylet const& loanKeylet) { @@ -569,8 +577,10 @@ protected: return LoanState{}; } - /// Get the state and check the values against the parameters used in - /// `lifecycle` + /** + * Get the state and check the values against the parameters used in + * `lifecycle` + */ LoanState getCurrentState( jtx::Env const& env, @@ -1242,7 +1252,8 @@ protected: PaymentParameters{.showStepBalances = true}); } - /** Runs through the complete lifecycle of a loan + /** + * Runs through the complete lifecycle of a loan * * 1. Create a loan. * 2. Test a bunch of transaction failure conditions. @@ -1560,7 +1571,8 @@ protected: return "Unknown"; } - /** Wrapper to run a series of lifecycle tests for a given asset and loan + /** + * Wrapper to run a series of lifecycle tests for a given asset and loan * amount * * Will be used in the future to vary the loan parameters. For now, it is diff --git a/src/test/app/Ticket_test.cpp b/src/test/app/Ticket_test.cpp index 5700503830..f14afd5990 100644 --- a/src/test/app/Ticket_test.cpp +++ b/src/test/app/Ticket_test.cpp @@ -47,9 +47,11 @@ namespace xrpl { class Ticket_test : public beast::unit_test::Suite { - /// @brief Validate metadata for a successful TicketCreate transaction. - /// - /// @param env current jtx env (tx and meta are extracted using it) + /** + * @brief Validate metadata for a successful TicketCreate transaction. + * + * @param env current jtx env (tx and meta are extracted using it) + */ void checkTicketCreateMeta(test::jtx::Env& env) { @@ -233,11 +235,13 @@ class Ticket_test : public beast::unit_test::Suite BEAST_EXPECT(*ticketSeqs.rbegin() == acctRootFinalSeq - 1); } - /// @brief Validate metadata for a ticket using transaction. - /// - /// The transaction may have been successful or failed with a tec. - /// - /// @param env current jtx env (tx and meta are extracted using it) + /** + * @brief Validate metadata for a ticket using transaction. + * + * The transaction may have been successful or failed with a tec. + * + * @param env current jtx env (tx and meta are extracted using it) + */ void checkTicketConsumeMeta(test::jtx::Env& env) { diff --git a/src/test/beast/define_print.cpp b/src/test/beast/define_print.cpp index 569b06ca67..e6b24e5cf2 100644 --- a/src/test/beast/define_print.cpp +++ b/src/test/beast/define_print.cpp @@ -15,7 +15,9 @@ namespace beast::unit_test { -/** A suite that prints the list of globally defined suites. */ +/** + * A suite that prints the list of globally defined suites. + */ class print_test : public Suite { public: diff --git a/src/test/consensus/DistributedValidatorsSim_test.cpp b/src/test/consensus/DistributedValidatorsSim_test.cpp index 1def09db13..437ad81ee0 100644 --- a/src/test/consensus/DistributedValidatorsSim_test.cpp +++ b/src/test/consensus/DistributedValidatorsSim_test.cpp @@ -21,7 +21,8 @@ namespace xrpl::test { -/** In progress simulations for diversifying and distributing validators +/** + * In progress simulations for diversifying and distributing validators */ class DistributedValidators_test : public beast::unit_test::Suite { diff --git a/src/test/consensus/LedgerTrie_test.cpp b/src/test/consensus/LedgerTrie_test.cpp index 0ddf1bf82f..a4eb7bc087 100644 --- a/src/test/consensus/LedgerTrie_test.cpp +++ b/src/test/consensus/LedgerTrie_test.cpp @@ -519,17 +519,18 @@ class LedgerTrie_test : public beast::unit_test::Suite // Changing largestSeq perspective changes preferred branch { - /** Build the tree below with initial tip support annotated - A - / \ - B(1) C(1) - / | | - H D F(1) - | - E(2) - | - G - */ + /** + * Build the tree below with initial tip support annotated + * A + * / \ + * B(1) C(1) + * / | | + * H D F(1) + * | + * E(2) + * | + * G + */ LedgerTrie t; LedgerHistoryHelper h; t.insert(h["ab"]); @@ -548,17 +549,18 @@ class LedgerTrie_test : public beast::unit_test::Suite BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["a"].id()); // NOLINTEND(bugprone-unchecked-optional-access) - /** One of E advancing to G doesn't change anything - A - / \ - B(1) C(1) - / | | - H D F(1) - | - E(1) - | - G(1) - */ + /** + * One of E advancing to G doesn't change anything + * A + * / \ + * B(1) C(1) + * / | | + * H D F(1) + * | + * E(1) + * | + * G(1) + */ t.remove(h["abde"]); t.insert(h["abdeg"]); @@ -570,17 +572,18 @@ class LedgerTrie_test : public beast::unit_test::Suite BEAST_EXPECT(t.getPreferred(Seq{5})->id == h["a"].id()); // NOLINTEND(bugprone-unchecked-optional-access) - /** C advancing to H does advance the seq 3 preferred ledger - A - / \ - B(1) C - / | | - H(1)D F(1) - | - E(1) - | - G(1) - */ + /** + * C advancing to H does advance the seq 3 preferred ledger + * A + * / \ + * B(1) C + * / | | + * H(1)D F(1) + * | + * E(1) + * | + * G(1) + */ t.remove(h["ac"]); t.insert(h["abh"]); @@ -592,17 +595,18 @@ class LedgerTrie_test : public beast::unit_test::Suite BEAST_EXPECT(t.getPreferred(Seq{5})->id == h["a"].id()); // NOLINTEND(bugprone-unchecked-optional-access) - /** F advancing to E also moves the preferred ledger forward - A - / \ - B(1) C - / | | - H(1)D F - | - E(2) - | - G(1) - */ + /** + * F advancing to E also moves the preferred ledger forward + * A + * / \ + * B(1) C + * / | | + * H(1)D F + * | + * E(2) + * | + * G(1) + */ t.remove(h["acf"]); t.insert(h["abde"]); diff --git a/src/test/core/Config_test.cpp b/src/test/core/Config_test.cpp index 17c48b1ccc..e98a0e1e88 100644 --- a/src/test/core/Config_test.cpp +++ b/src/test/core/Config_test.cpp @@ -124,7 +124,7 @@ backend=sqlite } /** - Write an xrpld config file and remove when done. + * Write an xrpld config file and remove when done. */ class FileCfgGuard : public xrpl::detail::FileDirGuard { @@ -235,7 +235,7 @@ more-xrpl-validators.net } /** - Write a validators.txt file and remove when done. + * Write a validators.txt file and remove when done. */ class ValidatorsTxtGuard : public detail::FileDirGuard { diff --git a/src/test/csf/BasicNetwork.h b/src/test/csf/BasicNetwork.h index 4b88592128..0428475504 100644 --- a/src/test/csf/BasicNetwork.h +++ b/src/test/csf/BasicNetwork.h @@ -6,59 +6,59 @@ #include namespace xrpl::test::csf { -/** Peer to peer network simulator. - - The network is formed from a set of Peer objects representing - vertices and configurable connections representing edges. - The caller is responsible for creating the Peer objects ahead - of time. - - Peer objects cannot be destroyed once the BasicNetwork is - constructed. To handle peers going online and offline, - callers can simply disconnect all links and reconnect them - later. Connections are directed, one end is the inbound - Peer and the other is the outbound Peer. - - Peers may send messages along their connections. To simulate - the effects of latency, these messages can be delayed by a - configurable duration set when the link is established. - Messages always arrive in the order they were sent on a - particular connection. - - A message is modeled using a lambda function. The caller - provides the code to execute upon delivery of the message. - If a Peer is disconnected, all messages pending delivery - at either end of the connection will not be delivered. - - When creating the Peer set, the caller needs to provide a - Scheduler object for managing the timing and delivery - of messages. After constructing the network, and establishing - connections, the caller uses the scheduler's step* functions - to drive messages through the network. - - The graph of peers and connections is internally represented - using Digraph. Clients have - const access to that graph to perform additional operations not - directly provided by BasicNetwork. - - Peer Requirements: - - Peer should be a lightweight type, cheap to copy - and/or move. A good candidate is a simple pointer to - the underlying user defined type in the simulation. - - Expression Type Requirements - ---------- ---- ------------ - P Peer - u, v Values of type P - P u(v) CopyConstructible - u.~P() Destructible - u == v bool EqualityComparable - u < v bool LessThanComparable - std::hash

class std::hash is defined for P - ! u bool true if u is not-a-peer - -*/ +/** + * Peer to peer network simulator. + * + * The network is formed from a set of Peer objects representing + * vertices and configurable connections representing edges. + * The caller is responsible for creating the Peer objects ahead + * of time. + * + * Peer objects cannot be destroyed once the BasicNetwork is + * constructed. To handle peers going online and offline, + * callers can simply disconnect all links and reconnect them + * later. Connections are directed, one end is the inbound + * Peer and the other is the outbound Peer. + * + * Peers may send messages along their connections. To simulate + * the effects of latency, these messages can be delayed by a + * configurable duration set when the link is established. + * Messages always arrive in the order they were sent on a + * particular connection. + * + * A message is modeled using a lambda function. The caller + * provides the code to execute upon delivery of the message. + * If a Peer is disconnected, all messages pending delivery + * at either end of the connection will not be delivered. + * + * When creating the Peer set, the caller needs to provide a + * Scheduler object for managing the timing and delivery + * of messages. After constructing the network, and establishing + * connections, the caller uses the scheduler's step* functions + * to drive messages through the network. + * + * The graph of peers and connections is internally represented + * using Digraph. Clients have + * const access to that graph to perform additional operations not + * directly provided by BasicNetwork. + * + * Peer Requirements: + * + * Peer should be a lightweight type, cheap to copy + * and/or move. A good candidate is a simple pointer to + * the underlying user defined type in the simulation. + * + * Expression Type Requirements + * ---------- ---- ------------ + * P Peer + * u, v Values of type P + * P u(v) CopyConstructible + * u.~P() Destructible + * u == v bool EqualityComparable + * u < v bool LessThanComparable + * std::hash

class std::hash is defined for P + * ! u bool true if u is not-a-peer + */ template class BasicNetwork { @@ -92,79 +92,84 @@ public: BasicNetwork(Scheduler& s); - /** Connect two peers. - - The link is directed, with `from` establishing - the outbound connection and `to` receiving the - incoming connection. - - Preconditions: - - from != to (self connect disallowed). - - A link between from and to does not - already exist (duplicates disallowed). - - Effects: - - Creates a link between from and to. - - @param `from` The source of the outgoing connection - @param `to` The recipient of the incoming connection - @param `delay` The time delay of all delivered messages - @return `true` if a new connection was established - */ + /** + * Connect two peers. + * + * The link is directed, with `from` establishing + * the outbound connection and `to` receiving the + * incoming connection. + * + * Preconditions: + * + * from != to (self connect disallowed). + * + * A link between from and to does not + * already exist (duplicates disallowed). + * + * Effects: + * + * Creates a link between from and to. + * + * @param `from` The source of the outgoing connection + * @param `to` The recipient of the incoming connection + * @param `delay` The time delay of all delivered messages + * @return `true` if a new connection was established + */ bool connect(Peer const& from, Peer const& to, duration const& delay = std::chrono::seconds{0}); - /** Break a link. - - Effects: - - If a connection is present, both ends are - disconnected. - - Any pending messages on the connection - are discarded. - - @return `true` if a connection was broken. - */ + /** + * Break a link. + * + * Effects: + * + * If a connection is present, both ends are + * disconnected. + * + * Any pending messages on the connection + * are discarded. + * + * @return `true` if a connection was broken. + */ bool disconnect(Peer const& peer1, Peer const& peer2); - /** Send a message to a peer. - - Preconditions: - - A link exists between from and to. - - Effects: - - If the link is not broken when the - link's `delay` time has elapsed, - the function will be invoked with - no arguments. - - @note Its the caller's responsibility to - ensure that the body of the function performs - activity consistent with `from`'s receipt of - a message from `to`. - */ + /** + * Send a message to a peer. + * + * Preconditions: + * + * A link exists between from and to. + * + * Effects: + * + * If the link is not broken when the + * link's `delay` time has elapsed, + * the function will be invoked with + * no arguments. + * + * @note Its the caller's responsibility to + * ensure that the body of the function performs + * activity consistent with `from`'s receipt of + * a message from `to`. + */ template void send(Peer const& from, Peer const& to, Function&& f); - /** Return the range of active links. - - @return A random access range over Digraph::Edge instances - */ + /** + * Return the range of active links. + * + * @return A random access range over Digraph::Edge instances + */ auto links(Peer const& from) { return links_.outEdges(from); } - /** Return the underlying digraph + /** + * Return the underlying digraph */ [[nodiscard]] Digraph const& graph() const diff --git a/src/test/csf/CollectorRef.h b/src/test/csf/CollectorRef.h index 5bc55c7515..3aef4d617f 100644 --- a/src/test/csf/CollectorRef.h +++ b/src/test/csf/CollectorRef.h @@ -12,45 +12,45 @@ namespace xrpl::test::csf { -/** Holds a type-erased reference to an arbitrary collector. - - A collector is any class that implements - - on(NodeID, SimTime, Event) - - for all events emitted by a Peer. - - This class is used to type-erase the actual collector used by each peer in - the simulation. The idea is to compose complicated and typed collectors - using the helpers in collectors.h, then only type erase at the higher-most - level when adding to the simulation. - - The example code below demonstrates the reason for storing the collector - as a reference. The collector's lifetime will generally be longer than - the simulation; perhaps several simulations are run for a single collector - instance. The collector potentially stores lots of data as well, so the - simulation needs to point to the single instance, rather than requiring - collectors to manage copying that data efficiently in their design. - - @code - // Initialize a specific collector that might write to a file. - SomeFancyCollector collector{"out.file"}; - - // Setup your simulation - Sim sim(trustgraph, topology, collector); - - // Run the simulation - sim.run(100); - - // do any reported related to the collector - collector.report(); - - @endcode - - @note If a new event type is added, it needs to be added to the interfaces - below. - -*/ +/** + * Holds a type-erased reference to an arbitrary collector. + * + * A collector is any class that implements + * + * on(NodeID, SimTime, Event) + * + * for all events emitted by a Peer. + * + * This class is used to type-erase the actual collector used by each peer in + * the simulation. The idea is to compose complicated and typed collectors + * using the helpers in collectors.h, then only type erase at the higher-most + * level when adding to the simulation. + * + * The example code below demonstrates the reason for storing the collector + * as a reference. The collector's lifetime will generally be longer than + * the simulation; perhaps several simulations are run for a single collector + * instance. The collector potentially stores lots of data as well, so the + * simulation needs to point to the single instance, rather than requiring + * collectors to manage copying that data efficiently in their design. + * + * @code + * // Initialize a specific collector that might write to a file. + * SomeFancyCollector collector{"out.file"}; + * + * // Setup your simulation + * Sim sim(trustgraph, topology, collector); + * + * // Run the simulation + * sim.run(100); + * + * // do any reported related to the collector + * collector.report(); + * + * @endcode + * + * @note If a new event type is added, it needs to be added to the interfaces + * below. + */ class CollectorRef { using tp = SimTime; @@ -296,16 +296,17 @@ public: } }; -/** A container of CollectorRefs - - A set of CollectorRef instances that process the same events. An event is - processed by collectors in the order the collectors were added. - - This class type-erases the collector instances. By contract, the - Collectors/collectors class/helper in collectors.h are not type erased and - offer an opportunity for type transformations and combinations with - improved compiler optimizations. -*/ +/** + * A container of CollectorRefs + * + * A set of CollectorRef instances that process the same events. An event is + * processed by collectors in the order the collectors were added. + * + * This class type-erases the collector instances. By contract, the + * Collectors/collectors class/helper in collectors.h are not type erased and + * offer an opportunity for type transformations and combinations with + * improved compiler optimizations. + */ class CollectorRefs { std::vector collectors_; diff --git a/src/test/csf/Digraph.h b/src/test/csf/Digraph.h index 82ed713561..b1b49404b5 100644 --- a/src/test/csf/Digraph.h +++ b/src/test/csf/Digraph.h @@ -21,16 +21,16 @@ struct NoEdgeData namespace test::csf { -/** Directed graph - -Basic directed graph that uses an adjacency list to represent out edges. - -Instances of Vertex uniquely identify vertices in the graph. Instances of -EdgeData is any data to store in the edge connecting two vertices. - -Both Vertex and EdgeData should be lightweight and cheap to copy. - -*/ +/** + * Directed graph + * + * Basic directed graph that uses an adjacency list to represent out edges. + * + * Instances of Vertex uniquely identify vertices in the graph. Instances of + * EdgeData is any data to store in the edge connecting two vertices. + * + * Both Vertex and EdgeData should be lightweight and cheap to copy. + */ template class Digraph { @@ -42,41 +42,42 @@ class Digraph Links empty_; public: - /** Connect two vertices - - @param source The source vertex - @param target The target vertex - @param e The edge data - @return true if the edge was created - - */ + /** + * Connect two vertices + * + * @param source The source vertex + * @param target The target vertex + * @param e The edge data + * @return true if the edge was created + */ bool connect(Vertex source, Vertex target, EdgeData e) { return graph_[source].emplace(target, e).second; } - /** Connect two vertices using default constructed edge data - - @param source The source vertex - @param target The target vertex - @return true if the edge was created - - */ + /** + * Connect two vertices using default constructed edge data + * + * @param source The source vertex + * @param target The target vertex + * @return true if the edge was created + */ bool connect(Vertex source, Vertex target) { return connect(source, target, EdgeData{}); } - /** Disconnect two vertices - - @param source The source vertex - @param target The target vertex - @return true if an edge was removed - - If source is not connected to target, this function does nothing. - */ + /** + * Disconnect two vertices + * + * @param source The source vertex + * @param target The target vertex + * @return true if an edge was removed + * + * If source is not connected to target, this function does nothing. + */ bool disconnect(Vertex source, Vertex target) { @@ -88,13 +89,13 @@ public: return false; } - /** Return edge data between two vertices - - @param source The source vertex - @param target The target vertex - @return optional which is std::nullopt if no edge exists - - */ + /** + * Return edge data between two vertices + * + * @param source The source vertex + * @param target The target vertex + * @return optional which is std::nullopt if no edge exists + */ [[nodiscard]] std::optional edge(Vertex source, Vertex target) const { @@ -108,23 +109,25 @@ public: return std::nullopt; } - /** Check if two vertices are connected - - @param source The source vertex - @param target The target vertex - @return true if the source has an out edge to target - */ + /** + * Check if two vertices are connected + * + * @param source The source vertex + * @param target The target vertex + * @return true if the source has an out edge to target + */ [[nodiscard]] bool connected(Vertex source, Vertex target) const { return edge(source, target) != std::nullopt; } - /** Range over vertices in the graph - - @return A boost transformed range over the vertices with out edges in - the graph - */ + /** + * Range over vertices in the graph + * + * @return A boost transformed range over the vertices with out edges in + * the graph + */ [[nodiscard]] auto outVertices() const { @@ -132,10 +135,11 @@ public: graph_, [](Graph::value_type const& v) { return v.first; }); } - /** Range over target vertices - - @param source The source vertex - @return A boost transformed range over the target vertices of source. + /** + * Range over target vertices + * + * @param source The source vertex + * @return A boost transformed range over the target vertices of source. */ [[nodiscard]] auto outVertices(Vertex source) const @@ -148,7 +152,8 @@ public: return boost::adaptors::transform(empty_, transform); } - /** Vertices and data associated with an Edge + /** + * Vertices and data associated with an Edge */ struct Edge { @@ -157,12 +162,13 @@ public: EdgeData data; }; - /** Range of out edges - - @param source The source vertex - @return A boost transformed range of Edge type for all out edges of - source. - */ + /** + * Range of out edges + * + * @param source The source vertex + * @return A boost transformed range of Edge type for all out edges of + * source. + */ [[nodiscard]] auto outEdges(Vertex source) const { @@ -177,11 +183,12 @@ public: return boost::adaptors::transform(empty_, transform); } - /** Vertex out-degree - - @param source The source vertex - @return The number of outgoing edges from source - */ + /** + * Vertex out-degree + * + * @param source The source vertex + * @return The number of outgoing edges from source + */ [[nodiscard]] std::size_t outDegree(Vertex source) const { @@ -191,14 +198,15 @@ public: return 0; } - /** Save GraphViz dot file - - Save a GraphViz dot description of the graph - @param fileName The output file (creates) - @param vertexName A invocable T vertexName(Vertex const &) that - returns the name target use for the vertex in the file - T must be ostream-able - */ + /** + * Save GraphViz dot file + * + * Save a GraphViz dot description of the graph + * @param fileName The output file (creates) + * @param vertexName A invocable T vertexName(Vertex const &) that + * returns the name target use for the vertex in the file + * T must be ostream-able + */ template void saveDot(std::ostream& out, VertexName&& vertexName) const diff --git a/src/test/csf/Histogram.h b/src/test/csf/Histogram.h index 60cb9a132e..62791c10f3 100644 --- a/src/test/csf/Histogram.h +++ b/src/test/csf/Histogram.h @@ -8,17 +8,16 @@ namespace xrpl::test::csf { -/** Basic histogram. - - Histogram for a type `T` that satisfies - - Default construction: T{} - - Comparison : T a, b; bool res = a < b - - Addition: T a, b; T c = a + b; - - Multiplication : T a, std::size_t b; T c = a * b; - - Division: T a; std::size_t b; T c = a/b; - - -*/ +/** + * Basic histogram. + * + * Histogram for a type `T` that satisfies + * - Default construction: T{} + * - Comparison : T a, b; bool res = a < b + * - Addition: T a, b; T c = a + b; + * - Multiplication : T a, std::size_t b; T c = a * b; + * - Division: T a; std::size_t b; T c = a/b; + */ template > class Histogram { @@ -28,7 +27,9 @@ class Histogram std::size_t samples_ = 0; public: - /** Insert an sample */ + /** + * Insert an sample + */ void insert(T const& s) { @@ -36,35 +37,45 @@ public: ++samples_; } - /** The number of samples */ + /** + * The number of samples + */ [[nodiscard]] std::size_t size() const { return samples_; } - /** The number of distinct samples (bins) */ + /** + * The number of distinct samples (bins) + */ [[nodiscard]] std::size_t numBins() const { return counts_.size(); } - /** Minimum observed value */ + /** + * Minimum observed value + */ [[nodiscard]] T minValue() const { return counts_.empty() ? T{} : counts_.begin()->first; } - /** Maximum observed value */ + /** + * Maximum observed value + */ [[nodiscard]] T maxValue() const { return counts_.empty() ? T{} : counts_.rbegin()->first; } - /** Histogram average */ + /** + * Histogram average + */ [[nodiscard]] T avg() const { @@ -80,12 +91,13 @@ public: return tmp / samples_; } - /** Calculate the given percentile of the distribution. - - @param p Percentile between 0 and 1, e.g. 0.50 is 50-th percentile - If the percentile falls between two bins, uses the nearest bin. - @return The given percentile of the distribution - */ + /** + * Calculate the given percentile of the distribution. + * + * @param p Percentile between 0 and 1, e.g. 0.50 is 50-th percentile + * If the percentile falls between two bins, uses the nearest bin. + * @return The given percentile of the distribution + */ [[nodiscard]] T percentile(float p) const { diff --git a/src/test/csf/Peer.h b/src/test/csf/Peer.h index 9d29704172..53b26d05cf 100644 --- a/src/test/csf/Peer.h +++ b/src/test/csf/Peer.h @@ -42,24 +42,26 @@ namespace xrpl::test::csf { namespace bc = boost::container; -/** A single peer in the simulation. - - This is the main work-horse of the consensus simulation framework and is - where many other components are integrated. The peer - - - Implements the Callbacks required by Consensus - - Manages trust & network connections with other peers - - Issues events back to the simulation based on its actions for analysis - by Collectors - - Exposes most internal state for forcibly simulating arbitrary scenarios -*/ +/** + * A single peer in the simulation. + * + * This is the main work-horse of the consensus simulation framework and is + * where many other components are integrated. The peer + * + * - Implements the Callbacks required by Consensus + * - Manages trust & network connections with other peers + * - Issues events back to the simulation based on its actions for analysis + * by Collectors + * - Exposes most internal state for forcibly simulating arbitrary scenarios + */ struct Peer { - /** Basic wrapper of a proposed position taken by a peer. - - For real consensus, this would add additional data for serialization - and signing. For simulation, nothing extra is needed. - */ + /** + * Basic wrapper of a proposed position taken by a peer. + * + * For real consensus, this would add additional data for serialization + * and signing. For simulation, nothing extra is needed. + */ class Position { public: @@ -89,16 +91,21 @@ struct Peer Proposal proposal_; }; - /** Simulated delays in internal peer processing. + /** + * Simulated delays in internal peer processing. */ struct ProcessingDelays { - //! Delay in consensus calling doAccept to accepting and issuing - //! validation - //! TODO: This should be a function of the number of transactions + /** + * Delay in consensus calling doAccept to accepting and issuing + * validation + * TODO: This should be a function of the number of transactions + */ std::chrono::milliseconds ledgerAccept{0}; - //! Delay in processing validations from remote peers + /** + * Delay in processing validations from remote peers + */ std::chrono::milliseconds recvValidation{0}; // Return the receive delay for message type M, default is no delay @@ -122,7 +129,8 @@ struct Peer { }; - /** Generic Validations adaptor that simply ignores recently stale + /** + * Generic Validations adaptor that simply ignores recently stale * validations */ class ValAdaptor @@ -165,7 +173,9 @@ struct Peer } }; - //! Type definitions for generic consensus + /** + * Type definitions for generic consensus + */ using Ledger_t = Ledger; using NodeID_t = PeerID; using NodeKey_t = PeerKey; @@ -174,74 +184,114 @@ struct Peer using Result = ConsensusResult; using NodeKey = Validation::NodeKey; - //! Logging support that prefixes messages with the peer ID + /** + * Logging support that prefixes messages with the peer ID + */ beast::WrappedSink sink; beast::Journal j; - //! Generic consensus + /** + * Generic consensus + */ Consensus consensus; - //! Our unique ID + /** + * Our unique ID + */ PeerID id; - //! Current signing key + /** + * Current signing key + */ PeerKey key; - //! The oracle that manages unique ledgers + /** + * The oracle that manages unique ledgers + */ LedgerOracle& oracle; - //! Scheduler of events + /** + * Scheduler of events + */ Scheduler& scheduler; - //! Handle to network for sending messages + /** + * Handle to network for sending messages + */ BasicNetwork& net; - //! Handle to Trust graph of network + /** + * Handle to Trust graph of network + */ TrustGraph& trustGraph; - //! openTxs that haven't been closed in a ledger yet + /** + * openTxs that haven't been closed in a ledger yet + */ TxSetType openTxs; - //! The last ledger closed by this node + /** + * The last ledger closed by this node + */ Ledger lastClosedLedger; - //! Ledgers this node has closed or loaded from the network + /** + * Ledgers this node has closed or loaded from the network + */ hash_map ledgers; - //! Validations from trusted nodes + /** + * Validations from trusted nodes + */ Validations validations; - //! The most recent ledger that has been fully validated by the network from - //! the perspective of this Peer + /** + * The most recent ledger that has been fully validated by the network from + * the perspective of this Peer + */ Ledger fullyValidatedLedger; //------------------------------------------------------------------------- // Store most network messages; these could be purged if memory use ever // becomes problematic - //! Map from Ledger::ID to vector of Positions with that ledger - //! as the prior ledger + /** + * Map from Ledger::ID to vector of Positions with that ledger + * as the prior ledger + */ bc::flat_map> peerPositions; - //! TxSet associated with a TxSet::ID + /** + * TxSet associated with a TxSet::ID + */ bc::flat_map txSets; // Ledgers/TxSets we are acquiring and when that request times out bc::flat_map acquiringLedgers; bc::flat_map acquiringTxSets; - //! The number of ledgers this peer has completed + /** + * The number of ledgers this peer has completed + */ int completedLedgers = 0; - //! The number of ledgers this peer should complete before stopping to run + /** + * The number of ledgers this peer should complete before stopping to run + */ int targetLedgers = std::numeric_limits::max(); - //! Skew of time relative to the common scheduler clock + /** + * Skew of time relative to the common scheduler clock + */ std::chrono::seconds clockSkew{0}; - //! Simulated delays to use for internal processing + /** + * Simulated delays to use for internal processing + */ ProcessingDelays delays; - //! Whether to simulate running as validator or a tracking node + /** + * Whether to simulate running as validator or a tracking node + */ bool runAsValidator = true; // TODO: Consider removing these two, they are only a convenience for tests @@ -259,20 +309,22 @@ struct Peer // Simulation parameters ConsensusParms consensusParms; - //! The collectors to report events to + /** + * The collectors to report events to + */ CollectorRefs& collectors; - /** Constructor - - @param i Unique PeerID - @param s Simulation Scheduler - @param o Simulation Oracle - @param n Simulation network - @param tg Simulation trust graph - @param c Simulation collectors - @param jIn Simulation journal - - */ + /** + * Constructor + * + * @param i Unique PeerID + * @param s Simulation Scheduler + * @param o Simulation Oracle + * @param n Simulation network + * @param tg Simulation trust graph + * @param c Simulation collectors + * @param jIn Simulation journal + */ Peer( PeerID i, Scheduler& s, @@ -302,9 +354,10 @@ struct Peer trustGraph.trust(this, this); } - /** Schedule the provided callback in `when` duration, but if - `when` is 0, call immediately - */ + /** + * Schedule the provided callback in `when` duration, but if + * `when` is 0, call immediately + */ template void schedule(std::chrono::nanoseconds when, T&& what) @@ -368,14 +421,15 @@ struct Peer return false; } - /** Create network connection - - Creates a new outbound connection to another Peer if none exists - - @param o The peer with the inbound connection - @param dur The fixed delay for messages between the two Peers - @return Whether the connection was created. - */ + /** + * Create network connection + * + * Creates a new outbound connection to another Peer if none exists + * + * @param o The peer with the inbound connection + * @param dur The fixed delay for messages between the two Peers + * @return Whether the connection was created. + */ bool connect(Peer& o, SimDuration dur) @@ -383,13 +437,14 @@ struct Peer return net.connect(this, &o, dur); } - /** Remove a network connection - - Removes a connection between peers if one exists - - @param o The peer we disconnect from - @return Whether the connection was removed - */ + /** + * Remove a network connection + * + * Removes a connection between peers if one exists + * + * @param o The peer we disconnect from + * @return Whether the connection was removed + */ bool disconnect(Peer& o) { @@ -653,7 +708,9 @@ struct Peer //-------------------------------------------------------------------------- // Validation members - /** Add a trusted validation and return true if it is worth forwarding */ + /** + * Add a trusted validation and return true if it is worth forwarding + */ bool addTrustedValidation(Validation v) { @@ -670,7 +727,9 @@ struct Peer return true; } - /** Check if a new ledger can be deemed fully validated */ + /** + * Check if a new ledger can be deemed fully validated + */ void checkFullyValidated(Ledger const& ledger) { @@ -872,7 +931,9 @@ struct Peer //-------------------------------------------------------------------------- // Simulation "driver" members - //! Heartbeat timer call + /** + * Heartbeat timer call + */ void timerEntry() { @@ -939,16 +1000,17 @@ struct Peer // TODO: Make this more robust hash_map txInjections; - /** Inject non-consensus Tx - - Injects a transactionsinto the ledger following prevLedger's sequence - number. - - @param prevLedger The ledger we are building the new ledger on top of - @param src The Consensus TxSet - @return Consensus TxSet with inject transactions added if prevLedger.seq - matches a previously registered Tx. - */ + /** + * Inject non-consensus Tx + * + * Injects a transactionsinto the ledger following prevLedger's sequence + * number. + * + * @param prevLedger The ledger we are building the new ledger on top of + * @param src The Consensus TxSet + * @return Consensus TxSet with inject transactions added if prevLedger.seq + * matches a previously registered Tx. + */ TxSet injectTxs(Ledger prevLedger, TxSet const& src) { diff --git a/src/test/csf/PeerGroup.h b/src/test/csf/PeerGroup.h index e5efecac34..1c31209ef3 100644 --- a/src/test/csf/PeerGroup.h +++ b/src/test/csf/PeerGroup.h @@ -17,14 +17,15 @@ namespace xrpl::test::csf { -/** A group of simulation Peers - - A PeerGroup is a convenient handle for logically grouping peers together, - and then creating trust or network relations for the group at large. Peer - groups may also be combined to build out more complex structures. - - The PeerGroup provides random access style iterators and operator[] -*/ +/** + * A group of simulation Peers + * + * A PeerGroup is a convenient handle for logically grouping peers together, + * and then creating trust or network relations for the group at large. Peer + * groups may also be combined to build out more complex structures. + * + * The PeerGroup provides random access style iterators and operator[] + */ class PeerGroup { using peers_type = std::vector; @@ -102,12 +103,13 @@ public: return peers_.size(); } - /** Establish trust - - Establish trust from all peers in this group to all peers in o - - @param o The group of peers to trust - */ + /** + * Establish trust + * + * Establish trust from all peers in this group to all peers in o + * + * @param o The group of peers to trust + */ void trust(PeerGroup const& o) { @@ -120,12 +122,13 @@ public: } } - /** Revoke trust - - Revoke trust from all peers in this group to all peers in o - - @param o The group of peers to untrust - */ + /** + * Revoke trust + * + * Revoke trust from all peers in this group to all peers in o + * + * @param o The group of peers to untrust + */ void untrust(PeerGroup const& o) { @@ -138,16 +141,15 @@ public: } } - /** Establish network connection - - Establish outbound connections from all peers in this group to all peers - in o. If a connection already exists, no new connection is established. - - @param o The group of peers to connect to (will get inbound connections) - @param delay The fixed messaging delay for all established connections - - - */ + /** + * Establish network connection + * + * Establish outbound connections from all peers in this group to all peers + * in o. If a connection already exists, no new connection is established. + * + * @param o The group of peers to connect to (will get inbound connections) + * @param delay The fixed messaging delay for all established connections + */ void connect(PeerGroup const& o, SimDuration delay) { @@ -162,12 +164,13 @@ public: } } - /** Destroy network connection - - Destroy connections from all peers in this group to all peers in o - - @param o The group of peers to disconnect from - */ + /** + * Destroy network connection + * + * Destroy connections from all peers in this group to all peers in o + * + * @param o The group of peers to disconnect from + */ void disconnect(PeerGroup const& o) { @@ -180,14 +183,15 @@ public: } } - /** Establish trust and network connection - - Establish trust and create a network connection with fixed delay - from all peers in this group to all peers in o - - @param o The group of peers to trust and connect to - @param delay The fixed messaging delay for all established connections - */ + /** + * Establish trust and network connection + * + * Establish trust and create a network connection with fixed delay + * from all peers in this group to all peers in o + * + * @param o The group of peers to trust and connect to + * @param delay The fixed messaging delay for all established connections + */ void trustAndConnect(PeerGroup const& o, SimDuration delay) { @@ -195,15 +199,15 @@ public: connect(o, delay); } - /** Establish network connections based on trust relations - - For each peers in this group, create outbound network connection - to the set of peers it trusts. If a connection already exists, it is - not recreated. - - @param delay The fixed messaging delay for all established connections - - */ + /** + * Establish network connections based on trust relations + * + * For each peers in this group, create outbound network connection + * to the set of peers it trusts. If a connection already exists, it is + * not recreated. + * + * @param delay The fixed messaging delay for all established connections + */ void connectFromTrust(SimDuration delay) { @@ -253,25 +257,25 @@ public: } }; -/** Randomly generate peer groups according to ranks. - - Generates random peer groups based on a provided ranking of peers. This - mimics a process of randomly generating UNLs, where more "important" peers - are more likely to appear in a UNL. - - `numGroups` subgroups are generated by randomly sampling without without - replacement from peers according to the `ranks`. - - - - @param peers The group of peers - @param ranks The relative importance of each peer, must match the size of - peers. Higher relative rank means more likely to be sampled. - @param numGroups The number of peer link groups to generate - @param sizeDist The distribution that determines the size of a link group - @param g The uniform random bit generator - -*/ +/** + * Randomly generate peer groups according to ranks. + * + * Generates random peer groups based on a provided ranking of peers. This + * mimics a process of randomly generating UNLs, where more "important" peers + * are more likely to appear in a UNL. + * + * `numGroups` subgroups are generated by randomly sampling without without + * replacement from peers according to the `ranks`. + * + * + * + * @param peers The group of peers + * @param ranks The relative importance of each peer, must match the size of + * peers. Higher relative rank means more likely to be sampled. + * @param numGroups The number of peer link groups to generate + * @param sizeDist The distribution that determines the size of a link group + * @param g The uniform random bit generator + */ template std::vector randomRankedGroups( @@ -295,10 +299,11 @@ randomRankedGroups( return groups; } -/** Generate random trust groups based on peer rankings. - - @see randomRankedGroups for descriptions of the arguments -*/ +/** + * Generate random trust groups based on peer rankings. + * + * @see randomRankedGroups for descriptions of the arguments + */ template void randomRankedTrust( @@ -318,10 +323,11 @@ randomRankedTrust( } } -/** Generate random network groups based on peer rankings. - - @see randomRankedGroups for descriptions of the arguments -*/ +/** + * Generate random network groups based on peer rankings. + * + * @see randomRankedGroups for descriptions of the arguments + */ template void randomRankedConnect( diff --git a/src/test/csf/Proposal.h b/src/test/csf/Proposal.h index 06486daed3..ecf430ae8d 100644 --- a/src/test/csf/Proposal.h +++ b/src/test/csf/Proposal.h @@ -7,9 +7,10 @@ #include namespace xrpl::test::csf { -/** Proposal is a position taken in the consensus process and is represented - directly from the generic types. -*/ +/** + * Proposal is a position taken in the consensus process and is represented + * directly from the generic types. + */ using Proposal = ConsensusProposal; } // namespace xrpl::test::csf diff --git a/src/test/csf/Scheduler.h b/src/test/csf/Scheduler.h index 3fc187e1e7..b1ac2bb5d4 100644 --- a/src/test/csf/Scheduler.h +++ b/src/test/csf/Scheduler.h @@ -12,17 +12,18 @@ namespace xrpl::test::csf { -/** Simulated discrete-event scheduler. - - Simulates the behavior of events using a single common clock. - - An event is modeled using a lambda function and is scheduled to occur at a - specific time. Events may be canceled using a token returned when the - event is scheduled. - - The caller uses one or more of the step, stepOne, stepFor, stepUntil and - stepWhile functions to process scheduled events. -*/ +/** + * Simulated discrete-event scheduler. + * + * Simulates the behavior of events using a single common clock. + * + * An event is modeled using a lambda function and is scheduled to occur at a + * specific time. Events may be canceled using a token returned when the + * event is scheduled. + * + * The caller uses one or more of the step, stepOne, stepFor, stepUntil and + * stepWhile functions to process scheduled events. + */ class Scheduler { public: @@ -135,116 +136,127 @@ public: Scheduler(); - /** Return the clock. (aged_containers want a non-const ref =( */ + /** + * Return the clock. (aged_containers want a non-const ref =( + */ clock_type& clock() const; - /** Return the current network time. - - @note The epoch is unspecified - */ + /** + * Return the current network time. + * + * @note The epoch is unspecified + */ time_point now() const; // Used to cancel timers struct CancelToken; - /** Schedule an event at a specific time - - Effects: - - When the network time is reached, - the function will be called with - no arguments. - */ + /** + * Schedule an event at a specific time + * + * Effects: + * + * When the network time is reached, + * the function will be called with + * no arguments. + */ template CancelToken at(time_point const& when, Function&& f); - /** Schedule an event after a specified duration passes - - Effects: - - When the specified time has elapsed, - the function will be called with - no arguments. - */ + /** + * Schedule an event after a specified duration passes + * + * Effects: + * + * When the specified time has elapsed, + * the function will be called with + * no arguments. + */ template CancelToken in(duration const& delay, Function&& f); - /** Cancel a timer. - - Preconditions: - - `token` was the return value of a call - timer() which has not yet been invoked. - */ + /** + * Cancel a timer. + * + * Preconditions: + * + * `token` was the return value of a call + * timer() which has not yet been invoked. + */ void cancel(CancelToken const& token); - /** Run the scheduler for up to one event. - - Effects: - - The clock is advanced to the time - of the last delivered event. - - @return `true` if an event was processed. - */ + /** + * Run the scheduler for up to one event. + * + * Effects: + * + * The clock is advanced to the time + * of the last delivered event. + * + * @return `true` if an event was processed. + */ bool stepOne(); - /** Run the scheduler until no events remain. - - Effects: - - The clock is advanced to the time - of the last event. - - @return `true` if an event was processed. - */ + /** + * Run the scheduler until no events remain. + * + * Effects: + * + * The clock is advanced to the time + * of the last event. + * + * @return `true` if an event was processed. + */ bool step(); - /** Run the scheduler while a condition is true. - - Function takes no arguments and will be called - repeatedly after each event is processed to - decide whether to continue. - - Effects: - - The clock is advanced to the time - of the last delivered event. - - @return `true` if any event was processed. - */ + /** + * Run the scheduler while a condition is true. + * + * Function takes no arguments and will be called + * repeatedly after each event is processed to + * decide whether to continue. + * + * Effects: + * + * The clock is advanced to the time + * of the last delivered event. + * + * @return `true` if any event was processed. + */ template bool stepWhile(Function&& func); - /** Run the scheduler until the specified time. - - Effects: - - The clock is advanced to the - specified time. - - @return `true` if any event remain. - */ + /** + * Run the scheduler until the specified time. + * + * Effects: + * + * The clock is advanced to the + * specified time. + * + * @return `true` if any event remain. + */ bool stepUntil(time_point const& until); - /** Run the scheduler until time has elapsed. - - Effects: - - The clock is advanced by the - specified duration. - - @return `true` if any event remain. - */ + /** + * Run the scheduler until time has elapsed. + * + * Effects: + * + * The clock is advanced by the + * specified duration. + * + * @return `true` if any event remain. + */ template bool stepFor(std::chrono::duration const& amount); diff --git a/src/test/csf/Sim.h b/src/test/csf/Sim.h index d2a63f6507..94d26d5e06 100644 --- a/src/test/csf/Sim.h +++ b/src/test/csf/Sim.h @@ -21,7 +21,9 @@ namespace xrpl::test::csf { -/** Sink that prepends simulation time to messages */ +/** + * Sink that prepends simulation time to messages + */ class BasicSink : public beast::Journal::Sink { Scheduler::clock_type const& clock_; @@ -65,28 +67,29 @@ public: TrustGraph trustGraph; CollectorRefs collectors; - /** Create a simulation - - Creates a new simulation. The simulation has no peers, no trust links - and no network connections. - - */ + /** + * Create a simulation + * + * Creates a new simulation. The simulation has no peers, no trust links + * and no network connections. + */ // NOLINTNEXTLINE(bugprone-random-generator-seed): fixed seed for reproducible test Sim() : sink{scheduler.clock()}, j{sink}, net{scheduler} { } - /** Create a new group of peers. - - Creates a new group of peers. The peers do not have any trust relations - or network connections by default. Those must be configured by the - client. - - @param numPeers The number of peers in the group - @return PeerGroup representing these new peers - - @note This increases the number of peers in the simulation by numPeers. - */ + /** + * Create a new group of peers. + * + * Creates a new group of peers. The peers do not have any trust relations + * or network connections by default. Those must be configured by the + * client. + * + * @param numPeers The number of peers in the group + * @return PeerGroup representing these new peers + * + * @note This increases the number of peers in the simulation by numPeers. + */ PeerGroup createGroup(std::size_t numPeers) { @@ -109,48 +112,57 @@ public: return res; } - //! The number of peers in the simulation + /** + * The number of peers in the simulation + */ std::size_t size() const { return peers_.size(); } - /** Run consensus protocol to generate the provided number of ledgers. - - Has each peer run consensus until it closes `ledgers` more ledgers. - - @param ledgers The number of additional ledgers to close - */ + /** + * Run consensus protocol to generate the provided number of ledgers. + * + * Has each peer run consensus until it closes `ledgers` more ledgers. + * + * @param ledgers The number of additional ledgers to close + */ void run(int ledgers); - /** Run consensus for the given duration */ + /** + * Run consensus for the given duration + */ void run(SimDuration const& dur); - /** Check whether all peers in the group are synchronized. - - Nodes in the group are synchronized if they share the same last - fully validated and last generated ledger. - */ + /** + * Check whether all peers in the group are synchronized. + * + * Nodes in the group are synchronized if they share the same last + * fully validated and last generated ledger. + */ static bool synchronized(PeerGroup const& g); - /** Check whether all peers in the network are synchronized + /** + * Check whether all peers in the network are synchronized */ bool synchronized() const; - /** Calculate the number of branches in the group. - - A branch occurs if two nodes in the group have fullyValidatedLedgers - that are not on the same chain of ledgers. - */ + /** + * Calculate the number of branches in the group. + * + * A branch occurs if two nodes in the group have fullyValidatedLedgers + * that are not on the same chain of ledgers. + */ std::size_t branches(PeerGroup const& g) const; - /** Calculate the number of branches in the network + /** + * Calculate the number of branches in the network */ std::size_t branches() const; diff --git a/src/test/csf/TrustGraph.h b/src/test/csf/TrustGraph.h index a10e318706..d46a887364 100644 --- a/src/test/csf/TrustGraph.h +++ b/src/test/csf/TrustGraph.h @@ -10,13 +10,14 @@ namespace xrpl::test::csf { -/** Trust graph - - Trust is a directed relationship from a node i to node j. - If node i trusts node j, then node i has node j in its UNL. - This class wraps a digraph representing the trust relationships for all - peers in the simulation. -*/ +/** + * Trust graph + * + * Trust is a directed relationship from a node i to node j. + * If node i trusts node j, then node i has node j in its UNL. + * This class wraps a digraph representing the trust relationships for all + * peers in the simulation. + */ template class TrustGraph { @@ -25,7 +26,8 @@ class TrustGraph Graph graph_; public: - /** Create an empty trust graph + /** + * Create an empty trust graph */ TrustGraph() = default; @@ -35,29 +37,30 @@ public: return graph_; } - /** Create trust - - Establish trust between Peer `from` and Peer `to`; as if `from` put `to` - in its UNL. - - @param from The peer granting trust - @param to The peer receiving trust - - */ + /** + * Create trust + * + * Establish trust between Peer `from` and Peer `to`; as if `from` put `to` + * in its UNL. + * + * @param from The peer granting trust + * @param to The peer receiving trust + */ void trust(Peer const& from, Peer const& to) { graph_.connect(from, to); } - /** Remove trust - - Revoke trust from Peer `from` to Peer `to`; as if `from` removed `to` - from its UNL. - - @param from The peer revoking trust - @param to The peer being revoked - */ + /** + * Remove trust + * + * Revoke trust from Peer `from` to Peer `to`; as if `from` removed `to` + * from its UNL. + * + * @param from The peer revoking trust + * @param to The peer being revoked + */ void untrust(Peer const& from, Peer const& to) { @@ -71,19 +74,21 @@ public: return graph_.connected(from, to); } - /** Range over trusted peers - - @param a The node granting trust - @return boost transformed range over nodes `a` trusts, i.e. the nodes - in its UNL - */ + /** + * Range over trusted peers + * + * @param a The node granting trust + * @return boost transformed range over nodes `a` trusts, i.e. the nodes + * in its UNL + */ [[nodiscard]] auto trustedPeers(Peer const& a) const { return graph_.outVertices(a); } - /** An example of nodes that fail the whitepaper no-forking condition + /** + * An example of nodes that fail the whitepaper no-forking condition */ struct ForkInfo { @@ -133,9 +138,10 @@ public: return res; } - /** Check whether this trust graph satisfies the whitepaper no-forking - condition - */ + /** + * Check whether this trust graph satisfies the whitepaper no-forking + * condition + */ [[nodiscard]] bool canFork(double quorum) const { diff --git a/src/test/csf/Tx.h b/src/test/csf/Tx.h index 75254f865a..0412d346f3 100644 --- a/src/test/csf/Tx.h +++ b/src/test/csf/Tx.h @@ -17,7 +17,9 @@ namespace xrpl::test::csf { -//! A single transaction +/** + * A single transaction + */ class Tx { public: @@ -56,11 +58,15 @@ private: ID id_; }; -//!------------------------------------------------------------------------- -//! All sets of Tx are represented as a flat_set for performance. +/** + * ------------------------------------------------------------------------- + * All sets of Tx are represented as a flat_set for performance. + */ using TxSetType = boost::container::flat_set; -//! TxSet is a set of transactions to consider including in the ledger +/** + * TxSet is a set of transactions to consider including in the ledger + */ class TxSet { public: @@ -135,10 +141,11 @@ public: return id_; } - /** @return Map of Tx::ID that are missing. True means - it was in this set and not other. False means - it was in the other set and not this - */ + /** + * @return Map of Tx::ID that are missing. True means + * it was in this set and not other. False means + * it was in the other set and not this + */ [[nodiscard]] std::map compare(TxSet const& other) const { @@ -160,10 +167,14 @@ public: } private: - //! The set contains the actual transactions + /** + * The set contains the actual transactions + */ TxSetType txs_; - //! The unique ID of this tx set + /** + * The unique ID of this tx set + */ ID id_{}; }; diff --git a/src/test/csf/Validation.h b/src/test/csf/Validation.h index 73b6784534..0b9fc94890 100644 --- a/src/test/csf/Validation.h +++ b/src/test/csf/Validation.h @@ -16,15 +16,17 @@ struct PeerIDTag; //< Uniquely identifies a peer using PeerID = TaggedInteger; -/** The current key of a peer - - Eventually, the second entry in the pair can be used to model ephemeral - keys. Right now, the convention is to have the second entry 0 as the - master key. -*/ +/** + * The current key of a peer + * + * Eventually, the second entry in the pair can be used to model ephemeral + * keys. Right now, the convention is to have the second entry 0 as the + * master key. + */ using PeerKey = std::pair; -/** Validation of a specific ledger by a specific Peer. +/** + * Validation of a specific ledger by a specific Peer. */ class Validation { diff --git a/src/test/csf/collectors.h b/src/test/csf/collectors.h index 1100f6c690..f85854e5dd 100644 --- a/src/test/csf/collectors.h +++ b/src/test/csf/collectors.h @@ -32,12 +32,13 @@ namespace xrpl::test::csf { // This file contains helper functions for composing different collectors // and also defines several standard collectors available for simulations. -/** Group of collectors. - - Presents a group of collectors as a single collector which process an event - by calling each collector sequentially. This is analogous to CollectorRefs - in CollectorRef.h, but does *not* erase the type information of the combined - collectors. +/** + * Group of collectors. + * + * Presents a group of collectors as a single collector which process an event + * by calling each collector sequentially. This is analogous to CollectorRefs + * in CollectorRef.h, but does *not* erase the type information of the combined + * collectors. */ template class Collectors @@ -59,10 +60,11 @@ class Collectors } public: - /** Constructor - - @param cs References to the collectors to call together - */ + /** + * Constructor + * + * @param cs References to the collectors to call together + */ Collectors(Cs&... cs) : cs_(std::tie(cs...)) { } @@ -75,7 +77,9 @@ public: } }; -/** Create an instance of Collectors */ +/** + * Create an instance of Collectors + */ template Collectors makeCollectors(Cs&... cs) @@ -83,14 +87,15 @@ makeCollectors(Cs&... cs) return Collectors(cs...); } -/** Maintain an instance of a Collector per peer - - For each peer that emits events, this class maintains a corresponding - instance of CollectorType, only forwarding events emitted by the peer to - the related instance. - - CollectorType should be default constructible. -*/ +/** + * Maintain an instance of a Collector per peer + * + * For each peer that emits events, this class maintains a corresponding + * instance of CollectorType, only forwarding events emitted by the peer to + * the related instance. + * + * CollectorType should be default constructible. + */ template struct CollectByNode { @@ -115,7 +120,9 @@ struct CollectByNode } }; -/** Collector which ignores all events */ +/** + * Collector which ignores all events + */ struct NullCollector { template @@ -125,7 +132,9 @@ struct NullCollector } }; -/** Tracks the overall duration of a simulation */ +/** + * Tracks the overall duration of a simulation + */ struct SimDurationCollector { bool init = false; @@ -148,15 +157,16 @@ struct SimDurationCollector } }; -/** Tracks the submission -> accepted -> validated evolution of transactions. - - This collector tracks transactions through the network by monitoring the - *first* time the transaction is seen by any node in the network, or - seen by any node's accepted or fully validated ledger. - - If transactions submitted to the network do not have unique IDs, this - collector will not track subsequent submissions. -*/ +/** + * Tracks the submission -> accepted -> validated evolution of transactions. + * + * This collector tracks transactions through the network by monitoring the + * *first* time the transaction is seen by any node in the network, or + * seen by any node's accepted or fully validated ledger. + * + * If transactions submitted to the network do not have unique IDs, this + * collector will not track subsequent submissions. + */ struct TxCollector { // Counts @@ -381,12 +391,12 @@ struct TxCollector } }; -/** Tracks the accepted -> validated evolution of ledgers. - - This collector tracks ledgers through the network by monitoring the - *first* time the ledger is accepted or fully validated by ANY node. - -*/ +/** + * Tracks the accepted -> validated evolution of ledgers. + * + * This collector tracks ledgers through the network by monitoring the + * *first* time the ledger is accepted or fully validated by ANY node. + */ struct LedgerCollector { std::size_t accepted{0}; @@ -580,11 +590,12 @@ struct LedgerCollector } }; -/** Write out stream of ledger activity - - Writes information about every accepted and fully-validated ledger to a - provided std::ostream. -*/ +/** + * Write out stream of ledger activity + * + * Writes information about every accepted and fully-validated ledger to a + * provided std::ostream. + */ struct StreamCollector { std::ostream& out; @@ -611,11 +622,12 @@ struct StreamCollector } }; -/** Saves information about Jumps for closed and fully validated ledgers. A - jump occurs when a node closes/fully validates a new ledger that is not the - immediate child of the prior closed/fully validated ledgers. This includes - jumps across branches and jumps ahead in the same branch of ledger history. -*/ +/** + * Saves information about Jumps for closed and fully validated ledgers. A + * jump occurs when a node closes/fully validates a new ledger that is not the + * immediate child of the prior closed/fully validated ledgers. This includes + * jumps across branches and jumps ahead in the same branch of ledger history. + */ struct JumpCollector { struct Jump diff --git a/src/test/csf/events.h b/src/test/csf/events.h index dfedd2627a..2cf4fd9e9b 100644 --- a/src/test/csf/events.h +++ b/src/test/csf/events.h @@ -29,58 +29,81 @@ namespace xrpl::test::csf { // CollectorRef.f defines a type-erased holder for arbitrary Collectors. If // any new events are added, the interface there needs to be updated. -/** A value to be flooded to all other peers starting from this peer. +/** + * A value to be flooded to all other peers starting from this peer. */ template struct Share { - //! Event that is shared + /** + * Event that is shared + */ V val; }; -/** A value relayed to another peer as part of flooding +/** + * A value relayed to another peer as part of flooding */ template struct Relay { - //! Peer relaying to + /** + * Peer relaying to + */ PeerID to; - //! The value to relay + /** + * The value to relay + */ V val; }; -/** A value received from another peer as part of flooding +/** + * A value received from another peer as part of flooding */ template struct Receive { - //! Peer that sent the value + /** + * Peer that sent the value + */ PeerID from; - //! The received value + /** + * The received value + */ V val; }; -/** A transaction submitted to a peer */ +/** + * A transaction submitted to a peer + */ struct SubmitTx { - //! The submitted transaction + /** + * The submitted transaction + */ Tx tx; }; -/** Peer starts a new consensus round +/** + * Peer starts a new consensus round */ struct StartRound { - //! The preferred ledger for the start of consensus + /** + * The preferred ledger for the start of consensus + */ Ledger::ID bestLedger{}; - //! The prior ledger on hand + /** + * The prior ledger on hand + */ Ledger prevLedger; }; -/** Peer closed the open ledger +/** + * Peer closed the open ledger */ struct CloseLedger { @@ -91,7 +114,9 @@ struct CloseLedger TxSetType txs; }; -//! Peer accepted consensus results +/** + * Peer accepted consensus results + */ struct AcceptLedger { // The newly created ledger @@ -101,7 +126,9 @@ struct AcceptLedger Ledger prior; }; -//! Peer detected a wrong prior ledger during consensus +/** + * Peer detected a wrong prior ledger during consensus + */ struct WrongPrevLedger { // ID of wrong ledger we had @@ -110,14 +137,20 @@ struct WrongPrevLedger Ledger::ID right; }; -//! Peer fully validated a new ledger +/** + * Peer fully validated a new ledger + */ struct FullyValidateLedger { - //! The new fully validated ledger + /** + * The new fully validated ledger + */ Ledger ledger; - //! The prior fully validated ledger - //! This is a jump if prior.id() != ledger.parentID() + /** + * The prior fully validated ledger + * This is a jump if prior.id() != ledger.parentID() + */ Ledger prior; }; diff --git a/src/test/csf/ledgers.h b/src/test/csf/ledgers.h index 4873519dee..09f5fa54de 100644 --- a/src/test/csf/ledgers.h +++ b/src/test/csf/ledgers.h @@ -22,27 +22,28 @@ namespace xrpl::test::csf { -/** A ledger is a set of observed transactions and a sequence number - identifying the ledger. - - Peers in the consensus process are trying to agree on a set of transactions - to include in a ledger. For simulation, each transaction is a single - integer and the ledger is the set of observed integers. This means future - ledgers have prior ledgers as subsets, e.g. - - Ledger 0 : {} - Ledger 1 : {1,4,5} - Ledger 2 : {1,2,4,5,10} - .... - - Ledgers are immutable value types. All ledgers with the same sequence - number, transactions, close time, etc. will have the same ledger ID. The - LedgerOracle class below manages ID assignments for a simulation and is the - only way to close and create a new ledger. Since the parent ledger ID is - part of type, this also means ledgers with distinct histories will have - distinct ids, even if they have the same set of transactions, sequence - number and close time. -*/ +/** + * A ledger is a set of observed transactions and a sequence number + * identifying the ledger. + * + * Peers in the consensus process are trying to agree on a set of transactions + * to include in a ledger. For simulation, each transaction is a single + * integer and the ledger is the set of observed integers. This means future + * ledgers have prior ledgers as subsets, e.g. + * + * Ledger 0 : {} + * Ledger 1 : {1,4,5} + * Ledger 2 : {1,2,4,5,10} + * .... + * + * Ledgers are immutable value types. All ledgers with the same sequence + * number, transactions, close time, etc. will have the same ledger ID. The + * LedgerOracle class below manages ID assignments for a simulation and is the + * only way to close and create a new ledger. Since the parent ledger ID is + * part of type, this also means ledgers with distinct histories will have + * distinct ids, even if they have the same set of transactions, sequence + * number and close time. + */ class Ledger { friend class LedgerOracle; @@ -74,21 +75,31 @@ private: // Resolution used to determine close time NetClock::duration closeTimeResolution = kLedgerDefaultTimeResolution; - //! When the ledger closed (up to closeTimeResolution) + /** + * When the ledger closed (up to closeTimeResolution) + */ NetClock::time_point closeTime; - //! Whether consensus agreed on the close time + /** + * Whether consensus agreed on the close time + */ bool closeTimeAgree = true; - //! Parent ledger id + /** + * Parent ledger id + */ ID parentID{0}; - //! Parent ledger close time + /** + * Parent ledger close time + */ NetClock::time_point parentCloseTime; - //! IDs of this ledgers ancestors. Since each ledger already has unique - //! ancestors based on the parentID, this member is not needed for any - //! of the operators below. + /** + * IDs of this ledgers ancestors. Since each ledger already has unique + * ancestors based on the parentID, this member is not needed for any + * of the operators below. + */ std::vector ancestors; [[nodiscard]] auto @@ -199,16 +210,20 @@ public: return instance_->txs; } - /** Determine whether ancestor is really an ancestor of this ledger */ + /** + * Determine whether ancestor is really an ancestor of this ledger + */ [[nodiscard]] bool isAncestor(Ledger const& ancestor) const; - /** Return the id of the ancestor with the given seq (if exists/known) + /** + * Return the id of the ancestor with the given seq (if exists/known) */ ID operator[](Seq seq) const; - /** Return the sequence number of the first mismatching ancestor + /** + * Return the sequence number of the first mismatching ancestor */ friend Ledger::Seq mismatch(Ledger const& a, Ledger const& o); @@ -227,7 +242,8 @@ private: Instance const* instance_; }; -/** Oracle maintaining unique ledgers for a simulation. +/** + * Oracle maintaining unique ledgers for a simulation. */ class LedgerOracle { @@ -246,18 +262,21 @@ class LedgerOracle public: LedgerOracle(); - /** Find the ledger with the given ID */ + /** + * Find the ledger with the given ID + */ [[nodiscard]] std::optional lookup(Ledger::ID const& id) const; - /** Accept the given txs and generate a new ledger - - @param curr The current ledger - @param txs The transactions to apply to the current ledger - @param closeTimeResolution Resolution used in determining close time - @param consensusCloseTime The consensus agreed close time, no valid time - if 0 - */ + /** + * Accept the given txs and generate a new ledger + * + * @param curr The current ledger + * @param txs The transactions to apply to the current ledger + * @param closeTimeResolution Resolution used in determining close time + * @param consensusCloseTime The consensus agreed close time, no valid time + * if 0 + */ Ledger accept( Ledger const& curr, @@ -272,38 +291,39 @@ public: return accept(curr, TxSetType{tx}, curr.closeTimeResolution(), curr.closeTime() + 1s); } - /** Determine the number of distinct branches for the set of ledgers. - - Ledgers A and B are on different branches if A != B, A is not an - ancestor of B and B is not an ancestor of A, e.g. - - /--> A - O - \--> B - */ + /** + * Determine the number of distinct branches for the set of ledgers. + * + * Ledgers A and B are on different branches if A != B, A is not an + * ancestor of B and B is not an ancestor of A, e.g. + * + * /--> A + * O + * \--> B + */ static std::size_t branches(std::set const& ledgers); }; -/** Helper for writing unit tests with controlled ledger histories. - - This class allows clients to refer to distinct ledgers as strings, where - each character in the string indicates a unique ledger. It enforces the - uniqueness at runtime, but this simplifies creation of alternate ledger - histories, e.g. - - HistoryHelper hh; - hh["a"] - hh["ab"] - hh["ac"] - hh["abd"] - - Creates a history like - b - d - / - a - c - -*/ +/** + * Helper for writing unit tests with controlled ledger histories. + * + * This class allows clients to refer to distinct ledgers as strings, where + * each character in the string indicates a unique ledger. It enforces the + * uniqueness at runtime, but this simplifies creation of alternate ledger + * histories, e.g. + * + * HistoryHelper hh; + * hh["a"] + * hh["ab"] + * hh["ac"] + * hh["abd"] + * + * Creates a history like + * b - d + * / + * a - c + */ struct LedgerHistoryHelper { LedgerOracle oracle; @@ -316,11 +336,12 @@ struct LedgerHistoryHelper ledgers[""] = Ledger{Ledger::MakeGenesis{}}; } - /** Get or create the ledger with the given string history. - - Creates any necessary intermediate ledgers, but asserts if - a letter is re-used (e.g. "abc" then "adc" would assert) - */ + /** + * Get or create the ledger with the given string history. + * + * Creates any necessary intermediate ledgers, but asserts if + * a letter is re-used (e.g. "abc" then "adc" would assert) + */ Ledger const& operator[](std::string const& s) { diff --git a/src/test/csf/random.h b/src/test/csf/random.h index aa20c73a10..f8df253642 100644 --- a/src/test/csf/random.h +++ b/src/test/csf/random.h @@ -8,15 +8,16 @@ namespace xrpl::test::csf { -/** Return a randomly shuffled copy of vector based on weights w. - - @param v The set of values - @param w The set of weights of each value - @param g A pseudo-random number generator - @return A vector with entries randomly sampled without replacement - from the original vector based on the provided weights. - I.e. res[0] comes from sample v[i] with weight w[i]/suk_ w[k] -*/ +/** + * Return a randomly shuffled copy of vector based on weights w. + * + * @param v The set of values + * @param w The set of weights of each value + * @param g A pseudo-random number generator + * @return A vector with entries randomly sampled without replacement + * from the original vector based on the provided weights. + * I.e. res[0] comes from sample v[i] with weight w[i]/suk_ w[k] + */ template std::vector randomWeightedShuffle(std::vector v, std::vector w, G& g) @@ -34,14 +35,15 @@ randomWeightedShuffle(std::vector v, std::vector w, G& g) return v; } -/** Generate a vector of random samples - - @param size the size of the sample - @param dist the distribution to sample - @param g the pseudo-random number generator - - @return vector of samples -*/ +/** + * Generate a vector of random samples + * + * @param size the size of the sample + * @param dist the distribution to sample + * @param g the pseudo-random number generator + * + * @return vector of samples + */ template std::vector sample(std::size_t size, RandomNumberDistribution dist, Generator& g) @@ -51,13 +53,14 @@ sample(std::size_t size, RandomNumberDistribution dist, Generator& g) return res; } -/** Invocable that returns random samples from a range according to a discrete - distribution - - Given a pair of random access iterators begin and end, each call to the - instance of Selector returns a random entry in the range (begin,end) - according to the weights provided at construction. -*/ +/** + * Invocable that returns random samples from a range according to a discrete + * distribution + * + * Given a pair of random access iterators begin and end, each call to the + * instance of Selector returns a random entry in the range (begin,end) + * according to the weights provided at construction. + */ template class Selector { @@ -66,12 +69,13 @@ class Selector Generator g_; public: - /** Constructor - @param first Random access iterator to the start of the range - @param last Random access iterator to the end of the range - @param w Vector of weights of size list-first - @param g the pseudo-random number generator - */ + /** + * Constructor + * @param first Random access iterator to the start of the range + * @param last Random access iterator to the end of the range + * @param w Vector of weights of size list-first + * @param g the pseudo-random number generator + */ Selector(RAIter first, RAIter last, std::vector const& w, Generator& g) : first_{first}, last_{last}, dd_{w.begin(), w.end()}, g_{g} { @@ -100,7 +104,8 @@ makeSelector(Iter first, Iter last, std::vector const& w, Generator& g) //------------------------------------------------------------------------------ // Additional distributions of interest not defined in -/** Constant "distribution" that always returns the same value +/** + * Constant "distribution" that always returns the same value */ class ConstantDistribution { @@ -119,11 +124,12 @@ public: } }; -/** Power-law distribution with PDF - - P(x) = (x/xmin)^-a - - for a >= 1 and xmin >= 1 +/** + * Power-law distribution with PDF + * + * P(x) = (x/xmin)^-a + * + * for a >= 1 and xmin >= 1 */ class PowerLawDistribution { diff --git a/src/test/csf/submitters.h b/src/test/csf/submitters.h index a71c849fb7..160d0bcd9f 100644 --- a/src/test/csf/submitters.h +++ b/src/test/csf/submitters.h @@ -13,7 +13,9 @@ namespace xrpl::test::csf { // Submitters are classes for simulating submission of transactions to the // network -/** Represents rate as a count/duration */ +/** + * Represents rate as a count/duration + */ struct Rate { std::size_t count; @@ -26,23 +28,24 @@ struct Rate } }; -/** Submits transactions to a specified peer - - Submits successive transactions beginning at start, then spaced according - to successive calls of distribution(), until stop. - - @tparam Distribution is a `UniformRandomBitGenerator` from the STL that - is used by random distributions to generate random samples - @tparam Generator is an object with member - - T operator()(Generator &g) - - which generates the delay T in SimDuration units to the next - transaction. For the current definition of SimDuration, this is - currently the number of nanoseconds. Submitter internally casts - arithmetic T to SimDuration::rep units to allow using standard - library distributions as a Distribution. -*/ +/** + * Submits transactions to a specified peer + * + * Submits successive transactions beginning at start, then spaced according + * to successive calls of distribution(), until stop. + * + * @tparam Distribution is a `UniformRandomBitGenerator` from the STL that + * is used by random distributions to generate random samples + * @tparam Generator is an object with member + * + * T operator()(Generator &g) + * + * which generates the delay T in SimDuration units to the next + * transaction. For the current definition of SimDuration, this is + * currently the number of nanoseconds. Submitter internally casts + * arithmetic T to SimDuration::rep units to allow using standard + * library distributions as a Distribution. + */ template class Submitter { diff --git a/src/test/csf/timers.h b/src/test/csf/timers.h index 2f86fe7729..4f13b21b25 100644 --- a/src/test/csf/timers.h +++ b/src/test/csf/timers.h @@ -12,7 +12,8 @@ namespace xrpl::test::csf { // Timers are classes that schedule repeated events and are mostly independent // of simulation-specific details. -/** Gives heartbeat of simulation to signal simulation progression +/** + * Gives heartbeat of simulation to signal simulation progression */ class HeartbeatTimer { diff --git a/src/test/jtx/AMM.h b/src/test/jtx/AMM.h index 605f15f812..68b6d9f745 100644 --- a/src/test/jtx/AMM.h +++ b/src/test/jtx/AMM.h @@ -131,7 +131,8 @@ struct ClawbackArg std::optional err = std::nullopt; }; -/** Convenience class to test AMM functionality. +/** + * Convenience class to test AMM functionality. */ class AMM { @@ -187,7 +188,8 @@ public: STAmount const& asset2, std::uint16_t const& tfee); - /** Send amm_info RPC command + /** + * Send amm_info RPC command */ [[nodiscard]] json::Value ammRpcInfo( @@ -209,7 +211,8 @@ public: bool ignoreParams, unsigned apiVersion) const; - /** Verify the AMM balances. + /** + * Verify the AMM balances. */ [[nodiscard]] bool expectBalances( @@ -218,7 +221,8 @@ public: IOUAmount const& lpt, std::optional const& account = std::nullopt) const; - /** Get AMM balances for the token pair. + /** + * Get AMM balances for the token pair. */ [[nodiscard]] std::tuple balances( diff --git a/src/test/jtx/AMMTest.h b/src/test/jtx/AMMTest.h index 971cc5db84..9385313225 100644 --- a/src/test/jtx/AMMTest.h +++ b/src/test/jtx/AMMTest.h @@ -103,7 +103,8 @@ public: } protected: - /** testAMM() funds 30,000XRP and 30,000IOU + /** + * testAMM() funds 30,000XRP and 30,000IOU * for each non-XRP asset to Alice and Carol */ void diff --git a/src/test/jtx/AbstractClient.h b/src/test/jtx/AbstractClient.h index 7c7107ca79..f9d8de0768 100644 --- a/src/test/jtx/AbstractClient.h +++ b/src/test/jtx/AbstractClient.h @@ -20,21 +20,24 @@ public: AbstractClient& operator=(AbstractClient const&) = delete; - /** Submit a command synchronously. - - The arguments to the function and the returned JSON - are in a normalized format, the same whether the client - is using the JSON-RPC over HTTP/S or WebSocket transport. - - @param cmd The command to execute - @param params json::Value of null or object type - with zero or more key/value pairs. - @return The server response in normalized format. - */ + /** + * Submit a command synchronously. + * + * The arguments to the function and the returned JSON + * are in a normalized format, the same whether the client + * is using the JSON-RPC over HTTP/S or WebSocket transport. + * + * @param cmd The command to execute + * @param params json::Value of null or object type + * with zero or more key/value pairs. + * @return The server response in normalized format. + */ virtual json::Value invoke(std::string const& cmd, json::Value const& params = {}) = 0; - /// Get RPC 1.0 or RPC 2.0 + /** + * Get RPC 1.0 or RPC 2.0 + */ [[nodiscard]] virtual unsigned version() const = 0; }; diff --git a/src/test/jtx/Account.h b/src/test/jtx/Account.h index 264a39f08b..60d07beebd 100644 --- a/src/test/jtx/Account.h +++ b/src/test/jtx/Account.h @@ -14,7 +14,9 @@ namespace xrpl::test::jtx { class IOU; -/** Immutable cryptographic account descriptor. */ +/** + * Immutable cryptographic account descriptor. + */ class Account { private: @@ -24,7 +26,9 @@ private: }; public: - /** The master account. */ + /** + * The master account. + */ static Account const kMaster; Account() = delete; @@ -35,7 +39,9 @@ public: Account& operator=(Account&&) = default; - /** Create an account from a simple string name. */ + /** + * Create an account from a simple string name. + */ /** @{ */ Account(std::string name, KeyType type = KeyType::Secp256k1); @@ -50,63 +56,79 @@ public: /** @} */ - /** Create an Account from an account ID. Should only be used when the - * secret key is unavailable, such as for pseudo-accounts. */ + /** + * Create an Account from an account ID. Should only be used when the + * secret key is unavailable, such as for pseudo-accounts. + */ explicit Account(std::string name, AccountID const& id); enum class AcctStringType { Base58Seed, Other }; - /** Create an account from a base58 seed string. Throws on invalid seed. */ + /** + * Create an account from a base58 seed string. Throws on invalid seed. + */ Account(AcctStringType stringType, std::string base58SeedStr); - /** Return the name */ + /** + * Return the name + */ [[nodiscard]] std::string const& name() const { return name_; } - /** Return the public key. */ + /** + * Return the public key. + */ [[nodiscard]] PublicKey const& pk() const { return pk_; } - /** Return the secret key. */ + /** + * Return the secret key. + */ [[nodiscard]] SecretKey const& sk() const { return sk_; } - /** Returns the Account ID. - - The Account ID is the uint160 hash of the public key. - */ + /** + * Returns the Account ID. + * + * The Account ID is the uint160 hash of the public key. + */ [[nodiscard]] AccountID id() const { return id_; } - /** Returns the human readable public key. */ + /** + * Returns the human readable public key. + */ [[nodiscard]] std::string const& human() const { return human_; } - /** Implicit conversion to AccountID. - - This allows passing an Account - where an AccountID is expected. - */ + /** + * Implicit conversion to AccountID. + * + * This allows passing an Account + * where an AccountID is expected. + */ operator AccountID() const { return id_; } - /** Returns an IOU for the specified gateway currency. */ + /** + * Returns an IOU for the specified gateway currency. + */ IOU operator[](std::string const& s) const; diff --git a/src/test/jtx/CheckMessageLogs.h b/src/test/jtx/CheckMessageLogs.h index fc46f41671..24b2c24a73 100644 --- a/src/test/jtx/CheckMessageLogs.h +++ b/src/test/jtx/CheckMessageLogs.h @@ -9,7 +9,8 @@ namespace xrpl::test { -/** Log manager that searches for a specific message substring +/** + * Log manager that searches for a specific message substring */ class CheckMessageLogs : public Logs { @@ -41,12 +42,13 @@ class CheckMessageLogs : public Logs }; public: - /** Constructor - - @param msg The message string to search for - @param pFound Pointer to the variable to set to true if the message is - found - */ + /** + * Constructor + * + * @param msg The message string to search for + * @param pFound Pointer to the variable to set to true if the message is + * found + */ CheckMessageLogs(std::string msg, bool* pFound) : Logs{beast::Severity::Debug}, msg_{std::move(msg)}, pFound_{pFound} { diff --git a/src/test/jtx/Env.h b/src/test/jtx/Env.h index 04cbce953a..7e22cdd571 100644 --- a/src/test/jtx/Env.h +++ b/src/test/jtx/Env.h @@ -55,14 +55,15 @@ namespace xrpl::test::jtx { -/** Wrapper that captures std::source_location when implicitly constructed. - This solves the problem of combining std::source_location with variadic - templates. The std::source_location default argument is evaluated at the - call site when the wrapper is constructed via implicit conversion. - - This is a template struct that holds the value directly, allowing implicit - conversion without template argument deduction issues via CTAD. -*/ +/** + * Wrapper that captures std::source_location when implicitly constructed. + * This solves the problem of combining std::source_location with variadic + * templates. The std::source_location default argument is evaluated at the + * call site when the wrapper is constructed via implicit conversion. + * + * This is a template struct that holds the value directly, allowing implicit + * conversion without template argument deduction issues via CTAD. + */ template struct WithSourceLocation { @@ -77,7 +78,9 @@ struct WithSourceLocation } }; -/** Designate accounts as no-ripple in Env::fund */ +/** + * Designate accounts as no-ripple in Env::fund + */ template std::array noripple(Account const& account, Args const&... args) @@ -151,7 +154,9 @@ public: //------------------------------------------------------------------------------ -/** A transaction testing environment. */ +/** + * A transaction testing environment. + */ class Env { public: @@ -159,7 +164,9 @@ public: Account const& master = Account::kMaster; - /// Used by parseResult() and postConditions() + /** + * Used by parseResult() and postConditions() + */ struct ParsedResult { std::optional ter; @@ -308,11 +315,12 @@ public: return *bundle_.timeKeeper; } - /** Returns the current network time - - @note This is manually advanced when ledgers - close or by callers. - */ + /** + * Returns the current network time + * + * @note This is manually advanced when ledgers + * close or by callers. + */ NetClock::time_point // NOLINTNEXTLINE(readability-make-member-function-const) now() @@ -320,7 +328,9 @@ public: return timeKeeper().now(); } - /** Returns the connected client. */ + /** + * Returns the connected client. + */ AbstractClient& // NOLINTNEXTLINE(readability-make-member-function-const) client() @@ -328,11 +338,12 @@ public: return *bundle_.client; } - /** Execute an RPC command. - - The command is examined and used to build - the correct JSON as per the arguments. - */ + /** + * Execute an RPC command. + * + * The command is examined and used to build + * the correct JSON as per the arguments. + */ template json::Value rpc(unsigned apiVersion, @@ -354,61 +365,64 @@ public: json::Value rpc(std::string const& cmd, Args&&... args); - /** Returns the current ledger. - - This is a non-modifiable snapshot of the - open ledger at the moment of the call. - Transactions applied after the call to open() - will not be visible. - - */ + /** + * Returns the current ledger. + * + * This is a non-modifiable snapshot of the + * open ledger at the moment of the call. + * Transactions applied after the call to open() + * will not be visible. + */ [[nodiscard]] std::shared_ptr current() const { return app().getOpenLedger().current(); } - /** Returns the last closed ledger. - - The open ledger is built on top of the - last closed ledger. When the open ledger - is closed, it becomes the new closed ledger - and a new open ledger takes its place. - */ + /** + * Returns the last closed ledger. + * + * The open ledger is built on top of the + * last closed ledger. When the open ledger + * is closed, it becomes the new closed ledger + * and a new open ledger takes its place. + */ std::shared_ptr closed(); - /** Close and advance the ledger. - - The resulting close time will be different and - greater than the previous close time, and at or - after the passed-in close time. - - Effects: - - Creates a new closed ledger from the last - closed ledger. - - All transactions that made it into the open - ledger are applied to the closed ledger. - - The Application network time is set to - the close time of the resulting ledger. - - @return true if no error, false if error - */ + /** + * Close and advance the ledger. + * + * The resulting close time will be different and + * greater than the previous close time, and at or + * after the passed-in close time. + * + * Effects: + * + * Creates a new closed ledger from the last + * closed ledger. + * + * All transactions that made it into the open + * ledger are applied to the closed ledger. + * + * The Application network time is set to + * the close time of the resulting ledger. + * + * @return true if no error, false if error + */ bool close( NetClock::time_point closeTime, std::optional consensusDelay = std::nullopt); - /** Close and advance the ledger. - - The time is calculated as the duration from - the previous ledger closing time. - - @return true if no error, false if error - */ + /** + * Close and advance the ledger. + * + * The time is calculated as the duration from + * the previous ledger closing time. + * + * @return true if no error, false if error + */ template bool close(std::chrono::duration const& elapsed) @@ -417,13 +431,14 @@ public: return close(now() + elapsed); } - /** Close and advance the ledger. - - The time is calculated as five seconds from - the previous ledger closing time. - - @return true if no error, false if error - */ + /** + * Close and advance the ledger. + * + * The time is calculated as five seconds from + * the previous ledger closing time. + * + * @return true if no error, false if error + */ bool close() { @@ -431,34 +446,35 @@ public: return close(std::chrono::seconds(5)); } - /** Close and advance the ledger, then synchronize with the server's - io_context to ensure all async operations initiated by the close have - been started. - - This function performs the same ledger close as close(), but additionally - ensures that all tasks posted to the server's io_context (such as - WebSocket subscription message sends) have been initiated before returning. - - What it guarantees: - - All async operations posted before syncClose() have been STARTED - - For WebSocket sends: async_write_some() has been called - - The actual I/O completion may still be pending (async) - - What it does NOT guarantee: - - Async operations have COMPLETED - - WebSocket messages have been received by clients - - However, for localhost connections, the remaining latency is typically - microseconds, making tests reliable - - Use this instead of close() when: - - Test code immediately checks for subscription messages - - Race conditions between test and worker threads must be avoided - - Deterministic test behavior is required - - @param timeout Maximum time to wait for the barrier task to execute - @return true if close succeeded and barrier executed within timeout, - false otherwise - */ + /** + * Close and advance the ledger, then synchronize with the server's + * io_context to ensure all async operations initiated by the close have + * been started. + * + * This function performs the same ledger close as close(), but additionally + * ensures that all tasks posted to the server's io_context (such as + * WebSocket subscription message sends) have been initiated before returning. + * + * What it guarantees: + * - All async operations posted before syncClose() have been STARTED + * - For WebSocket sends: async_write_some() has been called + * - The actual I/O completion may still be pending (async) + * + * What it does NOT guarantee: + * - Async operations have COMPLETED + * - WebSocket messages have been received by clients + * - However, for localhost connections, the remaining latency is typically + * microseconds, making tests reliable + * + * Use this instead of close() when: + * - Test code immediately checks for subscription messages + * - Race conditions between test and worker threads must be avoided + * - Deterministic test behavior is required + * + * @param timeout Maximum time to wait for the barrier task to execute + * @return true if close succeeded and barrier executed within timeout, + * false otherwise + */ [[nodiscard]] bool syncClose(std::chrono::steady_clock::duration timeout = std::chrono::seconds{1}) { @@ -473,16 +489,19 @@ public: return result && status == std::future_status::ready; } - /** Turn on JSON tracing. - With no arguments, trace all - */ + /** + * Turn on JSON tracing. + * With no arguments, trace all + */ void trace(int howMany = -1) { trace_ = howMany; } - /** Turn off JSON tracing. */ + /** + * Turn off JSON tracing. + */ void notrace() { @@ -495,7 +514,9 @@ public: parseFailureExpected_ = b; } - /** Turn off signature checks. */ + /** + * Turn off signature checks. + */ void disableSigs() { @@ -516,11 +537,15 @@ public: return retries_; } - /** Associate AccountID with account. */ + /** + * Associate AccountID with account. + */ void memoize(Account const& account); - /** Returns the Account given the AccountID. */ + /** + * Returns the Account given the AccountID. + */ /** @{ */ [[nodiscard]] Account const& lookup(AccountID const& id) const; @@ -529,69 +554,84 @@ public: lookup(std::string const& base58ID) const; /** @} */ - /** Returns the XRP balance on an account. - Returns 0 if the account does not exist. - */ + /** + * Returns the XRP balance on an account. + * Returns 0 if the account does not exist. + */ [[nodiscard]] PrettyAmount balance(Account const& account) const; - /** Returns the next sequence number on account. - Exceptions: - Throws if the account does not exist - */ + /** + * Returns the next sequence number on account. + * + * @throws if the account does not exist + */ [[nodiscard]] std::uint32_t seq(Account const& account) const; - /** Return the balance on an account. - Returns 0 if the trust line does not exist. - */ + /** + * Return the balance on an account. + * Returns 0 if the trust line does not exist. + */ // VFALCO NOTE This should return a unit-less amount [[nodiscard]] PrettyAmount balance(Account const& account, Asset const& asset) const; - /** Returns the IOU limit on an account. - Returns 0 if the trust line does not exist. - */ + /** + * Returns the IOU limit on an account. + * Returns 0 if the trust line does not exist. + */ [[nodiscard]] PrettyAmount limit(Account const& account, Issue const& issue) const; - /** Return the number of objects owned by an account. + /** + * Return the number of objects owned by an account. * Returns 0 if the account does not exist. */ [[nodiscard]] std::uint32_t ownerCount(Account const& account) const; - /** Return the number of sponsored objects owned by an account. - * Throws if the account does not exist. + /** + * Return the number of sponsored objects owned by an account. + * + * @throws if the account does not exist. */ [[nodiscard]] std::uint32_t sponsoredOwnerCount(Account const& account) const; - /** Return the number of sponsoring objects owned by an account. - * Throws if the account does not exist. + /** + * Return the number of sponsoring objects owned by an account. + * + * @throws if the account does not exist. */ [[nodiscard]] std::uint32_t sponsoringOwnerCount(Account const& account) const; - /** Return the number of sponsoring accounts owned by an account. - * Throws if the account does not exist. + /** + * Return the number of sponsoring accounts owned by an account. + * + * @throws if the account does not exist. */ [[nodiscard]] std::uint32_t sponsoringAccountCount(Account const& account) const; - /** Return an account root. - @return empty if the account does not exist. - */ + /** + * Return an account root. + * @return empty if the account does not exist. + */ [[nodiscard]] SLE::const_pointer le(Account const& account) const; - /** Return a ledger entry. - @return empty if the ledger entry does not exist - */ + /** + * Return a ledger entry. + * @return empty if the ledger entry does not exist + */ [[nodiscard]] SLE::const_pointer le(Keylet const& k) const; - /** Create a JTx from parameters. */ + /** + * Create a JTx from parameters. + */ template JTx jt(JsonValue&& jv, FN const&... fN) @@ -603,7 +643,9 @@ public: return jt; } - /** Create a JTx from parameters. */ + /** + * Create a JTx from parameters. + */ template JTx jtnofill(JsonValue&& jv, FN const&... fN) @@ -615,9 +657,10 @@ public: return jt; } - /** Create JSON from parameters. - This will apply funclets and autofill. - */ + /** + * Create JSON from parameters. + * This will apply funclets and autofill. + */ template json::Value json(JsonValue&& jv, FN const&... fN) @@ -626,11 +669,12 @@ public: return std::move(tj.jv); } - /** Check a set of requirements. - - The requirements are formed - from condition functors. - */ + /** + * Check a set of requirements. + * + * The requirements are formed + * from condition functors. + */ template void require(Args const&... args) @@ -638,29 +682,33 @@ public: jtx::required(args...)(*this); } - /** Gets the TER result and `didApply` flag from a RPC Json result object. + /** + * Gets the TER result and `didApply` flag from a RPC Json result object. */ static ParsedResult parseResult(json::Value const& jr); - /** Submit an existing JTx. - This calls postconditions. - */ + /** + * Submit an existing JTx. + * This calls postconditions. + */ virtual void submit(JTx const& jt, std::source_location const& loc = std::source_location::current()); - /** Use the submit RPC command with a provided JTx object. - This calls postconditions. - */ + /** + * Use the submit RPC command with a provided JTx object. + * This calls postconditions. + */ void signAndSubmit( JTx const& jt, json::Value params = json::ValueType::Null, std::source_location const& loc = std::source_location::current()); - /** Check expected postconditions - of JTx submission. - */ + /** + * Check expected postconditions + * of JTx submission. + */ void postconditions( JTx const& jt, @@ -668,7 +716,9 @@ public: json::Value const& jr = json::Value(), std::source_location const& loc = std::source_location::current()); - /** Apply funclets and submit. */ + /** + * Apply funclets and submit. + */ /** @{ */ template Env& @@ -701,38 +751,42 @@ public: } /** @} */ - /** Return the TER for the last JTx. */ + /** + * Return the TER for the last JTx. + */ [[nodiscard]] TER ter() const { return ter_; } - /** Return metadata for the last JTx. + /** + * Return metadata for the last JTx. * - * NOTE: this has a side effect of closing the open ledger. - * The ledger will only be closed if it includes transactions. + * NOTE: this has a side effect of closing the open ledger. + * The ledger will only be closed if it includes transactions. * - * Effects: + * Effects: * - * The open ledger is closed as if by a call - * to close(). The metadata for the last - * transaction ID, if any, is returned. + * The open ledger is closed as if by a call + * to close(). The metadata for the last + * transaction ID, if any, is returned. */ std::shared_ptr meta(); - /** Return the tx data for the last JTx. - - Effects: - - The tx data for the last transaction - ID, if any, is returned. No side - effects. - - @note Only necessary for JTx submitted - with via sign-and-submit method. - */ + /** + * Return the tx data for the last JTx. + * + * Effects: + * + * The tx data for the last transaction + * ID, if any, is returned. No side + * effects. + * + * @note Only necessary for JTx submitted + * with via sign-and-submit method. + */ [[nodiscard]] std::shared_ptr tx() const; @@ -767,32 +821,33 @@ private: } public: - /** Create a new account with some XRP. - - These convenience functions are for easy set-up - of the environment, they bypass fee, seq, and sig - settings. The XRP is transferred from the master - account. - - Preconditions: - The account must not already exist - - Effects: - The asfDefaultRipple on the account is set, - and the sequence number is incremented, unless - the account is wrapped with a call to noripple. - - The account's XRP balance is set to amount. - - Generates a test that the balance is set. - - @param amount The amount of XRP to transfer to - each account. - - @param args A heterogeneous list of accounts to fund - or calls to noripple with lists of accounts - to fund. - */ + /** + * Create a new account with some XRP. + * + * These convenience functions are for easy set-up + * of the environment, they bypass fee, seq, and sig + * settings. The XRP is transferred from the master + * account. + * + * Preconditions: + * The account must not already exist + * + * Effects: + * The asfDefaultRipple on the account is set, + * and the sequence number is incremented, unless + * the account is wrapped with a call to noripple. + * + * The account's XRP balance is set to amount. + * + * Generates a test that the balance is set. + * + * @param amount The amount of XRP to transfer to + * each account. + * + * @param args A heterogeneous list of accounts to fund + * or calls to noripple with lists of accounts + * to fund. + */ template void fund(STAmount const& amount, Arg const& arg, Args const&... args) @@ -802,23 +857,24 @@ public: fund(amount, args...); } - /** Establish trust lines. - - These convenience functions are for easy set-up - of the environment, they bypass fee, seq, and sig - settings. - - Preconditions: - The account must already exist - - Effects: - A trust line is added for the account. - The account's sequence number is incremented. - The account is refunded for the transaction fee - to set the trust line. - - The refund comes from the master account. - */ + /** + * Establish trust lines. + * + * These convenience functions are for easy set-up + * of the environment, they bypass fee, seq, and sig + * settings. + * + * Preconditions: + * The account must already exist + * + * Effects: + * A trust line is added for the account. + * The account's sequence number is incremented. + * The account is refunded for the transaction fee + * to set the trust line. + * + * The refund comes from the master account. + */ /** @{ */ void trust(STAmount const& amount, Account const& account); @@ -832,10 +888,11 @@ public: } /** @} */ - /** Create a STTx from a JTx without sanitizing - Use to inject bogus values into test transactions by first - editing the JSON. - */ + /** + * Create a STTx from a JTx without sanitizing + * Use to inject bogus values into test transactions by first + * editing the JSON. + */ std::shared_ptr ust(JTx const& jt); @@ -859,13 +916,14 @@ protected: virtual void autofill(JTx& jt); - /** Create a STTx from a JTx - The framework requires that JSON is valid. - On a parse error, the JSON is logged and - an exception thrown. - Throws: - ParseError - */ + /** + * Create a STTx from a JTx + * The framework requires that JSON is valid. + * On a parse error, the JSON is logged and + * an exception thrown. + * + * @throws ParseError + */ std::shared_ptr st(JTx const& jt); diff --git a/src/test/jtx/Env_ss.h b/src/test/jtx/Env_ss.h index 16e1cdbc82..ca298f0069 100644 --- a/src/test/jtx/Env_ss.h +++ b/src/test/jtx/Env_ss.h @@ -10,10 +10,11 @@ namespace xrpl::test::jtx { -/** A transaction testing environment wrapper. - Transactions submitted in sign-and-submit mode - by default. -*/ +/** + * A transaction testing environment wrapper. + * Transactions submitted in sign-and-submit mode + * by default. + */ class EnvSs { private: diff --git a/src/test/jtx/JSONRPCClient.h b/src/test/jtx/JSONRPCClient.h index 2864cbbdc8..10e567e91f 100644 --- a/src/test/jtx/JSONRPCClient.h +++ b/src/test/jtx/JSONRPCClient.h @@ -8,7 +8,9 @@ namespace xrpl::test { -/** Returns a client using JSON-RPC over HTTP/S. */ +/** + * Returns a client using JSON-RPC over HTTP/S. + */ std::unique_ptr makeJSONRPCClient(Config const& cfg, unsigned rpcVersion = 2); diff --git a/src/test/jtx/JTx.h b/src/test/jtx/JTx.h index ed585ebb6e..794334bec7 100644 --- a/src/test/jtx/JTx.h +++ b/src/test/jtx/JTx.h @@ -19,9 +19,10 @@ namespace xrpl::test::jtx { class Env; -/** Execution context for applying a JSON transaction. - This augments the transaction with various settings. -*/ +/** + * Execution context for applying a JSON transaction. + * This augments the transaction with various settings. + */ struct JTx { json::Value jv; @@ -63,10 +64,11 @@ struct JTx return jv[key]; } - /** Return a property if it exists - - @return nullptr if the Prop does not exist - */ + /** + * Return a property if it exists + * + * @return nullptr if the Prop does not exist + */ /** @{ */ template Prop* @@ -93,10 +95,11 @@ struct JTx } /** @} */ - /** Set a property - If the property already exists, - it is replaced. - */ + /** + * Set a property + * If the property already exists, + * it is replaced. + */ /** @{ */ void set(std::unique_ptr p) diff --git a/src/test/jtx/Oracle.h b/src/test/jtx/Oracle.h index 2c296ba705..d0fe8104e5 100644 --- a/src/test/jtx/Oracle.h +++ b/src/test/jtx/Oracle.h @@ -102,7 +102,8 @@ struct RemoveArg // validation {close-maxLastUpdateTimeDelta,close+maxLastUpdateTimeDelta}. static constexpr std::chrono::seconds kTestStartTime = kEpochOffset + std::chrono::seconds(10'000); -/** Oracle class facilitates unit-testing of the Price Oracle feature. +/** + * Oracle class facilitates unit-testing of the Price Oracle feature. * It defines functions to create, update, and delete the Oracle object, * to query for various states, and to call APIs. */ diff --git a/src/test/jtx/PathSet.h b/src/test/jtx/PathSet.h index fa3f0d40f9..42d391c46f 100644 --- a/src/test/jtx/PathSet.h +++ b/src/test/jtx/PathSet.h @@ -18,7 +18,8 @@ namespace xrpl::test { -/** Count offer +/** + * Count offer */ inline std::size_t countOffers( @@ -52,7 +53,8 @@ countOffers( return count; } -/** An offer exists +/** + * An offer exists */ inline bool isOffer( @@ -64,7 +66,8 @@ isOffer( return countOffers(env, account, takerPays, takerGets) > 0; } -/** An offer exists +/** + * An offer exists */ inline bool isOffer(jtx::Env& env, jtx::Account const& account, Asset const& takerPays, Asset const& takerGets) diff --git a/src/test/jtx/TestHelpers.h b/src/test/jtx/TestHelpers.h index 4c18d7343c..c4991c4ff3 100644 --- a/src/test/jtx/TestHelpers.h +++ b/src/test/jtx/TestHelpers.h @@ -51,11 +51,12 @@ namespace xrpl::test::jtx { -/** Generic helper class for helper classes that set a field on a JTx. - - Not every helper will be able to use this because of conversions and other - issues, but for classes where it's straightforward, this can simplify things. -*/ +/** + * Generic helper class for helper classes that set a field on a JTx. + * + * Not every helper will be able to use this because of conversions and other + * issues, but for classes where it's straightforward, this can simplify things. + */ template < class SField, // NOLINTNEXTLINE(readability-redundant-typename): typename required by MSVC @@ -306,7 +307,8 @@ using valueUnitWrapper = JTxFieldWrapper using simpleField = JTxFieldWrapper>; -/** General field definitions, or fields used in multiple transaction namespaces +/** + * General field definitions, or fields used in multiple transaction namespaces */ auto const kData = JTxFieldWrapper(sfData); @@ -753,7 +755,9 @@ equal(Strand const& strand, Args&&... args) /***************************************************************/ namespace check { -/** Create a check. */ +/** + * Create a check. + */ template requires std::is_same_v json::Value @@ -973,7 +977,9 @@ pay(AccountID const& account, } // namespace loan -/** Set Expiration on a JTx. */ +/** + * Set Expiration on a JTx. + */ class Expiration { private: @@ -992,7 +998,9 @@ public: } }; -/** Set SourceTag on a JTx. */ +/** + * Set SourceTag on a JTx. + */ class SourceTag { private: @@ -1010,7 +1018,9 @@ public: } }; -/** Set DestinationTag on a JTx. */ +/** + * Set DestinationTag on a JTx. + */ class DestTag { private: diff --git a/src/test/jtx/WSClient.h b/src/test/jtx/WSClient.h index 4aa41e072c..ee5a46e9ae 100644 --- a/src/test/jtx/WSClient.h +++ b/src/test/jtx/WSClient.h @@ -18,18 +18,24 @@ namespace xrpl::test { class WSClient : public AbstractClient { public: - /** Retrieve a message. */ + /** + * Retrieve a message. + */ virtual std::optional getMsg(std::chrono::milliseconds const& timeout = std::chrono::milliseconds{0}) = 0; - /** Retrieve a message that meets the predicate criteria. */ + /** + * Retrieve a message that meets the predicate criteria. + */ virtual std::optional findMsg( std::chrono::milliseconds const& timeout, std::function pred) = 0; }; -/** Returns a client operating through WebSockets/S. */ +/** + * Returns a client operating through WebSockets/S. + */ std::unique_ptr makeWSClient( Config const& cfg, diff --git a/src/test/jtx/acctdelete.h b/src/test/jtx/acctdelete.h index 3f8b9f1ed2..935a97ac65 100644 --- a/src/test/jtx/acctdelete.h +++ b/src/test/jtx/acctdelete.h @@ -9,7 +9,9 @@ namespace xrpl::test::jtx { -/** Delete account. If successful transfer remaining XRP to dest. */ +/** + * Delete account. If successful transfer remaining XRP to dest. + */ json::Value acctdelete(Account const& account, Account const& dest); diff --git a/src/test/jtx/amount.h b/src/test/jtx/amount.h index db7dbd1cc1..57a4502db9 100644 --- a/src/test/jtx/amount.h +++ b/src/test/jtx/amount.h @@ -69,10 +69,11 @@ struct None // could change that value (however unlikely). constexpr XRPAmount kJtxDropsPerXrp{1'000'000}; -/** Represents an XRP, IOU, or MPT quantity - This customizes the string conversion and supports - XRP conversions from integer and floating point. -*/ +/** + * Represents an XRP, IOU, or MPT quantity + * This customizes the string conversion and supports + * XRP conversions from integer and floating point. + */ struct PrettyAmount { private: @@ -91,7 +92,9 @@ public: { } - /** drops */ + /** + * drops + */ template PrettyAmount(T v) requires(sizeof(T) >= sizeof(int) && std::is_integral_v && std::is_signed_v) @@ -99,7 +102,9 @@ public: { } - /** drops */ + /** + * drops + */ template PrettyAmount(T v) requires(sizeof(T) >= sizeof(int) && std::is_unsigned_v) @@ -107,7 +112,9 @@ public: { } - /** drops */ + /** + * drops + */ PrettyAmount(XRPAmount v) : amount_(v) { } @@ -253,11 +260,12 @@ struct BookSpec struct XrpT { - /** Implicit conversion to Issue. - - This allows passing XRP where - an Issue is expected. - */ + /** + * Implicit conversion to Issue. + * + * This allows passing XRP where + * an Issue is expected. + */ operator Issue() const { return xrpIssue(); @@ -273,11 +281,12 @@ struct XrpT return true; } - /** Returns an amount of XRP as PrettyAmount, - which is trivially convertible to STAmount - - @param v The number of XRP (not drops) - */ + /** + * Returns an amount of XRP as PrettyAmount, + * which is trivially convertible to STAmount + * + * @param v The number of XRP (not drops) + */ /** @{ */ template PrettyAmount @@ -288,11 +297,12 @@ struct XrpT return {TOut{v} * kJtxDropsPerXrp}; } - /** Returns an amount of XRP as PrettyAmount, - which is trivially convertible to STAmount - - @param v The Number of XRP (not drops). May be fractional. - */ + /** + * Returns an amount of XRP as PrettyAmount, + * which is trivially convertible to STAmount + * + * @param v The Number of XRP (not drops). May be fractional. + */ PrettyAmount operator()(Number v) const { @@ -321,7 +331,9 @@ struct XrpT } /** @} */ - /** Returns None-of-XRP */ + /** + * Returns None-of-XRP + */ None operator()(NoneT) const { @@ -335,19 +347,21 @@ struct XrpT } }; -/** Converts to XRP Issue or STAmount. - - Examples: - XRP Converts to the XRP Issue - XRP(10) Returns STAmount of 10 XRP -*/ +/** + * Converts to XRP Issue or STAmount. + * + * Examples: + * XRP Converts to the XRP Issue + * XRP(10) Returns STAmount of 10 XRP + */ extern XrpT const XRP; // NOLINT(readability-identifier-naming) -/** Returns an XRP PrettyAmount, which is trivially convertible to STAmount. - - Example: - drops(10) Returns PrettyAmount of 10 drops -*/ +/** + * Returns an XRP PrettyAmount, which is trivially convertible to STAmount. + * + * Example: + * drops(10) Returns PrettyAmount of 10 drops + */ template PrettyAmount drops(Integer i) @@ -356,11 +370,12 @@ drops(Integer i) return {i}; } -/** Returns an XRP PrettyAmount, which is trivially convertible to STAmount. - -Example: -drops(view->fee().basefee) Returns PrettyAmount of 10 drops -*/ +/** + * Returns an XRP PrettyAmount, which is trivially convertible to STAmount. + * + * Example: + * drops(view->fee().basefee) Returns PrettyAmount of 10 drops + */ inline PrettyAmount drops(XRPAmount i) { @@ -383,13 +398,14 @@ struct EpsilonT static EpsilonT const kEpsilon; -/** Converts to IOU Issue or STAmount. - - Examples: - IOU Converts to the underlying Issue - IOU(10) Returns STAmount of 10 of - the underlying Issue. -*/ +/** + * Converts to IOU Issue or STAmount. + * + * Examples: + * IOU Converts to the underlying Issue + * IOU(10) Returns STAmount of 10 of + * the underlying Issue. + */ class IOU { public: @@ -417,11 +433,12 @@ public: return issue().integral(); } - /** Implicit conversion to Issue or Asset. - - This allows passing an IOU - value where an Issue or Asset is expected. - */ + /** + * Implicit conversion to Issue or Asset. + * + * This allows passing an IOU + * value where an Issue or Asset is expected. + */ operator Issue() const { return issue(); @@ -453,7 +470,9 @@ public: // VFALCO TODO // STAmount operator()(char const* s) const; - /** Returns None-of-Issue */ + /** + * Returns None-of-Issue + */ None operator()(NoneT) const { @@ -472,13 +491,14 @@ operator<<(std::ostream& os, IOU const& iou); //------------------------------------------------------------------------------ -/** Converts to MPT Issue or STAmount. - - Examples: - MPT Converts to the underlying Issue - MPT(10) Returns STAmount of 10 of - the underlying MPT -*/ +/** + * Converts to MPT Issue or STAmount. + * + * Examples: + * MPT Converts to the underlying Issue + * MPT(10) Returns STAmount of 10 of + * the underlying MPT + */ class MPT { public: @@ -504,7 +524,8 @@ public: return issuanceID; } - /** Explicit conversion to MPTIssue or asset. + /** + * Explicit conversion to MPTIssue or asset. */ [[nodiscard]] xrpl::MPTIssue mptIssue() const @@ -522,11 +543,12 @@ public: return true; } - /** Implicit conversion to MPTIssue or asset. - - This allows passing an MPT - value where an MPTIssue is expected. - */ + /** + * Implicit conversion to MPTIssue or asset. + * + * This allows passing an MPT + * value where an MPTIssue is expected. + */ operator xrpl::MPTIssue() const { return mptIssue(); @@ -558,7 +580,9 @@ public: PrettyAmount operator()(detail::EpsilonMultiple) const; - /** Returns None-of-Issue */ + /** + * Returns None-of-Issue + */ None operator()(NoneT) const { @@ -583,7 +607,9 @@ struct AnyT operator()(STAmount const& sta) const; }; -/** Amount specifier with an option for any issuer. */ +/** + * Amount specifier with an option for any issuer. + */ struct AnyAmount { bool isAny; @@ -618,9 +644,10 @@ AnyT::operator()(STAmount const& sta) const return AnyAmount(sta, this); } -/** Returns an amount representing "any issuer" - @note With respect to what the recipient will accept -*/ +/** + * Returns an amount representing "any issuer" + * @note With respect to what the recipient will accept + */ extern AnyT const kAny; } // namespace test::jtx diff --git a/src/test/jtx/balance.h b/src/test/jtx/balance.h index 3f39e9f0fb..9d7937369b 100644 --- a/src/test/jtx/balance.h +++ b/src/test/jtx/balance.h @@ -11,14 +11,15 @@ namespace xrpl::test::jtx { -/** A balance matches. - - This allows "none" which means either the account - doesn't exist (no XRP) or the trust line does not - exist. If an amount is specified, the SLE must - exist even if the amount is 0, or else the test - fails. -*/ +/** + * A balance matches. + * + * This allows "none" which means either the account + * doesn't exist (no XRP) or the trust line does not + * exist. If an amount is specified, the SLE must + * exist even if the amount is 0, or else the test + * fails. + */ class Balance { private: diff --git a/src/test/jtx/batch.h b/src/test/jtx/batch.h index bb1deec1a2..140fc84ab1 100644 --- a/src/test/jtx/batch.h +++ b/src/test/jtx/batch.h @@ -18,7 +18,9 @@ #include #include -/** @brief Helpers for constructing Batch test transactions. */ +/** + * @brief Helpers for constructing Batch test transactions. + */ namespace xrpl::test::jtx::batch { /** @@ -57,7 +59,9 @@ calcConfidentialBatchFee(jtx::Env const& env, uint32_t const& numSigners, uint32 json::Value outer(jtx::Account const& account, uint32_t seq, STAmount const& fee, std::uint32_t flags); -/** @brief Adds an inner Batch transaction to a JTx and autofills it. */ +/** + * @brief Adds an inner Batch transaction to a JTx and autofills it. + */ class Inner { private: @@ -106,7 +110,9 @@ public: } }; -/** @brief Sets the Batch transaction signers on a JTx. */ +/** + * @brief Sets the Batch transaction signers on a JTx. + */ class Sig { public: @@ -129,7 +135,9 @@ public: operator()(Env&, JTx& jt) const; }; -/** @brief Sets a nested multi-signature for a Batch transaction on a JTx. */ +/** + * @brief Sets a nested multi-signature for a Batch transaction on a JTx. + */ class Msig { public: diff --git a/src/test/jtx/check.h b/src/test/jtx/check.h index f66d802247..a5d1f079c4 100644 --- a/src/test/jtx/check.h +++ b/src/test/jtx/check.h @@ -12,14 +12,20 @@ namespace xrpl::test::jtx { -/** Check operations. */ +/** + * Check operations. + */ namespace check { -/** Cash a check requiring that a specific amount be delivered. */ +/** + * Cash a check requiring that a specific amount be delivered. + */ json::Value cash(jtx::Account const& dest, uint256 const& checkId, STAmount const& amount); -/** Type used to specify DeliverMin for cashing a check. */ +/** + * Type used to specify DeliverMin for cashing a check. + */ struct DeliverMin { STAmount value; @@ -28,17 +34,23 @@ struct DeliverMin } }; -/** Cash a check requiring that at least a minimum amount be delivered. */ +/** + * Cash a check requiring that at least a minimum amount be delivered. + */ json::Value cash(jtx::Account const& dest, uint256 const& checkId, DeliverMin const& atLeast); -/** Cancel a check. */ +/** + * Cancel a check. + */ json::Value cancel(jtx::Account const& dest, uint256 const& checkId); } // namespace check -/** Match the number of checks on the account. */ +/** + * Match the number of checks on the account. + */ using checks = OwnerCount; } // namespace xrpl::test::jtx diff --git a/src/test/jtx/delivermin.h b/src/test/jtx/delivermin.h index 29256e37bd..db5014bc07 100644 --- a/src/test/jtx/delivermin.h +++ b/src/test/jtx/delivermin.h @@ -9,7 +9,9 @@ namespace xrpl::test::jtx { -/** Sets the DeliverMin on a JTx. */ +/** + * Sets the DeliverMin on a JTx. + */ class DeliverMin { private: diff --git a/src/test/jtx/deposit.h b/src/test/jtx/deposit.h index 5ba12648e2..3039167cee 100644 --- a/src/test/jtx/deposit.h +++ b/src/test/jtx/deposit.h @@ -10,14 +10,20 @@ #include #include -/** Deposit preauthorize operations */ +/** + * Deposit preauthorize operations + */ namespace xrpl::test::jtx::deposit { -/** Preauthorize for deposit. Invoke as deposit::auth. */ +/** + * Preauthorize for deposit. Invoke as deposit::auth. + */ json::Value auth(Account const& account, Account const& auth); -/** Remove pre-authorization for deposit. Invoke as deposit::unauth. */ +/** + * Remove pre-authorization for deposit. Invoke as deposit::unauth. + */ json::Value unauth(Account const& account, Account const& unauth); diff --git a/src/test/jtx/did.h b/src/test/jtx/did.h index 30c89fd879..da10b476e8 100644 --- a/src/test/jtx/did.h +++ b/src/test/jtx/did.h @@ -10,7 +10,9 @@ #include -/** DID operations. */ +/** + * DID operations. + */ namespace xrpl::test::jtx::did { json::Value @@ -19,7 +21,9 @@ set(jtx::Account const& account); json::Value setValid(jtx::Account const& account); -/** Sets the optional DIDDocument on a DIDSet. */ +/** + * Sets the optional DIDDocument on a DIDSet. + */ class Document { private: @@ -37,7 +41,9 @@ public: } }; -/** Sets the optional URI on a DIDSet. */ +/** + * Sets the optional URI on a DIDSet. + */ class Uri { private: @@ -55,7 +61,9 @@ public: } }; -/** Sets the optional Data on a DIDSet. */ +/** + * Sets the optional Data on a DIDSet. + */ class Data { private: diff --git a/src/test/jtx/directory.h b/src/test/jtx/directory.h index 13473f949e..294cfcc43c 100644 --- a/src/test/jtx/directory.h +++ b/src/test/jtx/directory.h @@ -13,7 +13,9 @@ #include #include -/** Directory operations. */ +/** + * Directory operations. + */ namespace xrpl::test::jtx::directory { enum class Error { @@ -25,14 +27,16 @@ enum class Error { AdjustmentError }; -/// Move the position of the last page in the user's directory on open ledger to -/// newLastPage. Requirements: -/// - directory must have at least two pages (root and one more) -/// - adjust should be used to update owner nodes of the objects affected -/// - newLastPage must be greater than index of the last page in the directory -/// -/// Use this to test tecDIR_FULL errors in open ledger. -/// NOTE: effects will be DISCARDED on env.close() +/** + * Move the position of the last page in the user's directory on open ledger to + * newLastPage. Requirements: + * - directory must have at least two pages (root and one more) + * - adjust should be used to update owner nodes of the objects affected + * - newLastPage must be greater than index of the last page in the directory + * + * Use this to test tecDIR_FULL errors in open ledger. + * NOTE: effects will be DISCARDED on env.close() + */ auto bumpLastPage( Env& env, @@ -40,10 +44,12 @@ bumpLastPage( Keylet directory, std::function adjust) -> std::expected; -/// Implementation of adjust for the most common ledger entry, i.e. one where -/// page index is stored in sfOwnerNode (and only there). Pass this function -/// to bumpLastPage if the last page of directory has only objects -/// of this kind (e.g. ticket, DID, offer, deposit preauth, MPToken etc.) +/** + * Implementation of adjust for the most common ledger entry, i.e. one where + * page index is stored in sfOwnerNode (and only there). Pass this function + * to bumpLastPage if the last page of directory has only objects + * of this kind (e.g. ticket, DID, offer, deposit preauth, MPToken etc.) + */ bool adjustOwnerNode(ApplyView& view, uint256 key, std::uint64_t page); diff --git a/src/test/jtx/domain.h b/src/test/jtx/domain.h index ebcfdb662a..c6061c9795 100644 --- a/src/test/jtx/domain.h +++ b/src/test/jtx/domain.h @@ -7,7 +7,9 @@ namespace xrpl::test::jtx { -/** Set the domain on a JTx. */ +/** + * Set the domain on a JTx. + */ class Domain { private: diff --git a/src/test/jtx/envconfig.h b/src/test/jtx/envconfig.h index 0079605637..1f920fca58 100644 --- a/src/test/jtx/envconfig.h +++ b/src/test/jtx/envconfig.h @@ -17,18 +17,22 @@ getEnvLocalhostAddr() return gEnvUseIPv4 ? "127.0.0.1" : "::1"; } -/// @brief initializes a config object for use with jtx::Env -/// -/// @param config the configuration object to be initialized +/** + * @brief initializes a config object for use with jtx::Env + * + * @param config the configuration object to be initialized + */ extern void setupConfigForUnitTests(Config& config); namespace jtx { -/// @brief creates and initializes a default -/// configuration for jtx::Env -/// -/// @return unique_ptr to Config instance +/** + * @brief creates and initializes a default + * configuration for jtx::Env + * + * @return unique_ptr to Config instance + */ inline std::unique_ptr envconfig() { @@ -37,18 +41,20 @@ envconfig() return p; } -/// @brief creates and initializes a default configuration for jtx::Env and -/// invokes the provided function/lambda with the configuration object. -/// -/// @param modfunc callable function or lambda to modify the default config. -/// The first argument to the function must be std::unique_ptr to -/// xrpl::Config. The function takes ownership of the unique_ptr and -/// relinquishes ownership by returning a unique_ptr. -/// -/// @param args additional arguments that will be passed to -/// the config modifier function (optional) -/// -/// @return unique_ptr to Config instance +/** + * @brief creates and initializes a default configuration for jtx::Env and + * invokes the provided function/lambda with the configuration object. + * + * @param modfunc callable function or lambda to modify the default config. + * The first argument to the function must be std::unique_ptr to + * xrpl::Config. The function takes ownership of the unique_ptr and + * relinquishes ownership by returning a unique_ptr. + * + * @param args additional arguments that will be passed to + * the config modifier function (optional) + * + * @return unique_ptr to Config instance + */ template std::unique_ptr envconfig(F&& modfunc, Args&&... args) @@ -56,14 +62,16 @@ envconfig(F&& modfunc, Args&&... args) return modfunc(envconfig(), std::forward(args)...); } -/// @brief adjust config so no admin ports are enabled -/// -/// this is intended for use with envconfig, as in -/// envconfig(noAdmin) -/// -/// @param cfg config instance to be modified -/// -/// @return unique_ptr to Config instance +/** + * @brief adjust config so no admin ports are enabled + * + * this is intended for use with envconfig, as in + * envconfig(noAdmin) + * + * @param cfg config instance to be modified + * + * @return unique_ptr to Config instance + */ std::unique_ptr noAdmin(std::unique_ptr); std::unique_ptr secureGateway(std::unique_ptr); @@ -74,61 +82,71 @@ std::unique_ptr secureGatewayLocalnet(std::unique_ptr); std::unique_ptr singleThreadIo(std::unique_ptr); -/// @brief adjust configuration with params needed to be a validator -/// -/// this is intended for use with envconfig, as in -/// envconfig(validator, myseed) -/// -/// @param cfg config instance to be modified -/// @param seed seed string for use in secret key generation. A fixed default -/// value will be used if this string is empty -/// -/// @return unique_ptr to Config instance +/** + * @brief adjust configuration with params needed to be a validator + * + * this is intended for use with envconfig, as in + * envconfig(validator, myseed) + * + * @param cfg config instance to be modified + * @param seed seed string for use in secret key generation. A fixed default + * value will be used if this string is empty + * + * @return unique_ptr to Config instance + */ std::unique_ptr validator(std::unique_ptr, std::string const&); -/// @brief add a grpc address and port to config -/// -/// This is intended for use with envconfig, for tests that require a grpc -/// server. If this function is not called, grpc server will not start -/// -/// -/// @param cfg config instance to be modified +/** + * @brief add a grpc address and port to config + * + * This is intended for use with envconfig, for tests that require a grpc + * server. If this function is not called, grpc server will not start + * + * + * @param cfg config instance to be modified + */ std::unique_ptr addGrpcConfig(std::unique_ptr); -/// @brief add a grpc address, port and secureGateway to config -/// -/// This is intended for use with envconfig, for tests that require a grpc -/// server. If this function is not called, grpc server will not start -/// -/// -/// @param cfg config instance to be modified +/** + * @brief add a grpc address, port and secureGateway to config + * + * This is intended for use with envconfig, for tests that require a grpc + * server. If this function is not called, grpc server will not start + * + * + * @param cfg config instance to be modified + */ std::unique_ptr addGrpcConfigWithSecureGateway(std::unique_ptr, std::string const& secureGateway); -/// @brief add a grpc address, port and TLS certificate/key paths to config -/// -/// This is intended for use with envconfig, for tests that require a grpc -/// server with TLS enabled. -/// -/// @param cfg config instance to be modified -/// @param certPath path to SSL certificate file -/// @param keyPath path to SSL private key file +/** + * @brief add a grpc address, port and TLS certificate/key paths to config + * + * This is intended for use with envconfig, for tests that require a grpc + * server with TLS enabled. + * + * @param cfg config instance to be modified + * @param certPath path to SSL certificate file + * @param keyPath path to SSL private key file + */ std::unique_ptr addGrpcConfigWithTLS( std::unique_ptr, std::string const& certPath, std::string const& keyPath); -/// @brief add a grpc address, port and TLS certificate/key/client CA paths to config -/// -/// This is intended for use with envconfig, for tests that require a grpc -/// server with mutual TLS (client certificate verification) enabled. -/// -/// @param cfg config instance to be modified -/// @param certPath path to SSL certificate file -/// @param keyPath path to SSL private key file -/// @param clientCAPath path to SSL client CA certificate file for mTLS +/** + * @brief add a grpc address, port and TLS certificate/key/client CA paths to config + * + * This is intended for use with envconfig, for tests that require a grpc + * server with mutual TLS (client certificate verification) enabled. + * + * @param cfg config instance to be modified + * @param certPath path to SSL certificate file + * @param keyPath path to SSL private key file + * @param clientCAPath path to SSL client CA certificate file for mTLS + */ std::unique_ptr addGrpcConfigWithTLSAndClientCA( std::unique_ptr, @@ -136,15 +154,17 @@ addGrpcConfigWithTLSAndClientCA( std::string const& keyPath, std::string const& clientCAPath); -/// @brief add a grpc address, port and TLS with server cert chain to config -/// -/// This is intended for use with envconfig, for tests that require a grpc -/// server with TLS enabled and intermediate CA certificates. -/// -/// @param cfg config instance to be modified -/// @param certPath path to SSL certificate file -/// @param keyPath path to SSL private key file -/// @param certChainPath path to SSL intermediate CA certificate(s) file +/** + * @brief add a grpc address, port and TLS with server cert chain to config + * + * This is intended for use with envconfig, for tests that require a grpc + * server with TLS enabled and intermediate CA certificates. + * + * @param cfg config instance to be modified + * @param certPath path to SSL certificate file + * @param keyPath path to SSL private key file + * @param certChainPath path to SSL intermediate CA certificate(s) file + */ std::unique_ptr addGrpcConfigWithTLSAndCertChain( std::unique_ptr, diff --git a/src/test/jtx/escrow.h b/src/test/jtx/escrow.h index 728b2e3643..68737c2e35 100644 --- a/src/test/jtx/escrow.h +++ b/src/test/jtx/escrow.h @@ -13,7 +13,9 @@ #include #include -/** Escrow operations. */ +/** + * Escrow operations. + */ namespace xrpl::test::jtx::escrow { json::Value @@ -70,10 +72,14 @@ std::array const kCb3 = { 0x3F, 0xA6, 0x3B, 0x1B, 0x60, 0x6F, 0x2D, 0x26, 0x4A, 0x2D, 0x85, 0x7B, 0xE8, 0xA0, 0x9C, 0x1D, 0xFD, 0x57, 0x0D, 0x15, 0x85, 0x8B, 0xD4, 0x81, 0x01, 0x04}}; -/** Set the "FinishAfter" time tag on a JTx */ +/** + * Set the "FinishAfter" time tag on a JTx + */ auto const kFinishTime = JTxFieldWrapper(sfFinishAfter); -/** Set the "CancelAfter" time tag on a JTx */ +/** + * Set the "CancelAfter" time tag on a JTx + */ auto const kCancelTime = JTxFieldWrapper(sfCancelAfter); auto const kCondition = JTxFieldWrapper(sfCondition); diff --git a/src/test/jtx/fee.h b/src/test/jtx/fee.h index ad3002dc75..3754036479 100644 --- a/src/test/jtx/fee.h +++ b/src/test/jtx/fee.h @@ -13,7 +13,9 @@ namespace xrpl::test::jtx { -/** Set the fee on a JTx. */ +/** + * Set the fee on a JTx. + */ class Fee { private: diff --git a/src/test/jtx/flags.h b/src/test/jtx/flags.h index 83470f9a1b..5bbe3c8d12 100644 --- a/src/test/jtx/flags.h +++ b/src/test/jtx/flags.h @@ -97,18 +97,24 @@ namespace test::jtx { // JSON generators -/** Add and/or remove flag. */ +/** + * Add and/or remove flag. + */ json::Value fset(Account const& account, std::uint32_t on, std::uint32_t off = 0); -/** Remove account flag. */ +/** + * Remove account flag. + */ inline json::Value fclear(Account const& account, std::uint32_t off) { return fset(account, 0, off); } -/** Match set account flags */ +/** + * Match set account flags + */ class Flags : private xrpl::detail::FlagsHelper { private: @@ -124,7 +130,9 @@ public: operator()(Env& env) const; }; -/** Match clear account flags */ +/** + * Match clear account flags + */ class Nflags : private xrpl::detail::FlagsHelper { private: diff --git a/src/test/jtx/impl/dids.cpp b/src/test/jtx/impl/dids.cpp index d82250ca34..9c27ea21e3 100644 --- a/src/test/jtx/impl/dids.cpp +++ b/src/test/jtx/impl/dids.cpp @@ -7,7 +7,9 @@ #include #include -/** DID operations. */ +/** + * DID operations. + */ namespace xrpl::test::jtx::did { json::Value diff --git a/src/test/jtx/impl/directory.cpp b/src/test/jtx/impl/directory.cpp index 9fb06e25df..5eaf0be196 100644 --- a/src/test/jtx/impl/directory.cpp +++ b/src/test/jtx/impl/directory.cpp @@ -18,7 +18,9 @@ #include #include -/** Directory operations. */ +/** + * Directory operations. + */ namespace xrpl::test::jtx::directory { auto diff --git a/src/test/jtx/impl/escrow.cpp b/src/test/jtx/impl/escrow.cpp index 007f492849..61c260a5d0 100644 --- a/src/test/jtx/impl/escrow.cpp +++ b/src/test/jtx/impl/escrow.cpp @@ -14,7 +14,9 @@ #include -/** Escrow operations. */ +/** + * Escrow operations. + */ namespace xrpl::test::jtx::escrow { json::Value diff --git a/src/test/jtx/jtx_json.h b/src/test/jtx/jtx_json.h index b22d4c30e1..1ef5c155a8 100644 --- a/src/test/jtx/jtx_json.h +++ b/src/test/jtx/jtx_json.h @@ -9,7 +9,9 @@ namespace xrpl::test::jtx { -/** Inject raw JSON. */ +/** + * Inject raw JSON. + */ class Json { private: diff --git a/src/test/jtx/ledgerStateFix.h b/src/test/jtx/ledgerStateFix.h index dd1ac19f04..2fe5c8accc 100644 --- a/src/test/jtx/ledgerStateFix.h +++ b/src/test/jtx/ledgerStateFix.h @@ -5,14 +5,20 @@ #include #include -/** LedgerStateFix operations. */ +/** + * LedgerStateFix operations. + */ namespace xrpl::test::jtx::ledgerStateFix { -/** Repair the links in an NFToken directory. */ +/** + * Repair the links in an NFToken directory. + */ json::Value nftPageLinks(jtx::Account const& acct, jtx::Account const& owner); -/** Repair sfExchangeRate on a book directory's first page. */ +/** + * Repair sfExchangeRate on a book directory's first page. + */ json::Value bookExchangeRate(jtx::Account const& acct, uint256 const& bookDir); diff --git a/src/test/jtx/memo.h b/src/test/jtx/memo.h index cea4923d38..4e342816ca 100644 --- a/src/test/jtx/memo.h +++ b/src/test/jtx/memo.h @@ -8,11 +8,12 @@ namespace xrpl::test::jtx { -/** Add a memo to a JTx. - - If a memo already exists, the new - memo is appended to the array. -*/ +/** + * Add a memo to a JTx. + * + * If a memo already exists, the new + * memo is appended to the array. + */ class Memo { private: diff --git a/src/test/jtx/mpt.h b/src/test/jtx/mpt.h index 1a7fe94785..c6532ab14a 100644 --- a/src/test/jtx/mpt.h +++ b/src/test/jtx/mpt.h @@ -65,7 +65,9 @@ gMakeZeroBuffer(std::size_t size) return b; } -/** @brief Test helper that checks MPT flag settings after creation. */ +/** + * @brief Test helper that checks MPT flag settings after creation. + */ class MptFlags { private: @@ -86,7 +88,9 @@ public: operator()(Env& env) const; }; -/** @brief Test helper that checks MPT issuance or holder balances. */ +/** + * @brief Test helper that checks MPT issuance or holder balances. + */ class MptBalance { private: @@ -104,7 +108,9 @@ public: operator()(Env& env) const; }; -/** @brief Test helper that accepts any condition supplied by a callback. */ +/** + * @brief Test helper that accepts any condition supplied by a callback. + */ class RequireAny { private: @@ -121,7 +127,9 @@ public: using Holders = std::vector; -/** @brief Arguments for building an MPTokenIssuanceCreate test transaction. */ +/** + * @brief Arguments for building an MPTokenIssuanceCreate test transaction. + */ struct MPTCreate { static inline std::vector allHolders = {}; @@ -145,7 +153,9 @@ struct MPTCreate std::optional err = std::nullopt; }; -/** @brief Arguments for initializing funded MPT test accounts and issuance. */ +/** + * @brief Arguments for initializing funded MPT test accounts and issuance. + */ struct MPTInit { // Default-initialized so designated-initializer call sites that omit @@ -161,7 +171,9 @@ struct MPTInit }; static MPTInit const kMptInitNoFund{.fund = false}; -/** @brief Full constructor arguments for MPTTester initialization. */ +/** + * @brief Full constructor arguments for MPTTester initialization. + */ struct MPTInitDef { Env& env; @@ -179,7 +191,9 @@ struct MPTInitDef std::optional err = std::nullopt; }; -/** @brief Arguments for building an MPTokenIssuanceDestroy test transaction. */ +/** + * @brief Arguments for building an MPTokenIssuanceDestroy test transaction. + */ struct MPTDestroy { std::optional issuer = std::nullopt; @@ -190,7 +204,9 @@ struct MPTDestroy std::optional err = std::nullopt; }; -/** @brief Arguments for building an MPTokenAuthorize test transaction. */ +/** + * @brief Arguments for building an MPTokenAuthorize test transaction. + */ struct MPTAuthorize { std::optional account = std::nullopt; @@ -202,7 +218,9 @@ struct MPTAuthorize std::optional err = std::nullopt; }; -/** @brief Arguments for building an MPTokenIssuanceSet test transaction. */ +/** + * @brief Arguments for building an MPTokenIssuanceSet test transaction. + */ struct MPTSet { std::optional account = std::nullopt; @@ -222,7 +240,9 @@ struct MPTSet std::optional err = std::nullopt; }; -/** @brief Arguments for building a ConfidentialMPTConvert test transaction. */ +/** + * @brief Arguments for building a ConfidentialMPTConvert test transaction. + */ struct MPTConvert { std::optional account = std::nullopt; @@ -250,7 +270,9 @@ struct MPTConvert std::optional err = std::nullopt; }; -/** @brief Arguments for building a ConfidentialMPTMergeInbox test transaction. */ +/** + * @brief Arguments for building a ConfidentialMPTMergeInbox test transaction. + */ struct MPTMergeInbox { std::optional account = std::nullopt; @@ -264,7 +286,9 @@ struct MPTMergeInbox std::optional err = std::nullopt; }; -/** @brief Arguments for building a ConfidentialMPTSend test transaction. */ +/** + * @brief Arguments for building a ConfidentialMPTSend test transaction. + */ struct MPTConfidentialSend { std::optional account = std::nullopt; @@ -293,7 +317,9 @@ struct MPTConfidentialSend std::optional err = std::nullopt; }; -/** @brief Arguments for building a ConfidentialMPTConvertBack test transaction. */ +/** + * @brief Arguments for building a ConfidentialMPTConvertBack test transaction. + */ struct MPTConvertBack { std::optional account = std::nullopt; @@ -316,7 +342,9 @@ struct MPTConvertBack std::optional err = std::nullopt; }; -/** @brief Arguments for building a ConfidentialMPTClawback test transaction. */ +/** + * @brief Arguments for building a ConfidentialMPTClawback test transaction. + */ struct MPTConfidentialClawback { std::optional account = std::nullopt; @@ -339,16 +367,24 @@ struct MPTConfidentialClawback */ struct PedersenProofParams { - /** @brief The Pedersen commitment used by the proof. */ + /** + * @brief The Pedersen commitment used by the proof. + */ Buffer const pedersenCommitment; - /** @brief Either the spending balance or the value being transferred. */ + /** + * @brief Either the spending balance or the value being transferred. + */ uint64_t const amt; - /** @brief The encrypted amount linked to the Pedersen commitment. */ + /** + * @brief The encrypted amount linked to the Pedersen commitment. + */ Buffer const encryptedAmt; - /** @brief The blinding factor used to create the Pedersen commitment. */ + /** + * @brief The blinding factor used to create the Pedersen commitment. + */ Buffer const blindingFactor; }; @@ -365,13 +401,19 @@ struct PedersenProofParams */ struct ConfidentialSendChainState { - /** @brief Decrypted spending balance after the previous send. */ + /** + * @brief Decrypted spending balance after the previous send. + */ std::uint64_t spending; - /** @brief Encrypted spending balance after the previous send. */ + /** + * @brief Encrypted spending balance after the previous send. + */ Buffer encSpending; - /** @brief sfConfidentialBalanceVersion after the previous send. */ + /** + * @brief sfConfidentialBalanceVersion after the previous send. + */ std::uint32_t version; }; diff --git a/src/test/jtx/multisign.h b/src/test/jtx/multisign.h index c90e28537a..65e39f971d 100644 --- a/src/test/jtx/multisign.h +++ b/src/test/jtx/multisign.h @@ -20,7 +20,9 @@ namespace xrpl::test::jtx { -/** A signer in a SignerList */ +/** + * A signer in a SignerList + */ struct Signer { std::uint32_t weight; @@ -36,24 +38,31 @@ struct Signer json::Value signers(Account const& account, std::uint32_t quorum, std::vector const& v); -/** Remove a signer list. */ +/** + * Remove a signer list. + */ json::Value signers(Account const& account, NoneT); //------------------------------------------------------------------------------ -/** Set a multisignature on a JTx. */ +/** + * Set a multisignature on a JTx. + */ class Msig { public: std::vector signers; - /** Alternative transaction object field in which to place the signer list. + /** + * Alternative transaction object field in which to place the signer list. * * subField is only supported if an account_ is provided as well. */ SField const* const subField = nullptr; - /// Used solely as a convenience placeholder for ctors that do _not_ specify - /// a subfield. + /** + * Used solely as a convenience placeholder for ctors that do _not_ specify + * a subfield. + */ static constexpr SField const* kTopLevel = nullptr; Msig(SField const* subField, std::vector signers) @@ -103,7 +112,9 @@ public: //------------------------------------------------------------------------------ -/** The number of signer lists matches. */ +/** + * The number of signer lists matches. + */ using siglists = OwnerCount; } // namespace xrpl::test::jtx diff --git a/src/test/jtx/noop.h b/src/test/jtx/noop.h index c38dfdde25..2ca97a1916 100644 --- a/src/test/jtx/noop.h +++ b/src/test/jtx/noop.h @@ -7,7 +7,9 @@ namespace xrpl::test::jtx { -/** The null transaction. */ +/** + * The null transaction. + */ inline json::Value noop(Account const& account) { diff --git a/src/test/jtx/offer.h b/src/test/jtx/offer.h index f3e7277933..140e16da83 100644 --- a/src/test/jtx/offer.h +++ b/src/test/jtx/offer.h @@ -9,7 +9,9 @@ namespace xrpl::test::jtx { -/** Create an offer. */ +/** + * Create an offer. + */ json::Value offer( Account const& account, @@ -17,7 +19,9 @@ offer( STAmount const& takerGets, std::uint32_t flags = 0); -/** Cancel an offer. */ +/** + * Cancel an offer. + */ json::Value offerCancel(Account const& account, std::uint32_t offerSeq); diff --git a/src/test/jtx/owners.h b/src/test/jtx/owners.h index 3f64601244..1e12603429 100644 --- a/src/test/jtx/owners.h +++ b/src/test/jtx/owners.h @@ -48,7 +48,9 @@ public: } }; -/** Match the number of items in the account's owner directory */ +/** + * Match the number of items in the account's owner directory + */ class Owners { private: @@ -64,8 +66,10 @@ public: operator()(Env& env) const; }; -/** Match the account's SponsoredOwnerCount field: the number of owned - objects whose reserve is sponsored by another account. */ +/** + * Match the account's SponsoredOwnerCount field: the number of owned + * objects whose reserve is sponsored by another account. + */ class SponsoredOwners { private: @@ -82,8 +86,10 @@ public: operator()(Env& env) const; }; -/** Match the account's SponsoringOwnerCount field: the number of objects - (owned by other accounts) whose reserve this account sponsors. */ +/** + * Match the account's SponsoringOwnerCount field: the number of objects + * (owned by other accounts) whose reserve this account sponsors. + */ class SponsoringOwners { private: @@ -100,8 +106,10 @@ public: operator()(Env& env) const; }; -/** Match the account's SponsoringAccountCount field: the number of accounts - whose base reserve this account sponsors. */ +/** + * Match the account's SponsoringAccountCount field: the number of accounts + * whose base reserve this account sponsors. + */ class SponsoringAccountCount { private: @@ -118,13 +126,19 @@ public: operator()(Env& env) const; }; -/** Match the number of trust lines in the account's owner directory */ +/** + * Match the number of trust lines in the account's owner directory + */ using lines = OwnerCount; -/** Match the number of offers in the account's owner directory */ +/** + * Match the number of offers in the account's owner directory + */ using offers = OwnerCount; -/** Match the number of MPToken in the account's owner directory */ +/** + * Match the number of MPToken in the account's owner directory + */ using mptokens = OwnerCount; } // namespace test::jtx diff --git a/src/test/jtx/paths.h b/src/test/jtx/paths.h index b141300a66..07d0117f8f 100644 --- a/src/test/jtx/paths.h +++ b/src/test/jtx/paths.h @@ -16,7 +16,9 @@ class STPath; namespace test::jtx { -/** Set Paths, SendMax on a JTx. */ +/** + * Set Paths, SendMax on a JTx. + */ class Paths { private: @@ -36,10 +38,11 @@ public: //------------------------------------------------------------------------------ -/** Add a path. - - If no paths are present, a new one is created. -*/ +/** + * Add a path. + * + * If no paths are present, a new one is created. + */ class Path { private: diff --git a/src/test/jtx/pay.h b/src/test/jtx/pay.h index fa193e7b02..04ccf27f70 100644 --- a/src/test/jtx/pay.h +++ b/src/test/jtx/pay.h @@ -8,7 +8,9 @@ namespace xrpl::test::jtx { -/** Create a payment. */ +/** + * Create a payment. + */ json::Value pay(AccountID const& account, AccountID const& to, AnyAmount amount); json::Value diff --git a/src/test/jtx/prop.h b/src/test/jtx/prop.h index e85751e4c9..24dca21ca0 100644 --- a/src/test/jtx/prop.h +++ b/src/test/jtx/prop.h @@ -8,7 +8,9 @@ namespace xrpl::test::jtx { -/** Set a property on a JTx. */ +/** + * Set a property on a JTx. + */ template struct Prop { diff --git a/src/test/jtx/quality.h b/src/test/jtx/quality.h index c7896fadf9..a15d319b40 100644 --- a/src/test/jtx/quality.h +++ b/src/test/jtx/quality.h @@ -7,7 +7,9 @@ namespace xrpl::test::jtx { -/** Sets the literal QualityIn on a trust JTx. */ +/** + * Sets the literal QualityIn on a trust JTx. + */ class QualityIn { private: @@ -22,7 +24,9 @@ public: operator()(Env&, JTx& jtx) const; }; -/** Sets the QualityIn on a trust JTx. */ +/** + * Sets the QualityIn on a trust JTx. + */ class QualityInPercent { private: @@ -35,7 +39,9 @@ public: operator()(Env&, JTx& jtx) const; }; -/** Sets the literal QualityOut on a trust JTx. */ +/** + * Sets the literal QualityOut on a trust JTx. + */ class QualityOut { private: @@ -50,7 +56,9 @@ public: operator()(Env&, JTx& jtx) const; }; -/** Sets the QualityOut on a trust JTx as a percentage. */ +/** + * Sets the QualityOut on a trust JTx as a percentage. + */ class QualityOutPercent { private: diff --git a/src/test/jtx/rate.h b/src/test/jtx/rate.h index f76c2dd538..f8caf39fd8 100644 --- a/src/test/jtx/rate.h +++ b/src/test/jtx/rate.h @@ -6,7 +6,9 @@ namespace xrpl::test::jtx { -/** Set a transfer rate. */ +/** + * Set a transfer rate. + */ json::Value rate(Account const& account, double multiplier); diff --git a/src/test/jtx/regkey.h b/src/test/jtx/regkey.h index 676633b2b7..8aa5bd9709 100644 --- a/src/test/jtx/regkey.h +++ b/src/test/jtx/regkey.h @@ -7,11 +7,15 @@ namespace xrpl::test::jtx { -/** Disable the regular key. */ +/** + * Disable the regular key. + */ json::Value regkey(Account const& account, DisabledT); -/** Set a regular key. */ +/** + * Set a regular key. + */ json::Value regkey(Account const& account, Account const& signer); diff --git a/src/test/jtx/require.h b/src/test/jtx/require.h index 19e161b938..eba20b613c 100644 --- a/src/test/jtx/require.h +++ b/src/test/jtx/require.h @@ -23,7 +23,9 @@ requireArgs(test::jtx::requires_t& vec, Cond const& cond, Args const&... args) namespace test::jtx { -/** Compose many condition functors into one */ +/** + * Compose many condition functors into one + */ template require_t required(Args const&... args) @@ -36,12 +38,13 @@ required(Args const&... args) }; } -/** Check a set of conditions. - - The conditions are checked after a JTx is - applied, and only if the resulting TER - matches the expected TER. -*/ +/** + * Check a set of conditions. + * + * The conditions are checked after a JTx is + * applied, and only if the resulting TER + * matches the expected TER. + */ class Require { private: diff --git a/src/test/jtx/rpc.h b/src/test/jtx/rpc.h index bc05450909..9bd99c15f8 100644 --- a/src/test/jtx/rpc.h +++ b/src/test/jtx/rpc.h @@ -12,9 +12,10 @@ namespace xrpl::test::jtx { -/** Set the expected result code for a JTx - The test will fail if the code doesn't match. -*/ +/** + * Set the expected result code for a JTx + * The test will fail if the code doesn't match. + */ class Rpc { private: @@ -24,13 +25,17 @@ private: std::optional errorException_; public: - /// If there's an error code, we expect an error message + /** + * If there's an error code, we expect an error message + */ explicit Rpc(ErrorCodeI code, std::optional m = {}) : code_(code), errorMessage_(std::move(m)) { } - /// If there is not a code, we expect an exception message + /** + * If there is not a code, we expect an exception message + */ explicit Rpc(std::string error, std::optional exceptionMessage = {}) : error_(error), errorException_(std::move(exceptionMessage)) { diff --git a/src/test/jtx/sendmax.h b/src/test/jtx/sendmax.h index 1241d76b91..672959d1aa 100644 --- a/src/test/jtx/sendmax.h +++ b/src/test/jtx/sendmax.h @@ -9,7 +9,9 @@ namespace xrpl::test::jtx { -/** Sets the SendMax on a JTx. */ +/** + * Sets the SendMax on a JTx. + */ class Sendmax { private: diff --git a/src/test/jtx/seq.h b/src/test/jtx/seq.h index 956c0a77e8..323fb33bb2 100644 --- a/src/test/jtx/seq.h +++ b/src/test/jtx/seq.h @@ -9,7 +9,9 @@ namespace xrpl::test::jtx { -/** Set the sequence number on a JTx. */ +/** + * Set the sequence number on a JTx. + */ struct Seq { private: diff --git a/src/test/jtx/sig.h b/src/test/jtx/sig.h index 76f0a34dff..1f20dcf942 100644 --- a/src/test/jtx/sig.h +++ b/src/test/jtx/sig.h @@ -11,26 +11,31 @@ namespace xrpl::test::jtx { -/** Set the regular signature on a JTx. - @note For multisign, use msig. -*/ +/** + * Set the regular signature on a JTx. + * @note For multisign, use msig. + */ class Sig { private: bool manual_ = true; - /** Alternative transaction object field in which to place the signature. + /** + * Alternative transaction object field in which to place the signature. * * subField is only supported if an account_ is provided as well. */ SField const* const subField_ = nullptr; - /** Account that will generate the signature. + /** + * Account that will generate the signature. * * If not provided, no signature will be added by this helper. See also * Env::autofillSig. */ std::optional account_; - /// Used solely as a convenience placeholder for ctors that do _not_ specify - /// a subfield. + /** + * Used solely as a convenience placeholder for ctors that do _not_ specify + * a subfield. + */ static constexpr SField const* kTopLevel = nullptr; public: diff --git a/src/test/jtx/tag.h b/src/test/jtx/tag.h index 77870367a9..b9a10e55f4 100644 --- a/src/test/jtx/tag.h +++ b/src/test/jtx/tag.h @@ -7,7 +7,9 @@ namespace xrpl::test::jtx { -/** Set the destination tag on a JTx*/ +/** + * Set the destination tag on a JTx + */ struct Dtag { private: @@ -22,7 +24,9 @@ public: operator()(Env&, JTx& jt) const; }; -/** Set the source tag on a JTx*/ +/** + * Set the source tag on a JTx + */ struct Stag { private: diff --git a/src/test/jtx/tags.h b/src/test/jtx/tags.h index 4f4afbd1fa..1915c57b5c 100644 --- a/src/test/jtx/tags.h +++ b/src/test/jtx/tags.h @@ -20,7 +20,9 @@ struct DisabledT }; static DisabledT const kDisabled; -/** Used for Fee() calls that use an owner reserve increment */ +/** + * Used for Fee() calls that use an owner reserve increment + */ struct IncrementT { IncrementT() = default; diff --git a/src/test/jtx/ter.h b/src/test/jtx/ter.h index 880711dca0..0ac65e62ee 100644 --- a/src/test/jtx/ter.h +++ b/src/test/jtx/ter.h @@ -10,9 +10,10 @@ namespace xrpl::test::jtx { -/** Set the expected result code for a JTx - The test will fail if the code doesn't match. -*/ +/** + * Set the expected result code for a JTx + * The test will fail if the code doesn't match. + */ class Ter { private: diff --git a/src/test/jtx/ticket.h b/src/test/jtx/ticket.h index 1035be7674..9c9a698eaa 100644 --- a/src/test/jtx/ticket.h +++ b/src/test/jtx/ticket.h @@ -18,14 +18,20 @@ namespace xrpl::test::jtx { without changing the base declarations. */ -/** Ticket operations */ +/** + * Ticket operations + */ namespace ticket { -/** Create one of more tickets */ +/** + * Create one of more tickets + */ json::Value create(Account const& account, std::uint32_t count); -/** Set a ticket sequence on a JTx. */ +/** + * Set a ticket sequence on a JTx. + */ class Use { private: @@ -42,7 +48,9 @@ public: } // namespace ticket -/** Match the number of tickets on the account. */ +/** + * Match the number of tickets on the account. + */ using tickets = OwnerCount; } // namespace xrpl::test::jtx diff --git a/src/test/jtx/token.h b/src/test/jtx/token.h index 97f968fdfc..e94cfd2e12 100644 --- a/src/test/jtx/token.h +++ b/src/test/jtx/token.h @@ -16,11 +16,15 @@ namespace xrpl::test::jtx::token { -/** Mint an NFToken. */ +/** + * Mint an NFToken. + */ json::Value mint(jtx::Account const& account, std::uint32_t tokenTaxon = 0); -/** Sets the optional TransferFee on an NFTokenMint. */ +/** + * Sets the optional TransferFee on an NFTokenMint. + */ class XferFee { private: @@ -35,7 +39,9 @@ public: operator()(Env&, JTx& jtx) const; }; -/** Sets the optional Issuer on an NFTokenMint. */ +/** + * Sets the optional Issuer on an NFTokenMint. + */ class Issuer { private: @@ -50,7 +56,9 @@ public: operator()(Env&, JTx& jtx) const; }; -/** Sets the optional URI on an NFTokenMint. */ +/** + * Sets the optional URI on an NFTokenMint. + */ class Uri { private: @@ -65,7 +73,9 @@ public: operator()(Env&, JTx& jtx) const; }; -/** Sets the optional amount field on an NFTokenMint. */ +/** + * Sets the optional amount field on an NFTokenMint. + */ class Amount { private: @@ -80,7 +90,9 @@ public: operator()(Env&, JTx& jtx) const; }; -/** Get the next NFTokenID that will be issued. */ +/** + * Get the next NFTokenID that will be issued. + */ uint256 getNextID( jtx::Env const& env, @@ -89,7 +101,9 @@ getNextID( std::uint16_t flags = 0, std::uint16_t xferFee = 0); -/** Get the NFTokenID for a particular nftSequence. */ +/** + * Get the NFTokenID for a particular nftSequence. + */ uint256 getID( jtx::Env const& env, @@ -99,15 +113,21 @@ getID( std::uint16_t flags = 0, std::uint16_t xferFee = 0); -/** Burn an NFToken. */ +/** + * Burn an NFToken. + */ json::Value burn(jtx::Account const& account, uint256 const& nftokenID); -/** Create an NFTokenOffer. */ +/** + * Create an NFTokenOffer. + */ json::Value createOffer(jtx::Account const& account, uint256 const& nftokenID, STAmount const& amount); -/** Sets the optional Owner on an NFTokenOffer. */ +/** + * Sets the optional Owner on an NFTokenOffer. + */ class Owner { private: @@ -122,7 +142,9 @@ public: operator()(Env&, JTx& jtx) const; }; -/** Sets the optional Expiration field on an NFTokenOffer. */ +/** + * Sets the optional Expiration field on an NFTokenOffer. + */ class Expiration { private: @@ -137,7 +159,9 @@ public: operator()(Env&, JTx& jtx) const; }; -/** Sets the optional Destination field on an NFTokenOffer. */ +/** + * Sets the optional Destination field on an NFTokenOffer. + */ class Destination { private: @@ -152,14 +176,18 @@ public: operator()(Env&, JTx& jtx) const; }; -/** Cancel NFTokenOffers. */ +/** + * Cancel NFTokenOffers. + */ json::Value cancelOffer(jtx::Account const& account, std::initializer_list const& nftokenOffers = {}); json::Value cancelOffer(jtx::Account const& account, std::vector const& nftokenOffers); -/** Sets the optional RootIndex field when canceling NFTokenOffers. */ +/** + * Sets the optional RootIndex field when canceling NFTokenOffers. + */ class RootIndex { private: @@ -174,22 +202,30 @@ public: operator()(Env&, JTx& jtx) const; }; -/** Accept an NFToken buy offer. */ +/** + * Accept an NFToken buy offer. + */ json::Value acceptBuyOffer(jtx::Account const& account, uint256 const& offerIndex); -/** Accept an NFToken sell offer. */ +/** + * Accept an NFToken sell offer. + */ json::Value acceptSellOffer(jtx::Account const& account, uint256 const& offerIndex); -/** Broker two NFToken offers. */ +/** + * Broker two NFToken offers. + */ json::Value brokerOffers( jtx::Account const& account, uint256 const& buyOfferIndex, uint256 const& sellOfferIndex); -/** Sets the optional NFTokenBrokerFee field in a brokerOffer transaction. */ +/** + * Sets the optional NFTokenBrokerFee field in a brokerOffer transaction. + */ class BrokerFee { private: @@ -204,15 +240,21 @@ public: operator()(Env&, JTx& jtx) const; }; -/** Set the authorized minter on an account root. */ +/** + * Set the authorized minter on an account root. + */ json::Value setMinter(jtx::Account const& account, jtx::Account const& minter); -/** Clear any authorized minter from an account root. */ +/** + * Clear any authorized minter from an account root. + */ json::Value clearMinter(jtx::Account const& account); -/** Modify an NFToken. */ +/** + * Modify an NFToken. + */ json::Value modify(jtx::Account const& account, uint256 const& nftokenID); diff --git a/src/test/jtx/trust.h b/src/test/jtx/trust.h index 034f80fcec..235780c825 100644 --- a/src/test/jtx/trust.h +++ b/src/test/jtx/trust.h @@ -10,11 +10,15 @@ namespace xrpl::test::jtx { -/** Modify a trust line. */ +/** + * Modify a trust line. + */ json::Value trust(Account const& account, STAmount const& amount, std::uint32_t flags = 0); -/** Change flags on a trust line. */ +/** + * Change flags on a trust line. + */ json::Value trust(Account const& account, STAmount const& amount, Account const& peer, std::uint32_t flags); diff --git a/src/test/jtx/txflags.h b/src/test/jtx/txflags.h index 7f5b31b2ac..c940681d82 100644 --- a/src/test/jtx/txflags.h +++ b/src/test/jtx/txflags.h @@ -7,7 +7,9 @@ namespace xrpl::test::jtx { -/** Set the flags on a JTx. */ +/** + * Set the flags on a JTx. + */ class Txflags { private: diff --git a/src/test/jtx/utility.h b/src/test/jtx/utility.h index f1cf3f7ae8..289afec8b3 100644 --- a/src/test/jtx/utility.h +++ b/src/test/jtx/utility.h @@ -13,7 +13,9 @@ namespace xrpl::test::jtx { -/** Thrown when parse fails. */ +/** + * Thrown when parse fails. + */ struct ParseError : std::logic_error { template @@ -22,35 +24,44 @@ struct ParseError : std::logic_error } }; -/** Convert JSON to STObject. - This throws on failure, the JSON must be correct. - @note Testing malformed JSON is beyond the scope of - this set of unit test routines. -*/ +/** + * Convert JSON to STObject. + * This throws on failure, the JSON must be correct. + * @note Testing malformed JSON is beyond the scope of + * this set of unit test routines. + */ STObject parse(json::Value const& jv); -/** Sign automatically into a specific Json field of the jv object. - @note This only works on accounts with multi-signing off. -*/ +/** + * Sign automatically into a specific Json field of the jv object. + * @note This only works on accounts with multi-signing off. + */ void sign(json::Value& jv, Account const& account, json::Value& sigObject); -/** Sign automatically. - @note This only works on accounts with multi-signing off. -*/ +/** + * Sign automatically. + * @note This only works on accounts with multi-signing off. + */ void sign(json::Value& jv, Account const& account); -/** Set the fee automatically. */ +/** + * Set the fee automatically. + */ void fillFee(json::Value& jv, ReadView const& view); -/** Set the sequence number automatically. */ +/** + * Set the sequence number automatically. + */ void fillSeq(json::Value& jv, ReadView const& view); -/** Given an xrpld unit test rpc command, return the corresponding JSON. */ +/** + * Given an xrpld unit test rpc command, return the corresponding JSON. + */ json::Value cmdToJSONRPC(std::vector const& args, beast::Journal j, unsigned int apiVersion); } // namespace xrpl::test::jtx diff --git a/src/test/jtx/vault.h b/src/test/jtx/vault.h index 4e6b90fe1f..e72eae89b7 100644 --- a/src/test/jtx/vault.h +++ b/src/test/jtx/vault.h @@ -27,7 +27,9 @@ struct Vault std::nullopt; // NOLINT(readability-redundant-member-init) }; - /** Return a VaultCreate transaction and the Vault's expected keylet. */ + /** + * Return a VaultCreate transaction and the Vault's expected keylet. + */ [[nodiscard]] std::tuple create(CreateArgs const& args) const; diff --git a/src/test/nodestore/TestBase.h b/src/test/nodestore/TestBase.h index a1245ec963..235e76501f 100644 --- a/src/test/nodestore/TestBase.h +++ b/src/test/nodestore/TestBase.h @@ -20,13 +20,14 @@ namespace xrpl::NodeStore { -/** Binary function that satisfies the strict-weak-ordering requirement. - - This compares the hashes of both objects and returns true if - the first hash is considered to go before the second. - - @see std::sort -*/ +/** + * Binary function that satisfies the strict-weak-ordering requirement. + * + * This compares the hashes of both objects and returns true if + * the first hash is considered to go before the second. + * + * @see std::sort + */ struct LessThan { bool @@ -37,7 +38,9 @@ struct LessThan } }; -/** Returns `true` if objects are identical. */ +/** + * Returns `true` if objects are identical. + */ inline bool isSame(std::shared_ptr const& lhs, std::shared_ptr const& rhs) { diff --git a/src/test/overlay/reduce_relay_test.cpp b/src/test/overlay/reduce_relay_test.cpp index 433595522d..c55729a45a 100644 --- a/src/test/overlay/reduce_relay_test.cpp +++ b/src/test/overlay/reduce_relay_test.cpp @@ -64,7 +64,8 @@ static constexpr std::uint32_t kMaxPeers = 10; static constexpr std::uint32_t kMaxValidators = 10; static constexpr std::uint32_t kMaxMessages = 200000; -/** Simulate two entities - peer directly connected to the server +/** + * Simulate two entities - peer directly connected to the server * (via squelch in PeerSim) and PeerImp (via Overlay) */ class PeerPartial : public Peer @@ -192,7 +193,9 @@ public: } }; -/** Manually advanced clock. */ +/** + * Manually advanced clock. + */ class ManualClock { public: @@ -238,7 +241,9 @@ private: inline static time_point kNow = time_point(seconds(0)); }; -/** Simulate server's OverlayImpl */ +/** + * Simulate server's OverlayImpl + */ class Overlay { public: @@ -260,7 +265,8 @@ public: class Validator; -/** Simulate link from a validator to a peer directly connected +/** + * Simulate link from a validator to a peer directly connected * to the server. */ class Link @@ -317,7 +323,9 @@ private: bool up_{true}; }; -/** Simulate Validator */ +/** + * Simulate Validator + */ class Validator { using Links = std::unordered_map; @@ -400,14 +408,18 @@ public: } } - /** Send to specific peers */ + /** + * Send to specific peers + */ void send(std::vector peers, SquelchCB f) { forLinks(peers, [&](Link& link, MessageSPtr m) { link.send(m, f); }); } - /** Send to all peers */ + /** + * Send to all peers + */ void send(SquelchCB f) { @@ -478,7 +490,9 @@ public: sid = 0; } - /** Local Peer (PeerImp) */ + /** + * Local Peer (PeerImp) + */ void onMessage(MessageSPtr const& m, SquelchCB f) override { @@ -491,7 +505,9 @@ public: {}, *validator, id(), f); // NOLINT(bugprone-unchecked-optional-access) } - /** Remote Peer (Directly connected Peer) */ + /** + * Remote Peer (Directly connected Peer) + */ void onMessage(protocol::TMSquelch const& squelch) override { @@ -836,7 +852,9 @@ public: } } - /** Is peer in Selected state in any of the slots */ + /** + * Is peer in Selected state in any of the slots + */ bool isSelected(Peer::id_t id) { @@ -848,7 +866,8 @@ public: return false; } - /** Check if there are peers to unsquelch - peer is in Selected + /** + * Check if there are peers to unsquelch - peer is in Selected * state in any of the slots and there are peers in Squelched state * in those slots. */ @@ -892,7 +911,9 @@ protected: std::cout << std::endl; } - /** Send squelch (if duration is set) or unsquelch (if duration not set) */ + /** + * Send squelch (if duration is set) or unsquelch (if duration not set) + */ static Peer::id_t sendSquelch( PublicKey const& validator, @@ -930,7 +951,8 @@ protected: bool handled = false; }; - /** Randomly brings the link between a validator and a peer down. + /** + * Randomly brings the link between a validator and a peer down. * Randomly disconnects a peer. Those events are generated one at a time. */ void @@ -1108,7 +1130,8 @@ protected: f(log); } - /** Initial counting round: three peers receive message "faster" then + /** + * Initial counting round: three peers receive message "faster" then * others. Once the message count for the three peers reaches threshold * the rest of the peers are squelched and the slot for the given validator * is in Selected state. @@ -1119,7 +1142,8 @@ protected: doTest("Initial Round", log, [this](bool log) { BEAST_EXPECT(propagateAndSquelch(log)); }); } - /** Receiving message from squelched peer too soon should not change the + /** + * Receiving message from squelched peer too soon should not change the * slot's state to Counting. */ void @@ -1130,7 +1154,8 @@ protected: }); } - /** Receiving message from squelched peer should change the + /** + * Receiving message from squelched peer should change the * slot's state to Counting. */ void @@ -1142,7 +1167,9 @@ protected: }); } - /** Propagate enough messages to generate one squelch event */ + /** + * Propagate enough messages to generate one squelch event + */ bool propagateAndSquelch(bool log, bool purge = true, bool resetClock = true) { @@ -1176,7 +1203,9 @@ protected: return n == 1 && res; } - /** Send fewer message so that squelch event is not generated */ + /** + * Send fewer message so that squelch event is not generated + */ bool propagateNoSquelch( bool log, @@ -1203,7 +1232,8 @@ protected: return !squelched && res; } - /** Receiving a message from new peer should change the + /** + * Receiving a message from new peer should change the * slot's state to Counting. */ void @@ -1216,8 +1246,10 @@ protected: }); } - /** Selected peer disconnects. Should change the state to counting and - * unsquelch squelched peers. */ + /** + * Selected peer disconnects. Should change the state to counting and + * unsquelch squelched peers. + */ void testSelectedPeerDisconnects(bool log) { @@ -1235,8 +1267,10 @@ protected: }); } - /** Selected peer stops relaying. Should change the state to counting and - * unsquelch squelched peers. */ + /** + * Selected peer stops relaying. Should change the state to counting and + * unsquelch squelched peers. + */ void testSelectedPeerStopsRelaying(bool log) { @@ -1255,7 +1289,8 @@ protected: }); } - /** Squelched peer disconnects. Should not change the state to counting. + /** + * Squelched peer disconnects. Should not change the state to counting. */ void testSquelchedPeerDisconnects(bool log) diff --git a/src/test/rpc/LedgerEntry_test.cpp b/src/test/rpc/LedgerEntry_test.cpp index d321ab39aa..7adb5a4518 100644 --- a/src/test/rpc/LedgerEntry_test.cpp +++ b/src/test/rpc/LedgerEntry_test.cpp @@ -2288,7 +2288,9 @@ class LedgerEntry_test : public beast::unit_test::Suite } } - /// Test the ledger entry types that don't take parameters + /** + * Test the ledger entry types that don't take parameters + */ void testFixed() { @@ -2304,7 +2306,8 @@ class LedgerEntry_test : public beast::unit_test::Suite env.close(); - /** Verifies that the RPC result has the expected data + /** + * Verifies that the RPC result has the expected data * * @param good: Indicates that the request should have succeeded * and returned a ledger object of `expectedType` type. @@ -2339,7 +2342,8 @@ class LedgerEntry_test : public beast::unit_test::Suite } }; - /** Runs a series of tests for a given fixed-position ledger + /** + * Runs a series of tests for a given fixed-position ledger * entry. * * @param field: The Json request field to use. @@ -2497,7 +2501,8 @@ class LedgerEntry_test : public beast::unit_test::Suite env.close(); - /** Verifies that the RPC result has the expected data + /** + * Verifies that the RPC result has the expected data * * @param good: Indicates that the request should have succeeded * and returned a ledger object of `expectedType` type. @@ -2537,7 +2542,8 @@ class LedgerEntry_test : public beast::unit_test::Suite } }; - /** Runs a series of tests for a given ledger index. + /** + * Runs a series of tests for a given ledger index. * * @param ledger: The ledger index value of the "hashes" request * parameter. May not necessarily be a number. diff --git a/src/test/rpc/LedgerRPC_test.cpp b/src/test/rpc/LedgerRPC_test.cpp index af56a9e9ba..3a2c957691 100644 --- a/src/test/rpc/LedgerRPC_test.cpp +++ b/src/test/rpc/LedgerRPC_test.cpp @@ -258,10 +258,12 @@ class LedgerRPC_test : public beast::unit_test::Suite BEAST_EXPECT(jrr[jss::ledger][jss::accountState].size() == 3u); } - /// @brief ledger RPC requests as a way to drive - /// input options to lookupLedger. The point of this test is - /// coverage for lookupLedger, not so much the ledger - /// RPC request. + /** + * @brief ledger RPC requests as a way to drive + * input options to lookupLedger. The point of this test is + * coverage for lookupLedger, not so much the ledger + * RPC request. + */ void testLookupLedger() { diff --git a/src/test/unit_test/FileDirGuard.h b/src/test/unit_test/FileDirGuard.h index 3fc5d015b0..b583f821a4 100644 --- a/src/test/unit_test/FileDirGuard.h +++ b/src/test/unit_test/FileDirGuard.h @@ -15,8 +15,8 @@ namespace xrpl::detail { /** - Create a directory and remove it when it's done -*/ + * Create a directory and remove it when it's done + */ class DirGuard { protected: @@ -93,8 +93,8 @@ public: }; /** - Write a file in a directory and remove when done -*/ + * Write a file in a directory and remove when done + */ class FileDirGuard : public DirGuard { protected: diff --git a/src/test/unit_test/multi_runner.h b/src/test/unit_test/multi_runner.h index 3390014cb8..c17fdf3bf0 100644 --- a/src/test/unit_test/multi_runner.h +++ b/src/test/unit_test/multi_runner.h @@ -199,7 +199,8 @@ namespace test { //------------------------------------------------------------------------------ -/** Manager for children running unit tests +/** + * Manager for children running unit tests */ class MultiRunnerParent : private detail::MultiRunnerBase { @@ -234,7 +235,8 @@ public: //------------------------------------------------------------------------------ -/** A class to run a subset of unit tests +/** + * A class to run a subset of unit tests */ class MultiRunnerChild : public beast::unit_test::Runner, private detail::MultiRunnerBase diff --git a/src/test/unit_test/utils.h b/src/test/unit_test/utils.h index 677bbff31b..d4b1e5f7f4 100644 --- a/src/test/unit_test/utils.h +++ b/src/test/unit_test/utils.h @@ -6,9 +6,11 @@ namespace xrpl::test { -/// Compare two SecretKey objects for equality. -/// SecretKey::operator== is deleted, so a named function is used -/// to avoid member-function lookup shadowing free-function overloads. +/** + * Compare two SecretKey objects for equality. + * SecretKey::operator== is deleted, so a named function is used + * to avoid member-function lookup shadowing free-function overloads. + */ inline bool equal(SecretKey const& lhs, SecretKey const& rhs) { diff --git a/src/tests/libxrpl/helpers/Account.h b/src/tests/libxrpl/helpers/Account.h index a92497f2c3..ca2bf68afb 100644 --- a/src/tests/libxrpl/helpers/Account.h +++ b/src/tests/libxrpl/helpers/Account.h @@ -38,35 +38,45 @@ public: */ explicit Account(std::string_view name, KeyType type = KeyType::Secp256k1); - /** @brief Return the human-readable name. */ + /** + * @brief Return the human-readable name. + */ [[nodiscard]] std::string const& name() const noexcept { return name_; } - /** @brief Return the AccountID. */ + /** + * @brief Return the AccountID. + */ [[nodiscard]] AccountID const& id() const noexcept { return id_; } - /** @brief Return the public key. */ + /** + * @brief Return the public key. + */ [[nodiscard]] PublicKey const& pk() const noexcept { return keyPair_.first; } - /** @brief Return the secret key. */ + /** + * @brief Return the secret key. + */ [[nodiscard]] SecretKey const& sk() const noexcept { return keyPair_.second; } - /** @brief Implicit conversion to AccountID. */ + /** + * @brief Implicit conversion to AccountID. + */ operator AccountID const&() const noexcept { return id_; diff --git a/src/tests/libxrpl/helpers/TestFamily.h b/src/tests/libxrpl/helpers/TestFamily.h index a5ff34a111..1a11d3bb68 100644 --- a/src/tests/libxrpl/helpers/TestFamily.h +++ b/src/tests/libxrpl/helpers/TestFamily.h @@ -20,11 +20,12 @@ namespace xrpl::test { -/** Test implementation of Family for unit tests. - - Uses an in-memory NodeStore database and simple caches. - The missingNode methods throw since tests shouldn't encounter missing nodes. -*/ +/** + * Test implementation of Family for unit tests. + * + * Uses an in-memory NodeStore database and simple caches. + * The missingNode methods throw since tests shouldn't encounter missing nodes. + */ class TestFamily : public Family { private: @@ -109,7 +110,9 @@ public: (*tnCache_).reset(); } - /** Access the test clock for time manipulation in tests. */ + /** + * Access the test clock for time manipulation in tests. + */ TestStopwatch& clock() { diff --git a/src/tests/libxrpl/helpers/TestServiceRegistry.h b/src/tests/libxrpl/helpers/TestServiceRegistry.h index 0108f886d3..5475b54dc6 100644 --- a/src/tests/libxrpl/helpers/TestServiceRegistry.h +++ b/src/tests/libxrpl/helpers/TestServiceRegistry.h @@ -23,7 +23,9 @@ namespace xrpl::test { -/** Logs implementation that creates TestSink instances. */ +/** + * Logs implementation that creates TestSink instances. + */ class TestLogs : public Logs { public: @@ -38,7 +40,9 @@ public: } }; -/** Simple NetworkIDService implementation for tests. */ +/** + * Simple NetworkIDService implementation for tests. + */ class TestNetworkIDService final : public NetworkIDService { public: @@ -56,14 +60,15 @@ private: std::uint32_t networkID_; }; -/** Test implementation of ServiceRegistry for unit tests. - - This class provides real implementations for services that can be - instantiated from libxrpl (such as Logs, io_context, caches), and - throws std::logic_error for services that require the full Application. - - Tests can subclass this to provide additional services they need. -*/ +/** + * Test implementation of ServiceRegistry for unit tests. + * + * This class provides real implementations for services that can be + * instantiated from libxrpl (such as Logs, io_context, caches), and + * throws std::logic_error for services that require the full Application. + * + * Tests can subclass this to provide additional services they need. + */ class TestServiceRegistry : public ServiceRegistry { TestLogs logs_{beast::Severity::Warning}; diff --git a/src/tests/libxrpl/helpers/TxTest.h b/src/tests/libxrpl/helpers/TxTest.h index cb75cd5ee0..98198e45f8 100644 --- a/src/tests/libxrpl/helpers/TxTest.h +++ b/src/tests/libxrpl/helpers/TxTest.h @@ -157,10 +157,10 @@ allFeatures(); */ struct TxResult { - TER ter; /**< The transaction engine result code. */ - bool applied; /**< Whether the transaction was applied to the ledger. */ - std::optional metadata; /**< Transaction metadata, if available. */ - std::shared_ptr tx; /**< Pointer to the submitted transaction. */ + TER ter; ///< The transaction engine result code. + bool applied; ///< Whether the transaction was applied to the ledger. + std::optional metadata; ///< Transaction metadata, if available. + std::shared_ptr tx; ///< Pointer to the submitted transaction. }; /** @@ -360,10 +360,14 @@ private: std::shared_ptr closedLedger_; std::shared_ptr openLedger_; - /** Transactions submitted to the open ledger, for canonical reordering on close. */ + /** + * Transactions submitted to the open ledger, for canonical reordering on close. + */ std::vector> pendingTxs_; - /** Current time (can be advanced arbitrarily for testing). */ + /** + * Current time (can be advanced arbitrarily for testing). + */ NetClock::time_point now_; }; diff --git a/src/xrpld/app/consensus/RCLCensorshipDetector.h b/src/xrpld/app/consensus/RCLCensorshipDetector.h index 4318a7b4ff..6d0e20031e 100644 --- a/src/xrpld/app/consensus/RCLCensorshipDetector.h +++ b/src/xrpld/app/consensus/RCLCensorshipDetector.h @@ -51,11 +51,12 @@ private: public: RCLCensorshipDetector() = default; - /** Add transactions being proposed for the current consensus round. - - @param proposed The set of transactions that we are initially proposing - for this round. - */ + /** + * Add transactions being proposed for the current consensus round. + * + * @param proposed The set of transactions that we are initially proposing + * for this round. + */ void propose(TxIDSeqVec proposed) { @@ -74,19 +75,20 @@ public: tracker_ = std::move(proposed); } - /** Determine which transactions made it and perform censorship detection. - - This function is called when the server is proposing and a consensus - round it participated in completed. - - @param accepted The set of transactions that the network agreed - should be included in the ledger being built. - @param pred A predicate invoked for every transaction we've proposed - but which hasn't yet made it. The predicate must be - callable as: - bool pred(TxID const&, Sequence) - It must return true for entries that should be removed. - */ + /** + * Determine which transactions made it and perform censorship detection. + * + * This function is called when the server is proposing and a consensus + * round it participated in completed. + * + * @param accepted The set of transactions that the network agreed + * should be included in the ledger being built. + * @param pred A predicate invoked for every transaction we've proposed + * but which hasn't yet made it. The predicate must be + * callable as: + * bool pred(TxID const&, Sequence) + * It must return true for entries that should be removed. + */ template void check(std::vector accepted, Predicate&& pred) @@ -108,11 +110,12 @@ public: tracker_.erase(i, tracker_.end()); } - /** Removes all elements from the tracker - - Typically, this function might be called after we reconnect to the - network following an outage, or after we start tracking the network. - */ + /** + * Removes all elements from the tracker + * + * Typically, this function might be called after we reconnect to the + * network following an outage, or after we start tracking the network. + */ void reset() { diff --git a/src/xrpld/app/consensus/RCLConsensus.cpp b/src/xrpld/app/consensus/RCLConsensus.cpp index 2abc881adc..4abf77f578 100644 --- a/src/xrpld/app/consensus/RCLConsensus.cpp +++ b/src/xrpld/app/consensus/RCLConsensus.cpp @@ -955,7 +955,9 @@ RCLConsensus::gotTxSet(NetClock::time_point const& now, RCLTxSet const& txSet) } } -//! @see Consensus::simulate +/** + * @see Consensus::simulate + */ void RCLConsensus::simulate( diff --git a/src/xrpld/app/consensus/RCLConsensus.h b/src/xrpld/app/consensus/RCLConsensus.h index 359f6b8009..4ffe18a7a8 100644 --- a/src/xrpld/app/consensus/RCLConsensus.h +++ b/src/xrpld/app/consensus/RCLConsensus.h @@ -43,11 +43,13 @@ class LocalTxs; class LedgerMaster; class ValidatorKeys; -/** Manages the generic consensus algorithm for use by the RCL. +/** + * Manages the generic consensus algorithm for use by the RCL. */ class RCLConsensus { - /** Warn for transactions that haven't been included every so many ledgers. + /** + * Warn for transactions that haven't been included every so many ledgers. */ static constexpr unsigned int kCensorshipWarnInternal = 15; @@ -126,12 +128,13 @@ class RCLConsensus return mode_; } - /** Called before kicking off a new consensus round. - - @param prevLedger Ledger that will be prior ledger for next round - @param nowTrusted the new validators - @return Whether we enter the round proposing - */ + /** + * Called before kicking off a new consensus round. + * + * @param prevLedger Ledger that will be prior ledger for next round + * @param nowTrusted the new validators + * @return Whether we enter the round proposing + */ bool preStartRound(RCLCxLedger const& prevLedger, hash_set const& nowTrusted); @@ -147,14 +150,16 @@ class RCLConsensus std::size_t laggards(Ledger_t::Seq const seq, hash_set& trustedKeys) const; - /** Whether I am a validator. + /** + * Whether I am a validator. * * @return whether I am a validator. */ bool validator() const; - /** Update operating mode based on current peer positions. + /** + * Update operating mode based on current peer positions. * * If our current ledger has no agreement from the network, * then we cannot be in the omFULL mode. @@ -164,7 +169,8 @@ class RCLConsensus void updateOperatingMode(std::size_t const positions) const; - /** Consensus simulation parameters + /** + * Consensus simulation parameters */ ConsensusParms const& parms() const @@ -186,129 +192,142 @@ class RCLConsensus // changing state until a future call to startRound. friend class Consensus; - /** Attempt to acquire a specific ledger. - - If not available, asynchronously acquires from the network. - - @param hash The ID/hash of the ledger acquire - @return Optional ledger, will be seated if we locally had the ledger - */ + /** + * Attempt to acquire a specific ledger. + * + * If not available, asynchronously acquires from the network. + * + * @param hash The ID/hash of the ledger acquire + * @return Optional ledger, will be seated if we locally had the ledger + */ std::optional acquireLedger(LedgerHash const& hash); - /** Share the given proposal with all peers - - @param peerPos The peer position to share. + /** + * Share the given proposal with all peers + * + * @param peerPos The peer position to share. */ void share(RCLCxPeerPos const& peerPos); - /** Share disputed transaction to peers. - - Only share if the provided transaction hasn't been shared recently. - - @param tx The disputed transaction to share. - */ + /** + * Share disputed transaction to peers. + * + * Only share if the provided transaction hasn't been shared recently. + * + * @param tx The disputed transaction to share. + */ void share(RCLCxTx const& tx); - /** Acquire the transaction set associated with a proposal. - - If the transaction set is not available locally, will attempt - acquire it from the network. - - @param setId The transaction set ID associated with the proposal - @return Optional set of transactions, seated if available. - */ + /** + * Acquire the transaction set associated with a proposal. + * + * If the transaction set is not available locally, will attempt + * acquire it from the network. + * + * @param setId The transaction set ID associated with the proposal + * @return Optional set of transactions, seated if available. + */ std::optional acquireTxSet(RCLTxSet::ID const& setId); - /** Whether the open ledger has any transactions + /** + * Whether the open ledger has any transactions */ bool hasOpenTransactions() const; - /** Number of proposers that have validated the given ledger - - @param h The hash of the ledger of interest - @return the number of proposers that validated a ledger - */ + /** + * Number of proposers that have validated the given ledger + * + * @param h The hash of the ledger of interest + * @return the number of proposers that validated a ledger + */ std::size_t proposersValidated(LedgerHash const& h) const; - /** Number of proposers that have validated a ledger descended from - requested ledger. - - @param ledger The current working ledger - @param h The hash of the preferred working ledger - @return The number of validating peers that have validated a ledger - descended from the preferred working ledger. - */ + /** + * Number of proposers that have validated a ledger descended from + * requested ledger. + * + * @param ledger The current working ledger + * @param h The hash of the preferred working ledger + * @return The number of validating peers that have validated a ledger + * descended from the preferred working ledger. + */ std::size_t proposersFinished(RCLCxLedger const& ledger, LedgerHash const& h) const; - /** Propose the given position to my peers. - - @param proposal Our proposed position - */ + /** + * Propose the given position to my peers. + * + * @param proposal Our proposed position + */ void propose(RCLCxPeerPos::Proposal const& proposal); - /** Share the given tx set to peers. - - @param txns The TxSet to share. - */ + /** + * Share the given tx set to peers. + * + * @param txns The TxSet to share. + */ void share(RCLTxSet const& txns); - /** Get the ID of the previous ledger/last closed ledger(LCL) on the - network - - @param ledgerID ID of previous ledger used by consensus - @param ledger Previous ledger consensus has available - @param mode Current consensus mode - @return The id of the last closed network - - @note ledgerID may not match ledger.id() if we haven't acquired - the ledger matching ledgerID from the network + /** + * Get the ID of the previous ledger/last closed ledger(LCL) on the + * network + * + * @param ledgerID ID of previous ledger used by consensus + * @param ledger Previous ledger consensus has available + * @param mode Current consensus mode + * @return The id of the last closed network + * + * @note ledgerID may not match ledger.id() if we haven't acquired + * the ledger matching ledgerID from the network */ uint256 getPrevLedger(uint256 ledgerID, RCLCxLedger const& ledger, ConsensusMode mode); - /** Notified of change in consensus mode - - @param before The prior consensus mode - @param after The new consensus mode - */ + /** + * Notified of change in consensus mode + * + * @param before The prior consensus mode + * @param after The new consensus mode + */ void onModeChange(ConsensusMode before, ConsensusMode after); - /** Close the open ledger and return initial consensus position. - - @param ledger the ledger we are changing to - @param closeTime When consensus closed the ledger - @param mode Current consensus mode - @return Tentative consensus result - */ + /** + * Close the open ledger and return initial consensus position. + * + * @param ledger the ledger we are changing to + * @param closeTime When consensus closed the ledger + * @param mode Current consensus mode + * @return Tentative consensus result + */ Result onClose( RCLCxLedger const& ledger, NetClock::time_point const& closeTime, ConsensusMode mode); - /** Process the accepted ledger. - - @param result The result of consensus - @param prevLedger The closed ledger consensus worked from - @param closeResolution The resolution used in agreeing on an - effective closeTime - @param rawCloseTimes The unrounded closetimes of ourself and our - peers - @param mode Our participating mode at the time consensus was - declared - @param consensusJson Json representation of consensus state - @param validating whether this is a validator - */ + /** + * Process the accepted ledger. + * + * @param result The result of consensus + * @param prevLedger The closed ledger consensus worked from + * @param closeResolution The resolution used in agreeing on an + * effective closeTime + * @param rawCloseTimes The unrounded closetimes of ourself and our + * peers + * @param mode Our participating mode at the time consensus was + * declared + * @param consensusJson Json representation of consensus state + * @param validating whether this is a validator + */ void onAccept( Result const& result, @@ -319,11 +338,12 @@ class RCLConsensus json::Value&& consensusJson, bool const validating); - /** Process the accepted ledger that was a result of simulation/force - accept. - - @ref onAccept - */ + /** + * Process the accepted ledger that was a result of simulation/force + * accept. + * + * @ref onAccept + */ void onForceAccept( Result const& result, @@ -333,18 +353,20 @@ class RCLConsensus ConsensusMode const& mode, json::Value&& consensusJson); - /** Notify peers of a consensus state change - - @param ne Event type for notification - @param ledger The ledger at the time of the state change - @param haveCorrectLCL Whether we believe we have the correct LCL. - */ + /** + * Notify peers of a consensus state change + * + * @param ne Event type for notification + * @param ledger The ledger at the time of the state change + * @param haveCorrectLCL Whether we believe we have the correct LCL. + */ void notify(protocol::NodeEvent ne, RCLCxLedger const& ledger, bool haveCorrectLCL); - /** Accept a new ledger based on the given transactions. - - @ref onAccept + /** + * Accept a new ledger based on the given transactions. + * + * @ref onAccept */ void doAccept( @@ -355,27 +377,28 @@ class RCLConsensus ConsensusMode const& mode, json::Value&& consensusJson); - /** Build the new last closed ledger. - - Accept the given the provided set of consensus transactions and - build the last closed ledger. Since consensus just agrees on which - transactions to apply, but not whether they make it into the closed - ledger, this function also populates retriableTxs with those that - can be retried in the next round. - - @param previousLedger Prior ledger building upon - @param retriableTxs On entry, the set of transactions to apply to - the ledger; on return, the set of transactions - to retry in the next round. - @param closeTime The time the ledger closed - @param closeTimeCorrect Whether consensus agreed on close time - @param closeResolution Resolution used to determine consensus close - time - @param roundTime Duration of this consensus round - @param failedTxs Populate with transactions that we could not - successfully apply. - @return The newly built ledger - */ + /** + * Build the new last closed ledger. + * + * Accept the given the provided set of consensus transactions and + * build the last closed ledger. Since consensus just agrees on which + * transactions to apply, but not whether they make it into the closed + * ledger, this function also populates retriableTxs with those that + * can be retried in the next round. + * + * @param previousLedger Prior ledger building upon + * @param retriableTxs On entry, the set of transactions to apply to + * the ledger; on return, the set of transactions + * to retry in the next round. + * @param closeTime The time the ledger closed + * @param closeTimeCorrect Whether consensus agreed on close time + * @param closeResolution Resolution used to determine consensus close + * time + * @param roundTime Duration of this consensus round + * @param failedTxs Populate with transactions that we could not + * successfully apply. + * @return The newly built ledger + */ RCLCxLedger buildLCL( RCLCxLedger const& previousLedger, @@ -386,22 +409,25 @@ class RCLConsensus std::chrono::milliseconds roundTime, std::set& failedTxs); - /** Validate the given ledger and share with peers as necessary - - @param ledger The ledger to validate - @param txns The consensus transaction set - @param proposing Whether we were proposing transactions while - generating this ledger. If we are not proposing, - a validation can still be sent to inform peers that - we know we aren't fully participating in consensus - but are still around and trying to catch up. - */ + /** + * Validate the given ledger and share with peers as necessary + * + * @param ledger The ledger to validate + * @param txns The consensus transaction set + * @param proposing Whether we were proposing transactions while + * generating this ledger. If we are not proposing, + * a validation can still be sent to inform peers that + * we know we aren't fully participating in consensus + * but are still around and trying to catch up. + */ void validate(RCLCxLedger const& ledger, RCLTxSet const& txns, bool proposing); }; public: - //! Constructor + /** + * Constructor + */ RCLConsensus( Application& app, std::unique_ptr&& feeVote, @@ -417,35 +443,42 @@ public: RCLConsensus& operator=(RCLConsensus const&) = delete; - //! Whether we are validating consensus ledgers. + /** + * Whether we are validating consensus ledgers. + */ bool validating() const { return adaptor_.validating(); } - //! Get the number of proposing peers that participated in the previous - //! round. + /** + * Get the number of proposing peers that participated in the previous + * round. + */ std::size_t prevProposers() const { return adaptor_.prevProposers(); } - /** Get duration of the previous round. - - The duration of the round is the establish phase, measured from closing - the open ledger to accepting the consensus result. - - @return Last round duration in milliseconds - */ + /** + * Get duration of the previous round. + * + * The duration of the round is the establish phase, measured from closing + * the open ledger to accepting the consensus result. + * + * @return Last round duration in milliseconds + */ std::chrono::milliseconds prevRoundTime() const { return adaptor_.prevRoundTime(); } - //! @see Consensus::mode + /** + * @see Consensus::mode + */ ConsensusMode mode() const { @@ -458,12 +491,15 @@ public: return consensus_.phase(); } - //! @see Consensus::getJson + /** + * @see Consensus::getJson + */ json::Value getJson(bool full) const; - /** Adjust the set of trusted validators and kick-off the next round of - consensus. For more details, @see Consensus::startRound + /** + * Adjust the set of trusted validators and kick-off the next round of + * consensus. For more details, @see Consensus::startRound */ void startRound( @@ -474,17 +510,23 @@ public: hash_set const& nowTrusted, std::unique_ptr const& clog); - //! @see Consensus::timerEntry + /** + * @see Consensus::timerEntry + */ void timerEntry( NetClock::time_point const& now, std::unique_ptr const& clog = {}); - //! @see Consensus::gotTxSet + /** + * @see Consensus::gotTxSet + */ void gotTxSet(NetClock::time_point const& now, RCLTxSet const& txSet); - // @see Consensus::prevLedgerID + /** + * @see Consensus::prevLedgerID + */ RCLCxLedger::ID prevLedgerID() const { @@ -492,13 +534,17 @@ public: return consensus_.prevLedgerID(); } - //! @see Consensus::simulate + /** + * @see Consensus::simulate + */ void simulate( NetClock::time_point const& now, std::optional consensusDelay); - //! @see Consensus::proposal + /** + * @see Consensus::proposal + */ bool peerProposal(NetClock::time_point const& now, RCLCxPeerPos const& newProposal); @@ -519,7 +565,8 @@ private: beast::Journal const j_; }; -/** Collects logging information. +/** + * Collects logging information. * * Eases correlating multiple data points together to * help follow flow of a complex activity, such as diff --git a/src/xrpld/app/consensus/RCLCxLedger.h b/src/xrpld/app/consensus/RCLCxLedger.h index 89f70f9add..f9f27d2322 100644 --- a/src/xrpld/app/consensus/RCLCxLedger.h +++ b/src/xrpld/app/consensus/RCLCxLedger.h @@ -14,95 +14,119 @@ namespace xrpl { -/** Represents a ledger in RCLConsensus. - - RCLCxLedger is a thin wrapper over `std::shared_ptr`. -*/ +/** + * Represents a ledger in RCLConsensus. + * + * RCLCxLedger is a thin wrapper over `std::shared_ptr`. + */ class RCLCxLedger { public: - //! Unique identifier of a ledger + /** + * Unique identifier of a ledger + */ using ID = LedgerHash; - //! Sequence number of a ledger + /** + * Sequence number of a ledger + */ using Seq = LedgerIndex; - /** Default constructor - - TODO: This may not be needed if we ensure RCLConsensus is handed a valid - ledger in its constructor. Its bad now because other members are not - checking whether the ledger is valid. - */ + /** + * Default constructor + * + * TODO: This may not be needed if we ensure RCLConsensus is handed a valid + * ledger in its constructor. Its bad now because other members are not + * checking whether the ledger is valid. + */ RCLCxLedger() = default; - /** Constructor - - @param l The ledger to wrap. - */ + /** + * Constructor + * + * @param l The ledger to wrap. + */ RCLCxLedger(std::shared_ptr l) : ledger{std::move(l)} { } - //! Sequence number of the ledger. + /** + * Sequence number of the ledger. + */ [[nodiscard]] Seq const& seq() const { return ledger->header().seq; } - //! Unique identifier (hash) of this ledger. + /** + * Unique identifier (hash) of this ledger. + */ [[nodiscard]] ID const& id() const { return ledger->header().hash; } - //! Unique identifier (hash) of this ledger's parent. + /** + * Unique identifier (hash) of this ledger's parent. + */ [[nodiscard]] ID const& parentID() const { return ledger->header().parentHash; } - //! Resolution used when calculating this ledger's close time. + /** + * Resolution used when calculating this ledger's close time. + */ [[nodiscard]] NetClock::duration closeTimeResolution() const { return ledger->header().closeTimeResolution; } - //! Whether consensus process agreed on close time of the ledger. + /** + * Whether consensus process agreed on close time of the ledger. + */ [[nodiscard]] bool closeAgree() const { return xrpl::getCloseAgree(ledger->header()); } - //! The close time of this ledger + /** + * The close time of this ledger + */ [[nodiscard]] NetClock::time_point closeTime() const { return ledger->header().closeTime; } - //! The close time of this ledger's parent. + /** + * The close time of this ledger's parent. + */ [[nodiscard]] NetClock::time_point parentCloseTime() const { return ledger->header().parentCloseTime; } - //! JSON representation of this ledger. + /** + * JSON representation of this ledger. + */ [[nodiscard]] json::Value getJson() const { return xrpl::getJson({*ledger, {}}); } - /** The ledger instance. - - TODO: Make this shared_ptr .. requires ability to create - a new ledger from a readView? - */ + /** + * The ledger instance. + * + * TODO: Make this shared_ptr .. requires ability to create + * a new ledger from a readView? + */ std::shared_ptr ledger; }; } // namespace xrpl diff --git a/src/xrpld/app/consensus/RCLCxPeerPos.h b/src/xrpld/app/consensus/RCLCxPeerPos.h index e73ac3b532..050bdf6d36 100644 --- a/src/xrpld/app/consensus/RCLCxPeerPos.h +++ b/src/xrpld/app/consensus/RCLCxPeerPos.h @@ -18,26 +18,28 @@ namespace xrpl { -/** A peer's signed, proposed position for use in RCLConsensus. - - Carries a ConsensusProposal signed by a peer. Provides value semantics - but manages shared storage of the peer position internally. -*/ +/** + * A peer's signed, proposed position for use in RCLConsensus. + * + * Carries a ConsensusProposal signed by a peer. Provides value semantics + * but manages shared storage of the peer position internally. + */ class RCLCxPeerPos { public: //< The type of the proposed position using Proposal = ConsensusProposal; - /** Constructor - - Constructs a signed peer position. - - @param publicKey Public key of the peer - @param signature Signature provided with the proposal - @param suppress Unique id used for hash router suppression - @param proposal The consensus proposal - */ + /** + * Constructor + * + * Constructs a signed peer position. + * + * @param publicKey Public key of the peer + * @param signature Signature provided with the proposal + * @param suppress Unique id used for hash router suppression + * @param proposal The consensus proposal + */ RCLCxPeerPos( PublicKey const& publicKey, @@ -45,25 +47,33 @@ public: uint256 const& suppress, Proposal const& proposal); // trivially copyable - //! Verify the signing hash of the proposal + /** + * Verify the signing hash of the proposal + */ bool checkSign() const; - //! Signature of the proposal (not necessarily verified) + /** + * Signature of the proposal (not necessarily verified) + */ Slice signature() const { return {signature_.data(), signature_.size()}; } - //! Public key of peer that sent the proposal + /** + * Public key of peer that sent the proposal + */ PublicKey const& publicKey() const { return publicKey_; } - //! Unique id used by hash router to suppress duplicates + /** + * Unique id used by hash router to suppress duplicates + */ uint256 const& suppressionID() const { @@ -76,7 +86,9 @@ public: return proposal_; } - //! JSON representation of proposal + /** + * JSON representation of proposal + */ json::Value getJson() const; @@ -105,22 +117,23 @@ private: } }; -/** Calculate a unique identifier for a signed proposal. - - The identifier is based on all the fields that contribute to the signature, - as well as the signature itself. The "last closed ledger" field may be - omitted, but the signer will compute the signature as if this field was - present. Recipients of the proposal will inject the last closed ledger in - order to validate the signature. If the last closed ledger is left out, then - it is considered as all zeroes for the purposes of signing. - - @param proposeHash The hash of the proposed position - @param previousLedger The hash of the ledger the proposal is based upon - @param proposeSeq Sequence number of the proposal - @param closeTime Close time of the proposal - @param publicKey Signer's public key - @param signature Proposal signature -*/ +/** + * Calculate a unique identifier for a signed proposal. + * + * The identifier is based on all the fields that contribute to the signature, + * as well as the signature itself. The "last closed ledger" field may be + * omitted, but the signer will compute the signature as if this field was + * present. Recipients of the proposal will inject the last closed ledger in + * order to validate the signature. If the last closed ledger is left out, then + * it is considered as all zeroes for the purposes of signing. + * + * @param proposeHash The hash of the proposed position + * @param previousLedger The hash of the ledger the proposal is based upon + * @param proposeSeq Sequence number of the proposal + * @param closeTime Close time of the proposal + * @param publicKey Signer's public key + * @param signature Proposal signature + */ uint256 proposalUniqueId( uint256 const& proposeHash, diff --git a/src/xrpld/app/consensus/RCLCxTx.h b/src/xrpld/app/consensus/RCLCxTx.h index f174a2fd54..110ef14e1d 100644 --- a/src/xrpld/app/consensus/RCLCxTx.h +++ b/src/xrpld/app/consensus/RCLCxTx.h @@ -12,54 +12,69 @@ namespace xrpl { -/** Represents a transaction in RCLConsensus. - - RCLCxTx is a thin wrapper over the SHAMapItem that corresponds to the - transaction. -*/ +/** + * Represents a transaction in RCLConsensus. + * + * RCLCxTx is a thin wrapper over the SHAMapItem that corresponds to the + * transaction. + */ class RCLCxTx { public: - //! Unique identifier/hash of transaction + /** + * Unique identifier/hash of transaction + */ using ID = uint256; - /** Constructor - - @param txn The transaction to wrap - */ + /** + * Constructor + * + * @param txn The transaction to wrap + */ RCLCxTx(boost::intrusive_ptr txn) : tx(std::move(txn)) { } - //! The unique identifier/hash of the transaction + /** + * The unique identifier/hash of the transaction + */ [[nodiscard]] ID const& id() const { return tx->key(); } - //! The SHAMapItem that represents the transaction. + /** + * The SHAMapItem that represents the transaction. + */ boost::intrusive_ptr tx; }; -/** Represents a set of transactions in RCLConsensus. - - RCLTxSet is a thin wrapper over a SHAMap that stores the set of - transactions. -*/ +/** + * Represents a set of transactions in RCLConsensus. + * + * RCLTxSet is a thin wrapper over a SHAMap that stores the set of + * transactions. + */ class RCLTxSet { public: - //! Unique identifier/hash of the set of transactions + /** + * Unique identifier/hash of the set of transactions + */ using ID = uint256; - //! The type that corresponds to a single transaction + /** + * The type that corresponds to a single transaction + */ using Tx = RCLCxTx; //< Provide a mutable view of a TxSet class MutableTxSet { friend class RCLTxSet; - //! The SHAMap representing the transactions. + /** + * The SHAMap representing the transactions. + */ std::shared_ptr map_; public: @@ -67,22 +82,24 @@ public: { } - /** Insert a new transaction into the set. - - @param t The transaction to insert. - @return Whether the transaction took place. - */ + /** + * Insert a new transaction into the set. + * + * @param t The transaction to insert. + * @return Whether the transaction took place. + */ bool insert(Tx const& t) { return map_->addItem(SHAMapNodeType::TnTransactionNm, t.tx); } - /** Remove a transaction from the set. - - @param entry The ID of the transaction to remove. - @return Whether the transaction was removed. - */ + /** + * Remove a transaction from the set. + * + * @param entry The ID of the transaction to remove. + * @return Whether the transaction was removed. + */ bool erase(Tx::ID const& entry) { @@ -90,66 +107,73 @@ public: } }; - /** Constructor - - @param m SHAMap to wrap - */ + /** + * Constructor + * + * @param m SHAMap to wrap + */ RCLTxSet(std::shared_ptr m) : map{std::move(m)} { XRPL_ASSERT(map, "xrpl::RCLTxSet::MutableTxSet::RCLTxSet : non-null input"); } - /** Constructor from a previously created MutableTxSet - - @param m MutableTxSet that will become fixed + /** + * Constructor from a previously created MutableTxSet + * + * @param m MutableTxSet that will become fixed */ RCLTxSet(MutableTxSet const& m) : map{m.map_->snapShot(false)} { } - /** Test if a transaction is in the set. - - @param entry The ID of transaction to test. - @return Whether the transaction is in the set. - */ + /** + * Test if a transaction is in the set. + * + * @param entry The ID of transaction to test. + * @return Whether the transaction is in the set. + */ [[nodiscard]] bool exists(Tx::ID const& entry) const { return map->hasItem(entry); } - /** Lookup a transaction. - - @param entry The ID of the transaction to find. - @return A shared pointer to the SHAMapItem. - - @note Since find may not succeed, this returns a - `std::shared_ptr` rather than a Tx, which - cannot refer to a missing transaction. The generic consensus - code uses the shared_ptr semantics to know whether the find - was successful and properly creates a Tx as needed. - */ + /** + * Lookup a transaction. + * + * @param entry The ID of the transaction to find. + * @return A shared pointer to the SHAMapItem. + * + * @note Since find may not succeed, this returns a + * `std::shared_ptr` rather than a Tx, which + * cannot refer to a missing transaction. The generic consensus + * code uses the shared_ptr semantics to know whether the find + * was successful and properly creates a Tx as needed. + */ [[nodiscard]] boost::intrusive_ptr const& find(Tx::ID const& entry) const { return map->peekItem(entry); } - //! The unique ID/hash of the transaction set + /** + * The unique ID/hash of the transaction set + */ [[nodiscard]] ID id() const { return map->getHash().asUInt256(); } - /** Find transactions not in common between this and another transaction - set. - - @param j The set to compare with - @return Map of transactions in this set and `j` but not both. The key - is the transaction ID and the value is a bool of the transaction - exists in this set. - */ + /** + * Find transactions not in common between this and another transaction + * set. + * + * @param j The set to compare with + * @return Map of transactions in this set and `j` but not both. The key + * is the transaction ID and the value is a bool of the transaction + * exists in this set. + */ [[nodiscard]] std::map compare(RCLTxSet const& j) const { @@ -171,7 +195,9 @@ public: return ret; } - //! The SHAMap representing the transactions. + /** + * The SHAMap representing the transactions. + */ std::shared_ptr map; }; } // namespace xrpl diff --git a/src/xrpld/app/consensus/RCLValidations.h b/src/xrpld/app/consensus/RCLValidations.h index e8a1996204..7eadaf0dff 100644 --- a/src/xrpld/app/consensus/RCLValidations.h +++ b/src/xrpld/app/consensus/RCLValidations.h @@ -27,10 +27,11 @@ class Application; enum class BypassAccept : bool { No = false, Yes }; -/** Wrapper over STValidation for generic Validation code - - Wraps an STValidation for compatibility with the generic validation code. -*/ +/** + * Wrapper over STValidation for generic Validation code + * + * Wraps an STValidation for compatibility with the generic validation code. + */ class RCLValidation { std::shared_ptr val_; @@ -39,57 +40,72 @@ public: using NodeKey = xrpl::PublicKey; using NodeID = xrpl::NodeID; - /** Constructor - - @param v The validation to wrap. - */ + /** + * Constructor + * + * @param v The validation to wrap. + */ RCLValidation(std::shared_ptr v) : val_{std::move(v)} { } - /// Validated ledger's hash + /** + * Validated ledger's hash + */ [[nodiscard]] uint256 ledgerID() const { return val_->getLedgerHash(); } - /// Validated ledger's sequence number (0 if none) + /** + * Validated ledger's sequence number (0 if none) + */ [[nodiscard]] std::uint32_t seq() const { return val_->getFieldU32(sfLedgerSequence); } - /// Validation's signing time + /** + * Validation's signing time + */ [[nodiscard]] NetClock::time_point signTime() const { return val_->getSignTime(); } - /// Validated ledger's first seen time + /** + * Validated ledger's first seen time + */ [[nodiscard]] NetClock::time_point seenTime() const { return val_->getSeenTime(); } - /// Public key of validator that published the validation + /** + * Public key of validator that published the validation + */ [[nodiscard]] PublicKey key() const { return val_->getSignerPublic(); } - /// NodeID of validator that published the validation + /** + * NodeID of validator that published the validation + */ [[nodiscard]] NodeID nodeID() const { return val_->getNodeID(); } - /// Whether the validation is considered trusted. + /** + * Whether the validation is considered trusted. + */ [[nodiscard]] bool trusted() const { @@ -108,28 +124,36 @@ public: val_->setUntrusted(); } - /// Whether the validation is full (not-partial) + /** + * Whether the validation is full (not-partial) + */ [[nodiscard]] bool full() const { return val_->isFull(); } - /// Get the load fee of the validation if it exists + /** + * Get the load fee of the validation if it exists + */ [[nodiscard]] std::optional loadFee() const { return ~(*val_)[~sfLoadFee]; } - /// Get the cookie specified in the validation (0 if not set) + /** + * Get the cookie specified in the validation (0 if not set) + */ [[nodiscard]] std::uint64_t cookie() const { return (*val_)[sfCookie]; } - /// Extract the underlying STValidation being wrapped + /** + * Extract the underlying STValidation being wrapped + */ [[nodiscard]] std::shared_ptr unwrap() const { @@ -137,15 +161,16 @@ public: } }; -/** Wraps a ledger instance for use in generic Validations LedgerTrie. - - The LedgerTrie models a ledger's history as a map from Seq -> ID. Any - two ledgers that have the same ID for a given Seq have the same ID for - all earlier sequences (e.g. shared ancestry). In practice, a ledger only - conveniently has the prior 256 ancestor hashes available. For - RCLValidatedLedger, we treat any ledgers separated by more than 256 Seq as - distinct. -*/ +/** + * Wraps a ledger instance for use in generic Validations LedgerTrie. + * + * The LedgerTrie models a ledger's history as a map from Seq -> ID. Any + * two ledgers that have the same ID for a given Seq have the same ID for + * all earlier sequences (e.g. shared ancestry). In practice, a ledger only + * conveniently has the prior 256 ancestor hashes available. For + * RCLValidatedLedger, we treat any ledgers separated by more than 256 Seq as + * distinct. + */ class RCLValidatedLedger { public: @@ -160,24 +185,31 @@ public: RCLValidatedLedger(std::shared_ptr const& ledger, beast::Journal j); - /// The sequence (index) of the ledger + /** + * The sequence (index) of the ledger + */ [[nodiscard]] Seq seq() const; - /// The ID (hash) of the ledger + /** + * The ID (hash) of the ledger + */ [[nodiscard]] ID id() const; - /** Lookup the ID of the ancestor ledger - - @param s The sequence (index) of the ancestor - @return The ID of this ledger's ancestor with that sequence number or - ID{0} if one was not determined - */ + /** + * Lookup the ID of the ancestor ledger + * + * @param s The sequence (index) of the ancestor + * @return The ID of this ledger's ancestor with that sequence number or + * ID{0} if one was not determined + */ ID operator[](Seq const& s) const; - /// Find the sequence number of the earliest mismatching ancestor + /** + * Find the sequence number of the earliest mismatching ancestor + */ friend Seq mismatch(RCLValidatedLedger const& a, RCLValidatedLedger const& b); @@ -191,11 +223,12 @@ private: beast::Journal j_; }; -/** Generic validations adaptor class for RCL - - Manages storing and writing stale RCLValidations to the sqlite DB and - acquiring validated ledgers from the network. -*/ +/** + * Generic validations adaptor class for RCL + * + * Manages storing and writing stale RCLValidations to the sqlite DB and + * acquiring validated ledgers from the network. + */ class RCLValidationsAdaptor { public: @@ -206,12 +239,15 @@ public: RCLValidationsAdaptor(Application& app, beast::Journal j); - /** Current time used to determine if validations are stale. + /** + * Current time used to determine if validations are stale. */ [[nodiscard]] NetClock::time_point now() const; - /** Attempt to acquire the ledger with given id from the network */ + /** + * Attempt to acquire the ledger with given id from the network + */ std::optional acquire(LedgerHash const& id); @@ -226,18 +262,21 @@ private: beast::Journal j_; }; -/// Alias for RCL-specific instantiation of generic Validations +/** + * Alias for RCL-specific instantiation of generic Validations + */ using RCLValidations = Validations; -/** Handle a new validation - - Also sets the trust status of a validation based on the validating node's - public key and this node's current UNL. - - @param app Application object containing validations and ledgerMaster - @param val The validation to add - @param source Name associated with validation used in logging -*/ +/** + * Handle a new validation + * + * Also sets the trust status of a validation based on the validating node's + * public key and this node's current UNL. + * + * @param app Application object containing validations and ledgerMaster + * @param val The validation to add + * @param source Name associated with validation used in logging + */ void handleNewValidation( Application& app, diff --git a/src/xrpld/app/ledger/AbstractFetchPackContainer.h b/src/xrpld/app/ledger/AbstractFetchPackContainer.h index 3adc435bd6..6298ddda0d 100644 --- a/src/xrpld/app/ledger/AbstractFetchPackContainer.h +++ b/src/xrpld/app/ledger/AbstractFetchPackContainer.h @@ -7,20 +7,22 @@ namespace xrpl { -/** An interface facilitating retrieval of fetch packs without - an application or ledgermaster object. -*/ +/** + * An interface facilitating retrieval of fetch packs without + * an application or ledgermaster object. + */ class AbstractFetchPackContainer { public: virtual ~AbstractFetchPackContainer() = default; - /** Retrieves partial ledger data of the corresponding hash from peers.` - - @param nodeHash The 256-bit hash of the data to fetch. - @return `std::nullopt` if the hash isn't cached, - otherwise, the hash associated data. - */ + /** + * Retrieves partial ledger data of the corresponding hash from peers.` + * + * @param nodeHash The 256-bit hash of the data to fetch. + * @return `std::nullopt` if the hash isn't cached, + * otherwise, the hash associated data. + */ virtual std::optional getFetchPack(uint256 const& nodeHash) = 0; }; diff --git a/src/xrpld/app/ledger/AcceptedLedger.h b/src/xrpld/app/ledger/AcceptedLedger.h index ec83839d7a..6e42d611d4 100644 --- a/src/xrpld/app/ledger/AcceptedLedger.h +++ b/src/xrpld/app/ledger/AcceptedLedger.h @@ -11,14 +11,15 @@ namespace xrpl { -/** A ledger that has become irrevocable. - - An accepted ledger is a ledger that has a sufficient number of - validations to convince the local server that it is irrevocable. - - The existence of an accepted ledger implies all preceding ledgers - are accepted. -*/ +/** + * A ledger that has become irrevocable. + * + * An accepted ledger is a ledger that has a sufficient number of + * validations to convince the local server that it is irrevocable. + * + * The existence of an accepted ledger implies all preceding ledgers + * are accepted. + */ /* VFALCO TODO digest this terminology clarification: Closed and accepted refer to ledgers that have not passed the validation threshold yet. Once they pass the threshold, they are diff --git a/src/xrpld/app/ledger/BuildLedger.h b/src/xrpld/app/ledger/BuildLedger.h index faa800daa7..22e33cabc0 100644 --- a/src/xrpld/app/ledger/BuildLedger.h +++ b/src/xrpld/app/ledger/BuildLedger.h @@ -16,21 +16,22 @@ class Ledger; class LedgerReplay; class SHAMap; -/** Build a new ledger by applying consensus transactions - - Build a new ledger by applying a set of transactions accepted as part of - consensus. - - @param parent The ledger to apply transactions to - @param closeTime The time the ledger closed - @param closeTimeCorrect Whether consensus agreed on close time - @param closeResolution Resolution used to determine consensus close time - @param app Handle to application instance - @param txs On entry, transactions to apply; on exit, transactions that must - be retried in next round. - @param failedTxs Populated with transactions that failed in this round - @param j Journal to use for logging - @return The newly built ledger +/** + * Build a new ledger by applying consensus transactions + * + * Build a new ledger by applying a set of transactions accepted as part of + * consensus. + * + * @param parent The ledger to apply transactions to + * @param closeTime The time the ledger closed + * @param closeTimeCorrect Whether consensus agreed on close time + * @param closeResolution Resolution used to determine consensus close time + * @param app Handle to application instance + * @param txs On entry, transactions to apply; on exit, transactions that must + * be retried in next round. + * @param failedTxs Populated with transactions that failed in this round + * @param j Journal to use for logging + * @return The newly built ledger */ std::shared_ptr buildLedger( @@ -43,15 +44,16 @@ buildLedger( std::set& failedTxs, beast::Journal j); -/** Build a new ledger by replaying transactions - - Build a new ledger by replaying transactions accepted into a prior ledger. - - @param replayData Data of the ledger to replay - @param applyFlags Flags to use when applying transactions - @param app Handle to application instance - @param j Journal to use for logging - @return The newly built ledger +/** + * Build a new ledger by replaying transactions + * + * Build a new ledger by replaying transactions accepted into a prior ledger. + * + * @param replayData Data of the ledger to replay + * @param applyFlags Flags to use when applying transactions + * @param app Handle to application instance + * @param j Journal to use for logging + * @return The newly built ledger */ std::shared_ptr buildLedger( diff --git a/src/xrpld/app/ledger/InboundLedger.h b/src/xrpld/app/ledger/InboundLedger.h index 5f9f0e1baf..d8a9ddf46b 100644 --- a/src/xrpld/app/ledger/InboundLedger.h +++ b/src/xrpld/app/ledger/InboundLedger.h @@ -59,14 +59,18 @@ public: void update(std::uint32_t seq); - /** Returns true if we got all the data. */ + /** + * Returns true if we got all the data. + */ bool isComplete() const { return complete_; } - /** Returns false if we failed to get the data. */ + /** + * Returns false if we failed to get the data. + */ bool isFailed() const { @@ -95,7 +99,9 @@ public: using neededHash_t = std::pair; - /** Return a json::ValueType::Object. */ + /** + * Return a json::ValueType::Object. + */ json::Value getJson(int); diff --git a/src/xrpld/app/ledger/InboundLedgers.h b/src/xrpld/app/ledger/InboundLedgers.h index 65b2db7d8e..e288201c66 100644 --- a/src/xrpld/app/ledger/InboundLedgers.h +++ b/src/xrpld/app/ledger/InboundLedgers.h @@ -20,10 +20,11 @@ namespace xrpl { -/** Manages the lifetime of inbound ledgers. - - @see InboundLedger -*/ +/** + * Manages the lifetime of inbound ledgers. + * + * @see InboundLedger + */ class InboundLedgers { public: @@ -68,11 +69,15 @@ public: virtual json::Value getInfo() = 0; - /** Returns the rate of historical ledger fetches per minute. */ + /** + * Returns the rate of historical ledger fetches per minute. + */ virtual std::size_t fetchRate() = 0; - /** Called when a complete ledger is obtained. */ + /** + * Called when a complete ledger is obtained. + */ virtual void onLedgerFetched() = 0; diff --git a/src/xrpld/app/ledger/InboundTransactions.h b/src/xrpld/app/ledger/InboundTransactions.h index d9799d9ad0..a961d04c33 100644 --- a/src/xrpld/app/ledger/InboundTransactions.h +++ b/src/xrpld/app/ledger/InboundTransactions.h @@ -18,7 +18,8 @@ namespace xrpl { class Application; -/** Manages the acquisition and lifetime of transaction sets. +/** + * Manages the acquisition and lifetime of transaction sets. */ class InboundTransactions @@ -33,7 +34,8 @@ public: virtual ~InboundTransactions() = 0; - /** Find and return a transaction set, or nullptr if it is missing. + /** + * Find and return a transaction set, or nullptr if it is missing. * * @param setHash The transaction set ID (digest of the SHAMap root node). * @param acquire Whether to fetch the transaction set from the network if @@ -44,7 +46,8 @@ public: virtual std::shared_ptr getSet(uint256 const& setHash, bool acquire) = 0; - /** Add a transaction set from a LedgerData message. + /** + * Add a transaction set from a LedgerData message. * * @param setHash The transaction set ID (digest of the SHAMap root node). * @param peer The peer that sent the message. @@ -56,7 +59,8 @@ public: std::shared_ptr peer, std::shared_ptr message) = 0; - /** Add a transaction set. + /** + * Add a transaction set. * * @param setHash The transaction set ID (should match set.getHash()). * @param set The transaction set. @@ -66,7 +70,8 @@ public: virtual void giveSet(uint256 const& setHash, std::shared_ptr const& set, bool acquired) = 0; - /** Informs the container if a new consensus round + /** + * Informs the container if a new consensus round */ virtual void newRound(std::uint32_t seq) = 0; diff --git a/src/xrpld/app/ledger/LedgerCleaner.h b/src/xrpld/app/ledger/LedgerCleaner.h index fd693d6bec..9dc35d463f 100644 --- a/src/xrpld/app/ledger/LedgerCleaner.h +++ b/src/xrpld/app/ledger/LedgerCleaner.h @@ -10,7 +10,9 @@ namespace xrpl { -/** Check the ledger/transaction databases to make sure they have continuity */ +/** + * Check the ledger/transaction databases to make sure they have continuity + */ class LedgerCleaner : public beast::PropertyStream::Source { protected: @@ -27,16 +29,17 @@ public: virtual void stop() = 0; - /** Start a long running task to clean the ledger. - The ledger is cleaned asynchronously, on an implementation defined - thread. This function call does not block. The long running task - will be stopped by a call to stop(). - - Thread safety: - Safe to call from any thread at any time. - - @param parameters A Json object with configurable parameters. - */ + /** + * Start a long running task to clean the ledger. + * The ledger is cleaned asynchronously, on an implementation defined + * thread. This function call does not block. The long running task + * will be stopped by a call to stop(). + * + * Thread safety: + * Safe to call from any thread at any time. + * + * @param parameters A Json object with configurable parameters. + */ virtual void clean(json::Value const& parameters) = 0; }; diff --git a/src/xrpld/app/ledger/LedgerHistory.cpp b/src/xrpld/app/ledger/LedgerHistory.cpp index b7e1772942..8734faa1fb 100644 --- a/src/xrpld/app/ledger/LedgerHistory.cpp +++ b/src/xrpld/app/ledger/LedgerHistory.cpp @@ -502,7 +502,8 @@ LedgerHistory::validatedLedger( entry->validatedConsensusHash = consensusHash; } -/** Ensure ledgers_by_hash_ doesn't have the wrong hash for a particular index +/** + * Ensure ledgers_by_hash_ doesn't have the wrong hash for a particular index */ bool LedgerHistory::fixIndex(LedgerIndex ledgerIndex, LedgerHash const& ledgerHash) diff --git a/src/xrpld/app/ledger/LedgerHistory.h b/src/xrpld/app/ledger/LedgerHistory.h index 3fb6e345cf..922b354c32 100644 --- a/src/xrpld/app/ledger/LedgerHistory.h +++ b/src/xrpld/app/ledger/LedgerHistory.h @@ -19,43 +19,53 @@ namespace xrpl { // VFALCO TODO Rename to OldLedgers ? -/** Retains historical ledgers. */ +/** + * Retains historical ledgers. + */ class LedgerHistory { public: LedgerHistory(beast::insight::Collector::ptr const& collector, Application& app); - /** Track a ledger - @return `true` if the ledger was already tracked - */ + /** + * Track a ledger + * @return `true` if the ledger was already tracked + */ bool insert(std::shared_ptr const& ledger, bool validated); - /** Get the ledgers_by_hash cache hit rate - @return the hit rate - */ + /** + * Get the ledgers_by_hash cache hit rate + * @return the hit rate + */ float getCacheHitRate() { return ledgersByHash_.getHitRate(); } - /** Get a ledger given its sequence number */ + /** + * Get a ledger given its sequence number + */ std::shared_ptr getLedgerBySeq(LedgerIndex ledgerIndex); - /** Retrieve a ledger given its hash */ + /** + * Retrieve a ledger given its hash + */ std::shared_ptr getLedgerByHash(LedgerHash const& ledgerHash); - /** Get a ledger's hash given its sequence number - @param ledgerIndex The sequence number of the desired ledger - @return The hash of the specified ledger - */ + /** + * Get a ledger's hash given its sequence number + * @param ledgerIndex The sequence number of the desired ledger + * @return The hash of the specified ledger + */ LedgerHash getLedgerHash(LedgerIndex ledgerIndex); - /** Remove stale cache entries + /** + * Remove stale cache entries */ void sweep() @@ -64,21 +74,26 @@ public: consensusValidated_.sweep(); } - /** Report that we have locally built a particular ledger */ + /** + * Report that we have locally built a particular ledger + */ void builtLedger(std::shared_ptr const&, uint256 const& consensusHash, json::Value); - /** Report that we have validated a particular ledger */ + /** + * Report that we have validated a particular ledger + */ void validatedLedger( std::shared_ptr const&, std::optional const& consensusHash); - /** Repair a hash to index mapping - @param ledgerIndex The index whose mapping is to be repaired - @param ledgerHash The hash it is to be mapped to - @return `false` if the mapping was repaired - */ + /** + * Repair a hash to index mapping + * @param ledgerIndex The index whose mapping is to be repaired + * @param ledgerHash The hash it is to be mapped to + * @return `false` if the mapping was repaired + */ bool fixIndex(LedgerIndex ledgerIndex, LedgerHash const& ledgerHash); @@ -86,16 +101,17 @@ public: clearLedgerCachePrior(LedgerIndex seq); private: - /** Log details in the case where we build one ledger but - validate a different one. - @param built The hash of the ledger we built - @param valid The hash of the ledger we deemed fully valid - @param builtConsensusHash The hash of the consensus transaction for the - ledger we built - @param validatedConsensusHash The hash of the validated ledger's - consensus transaction set - @param consensus The status of the consensus round - */ + /** + * Log details in the case where we build one ledger but + * validate a different one. + * @param built The hash of the ledger we built + * @param valid The hash of the ledger we deemed fully valid + * @param builtConsensusHash The hash of the consensus transaction for the + * ledger we built + * @param validatedConsensusHash The hash of the validated ledger's + * consensus transaction set + * @param consensus The status of the consensus round + */ void handleMismatch( LedgerHash const& built, diff --git a/src/xrpld/app/ledger/LedgerHolder.h b/src/xrpld/app/ledger/LedgerHolder.h index 3e70544bfc..da365e5e31 100644 --- a/src/xrpld/app/ledger/LedgerHolder.h +++ b/src/xrpld/app/ledger/LedgerHolder.h @@ -14,12 +14,13 @@ namespace xrpl { // VFALCO NOTE This class can be replaced with atomic> -/** Hold a ledger in a thread-safe way. - - VFALCO TODO The constructor should require a valid ledger, this - way the object always holds a value. We can use the - genesis ledger in all cases. -*/ +/** + * Hold a ledger in a thread-safe way. + * + * VFALCO TODO The constructor should require a valid ledger, this + * way the object always holds a value. We can use the + * genesis ledger in all cases. + */ class LedgerHolder : public CountedObject { public: diff --git a/src/xrpld/app/ledger/LedgerMaster.h b/src/xrpld/app/ledger/LedgerMaster.h index efd8c15e20..32163fd57b 100644 --- a/src/xrpld/app/ledger/LedgerMaster.h +++ b/src/xrpld/app/ledger/LedgerMaster.h @@ -108,10 +108,11 @@ public: void setFullLedger(std::shared_ptr const& ledger, bool isSynchronous, bool isCurrent); - /** Check the sequence number and parent close time of a - ledger against our clock and last validated ledger to - see if it can be the network's current ledger - */ + /** + * Check the sequence number and parent close time of a + * ledger against our clock and last validated ledger to + * see if it can be the network's current ledger + */ bool canBeCurrent(std::shared_ptr const& ledger); @@ -124,38 +125,44 @@ public: std::string getCompleteLedgers(); - /** Apply held transactions to the open ledger - This is normally called as we close the ledger. - The open ledger remains open to handle new transactions - until a new open ledger is built. - */ + /** + * Apply held transactions to the open ledger + * This is normally called as we close the ledger. + * The open ledger remains open to handle new transactions + * until a new open ledger is built. + */ void applyHeldTransactions(); - /** Get the next transaction held for a particular account if any. - This is normally called when a transaction for that account is - successfully applied to the open ledger so the next transaction - can be resubmitted without waiting for ledger close. - */ + /** + * Get the next transaction held for a particular account if any. + * This is normally called when a transaction for that account is + * successfully applied to the open ledger so the next transaction + * can be resubmitted without waiting for ledger close. + */ std::shared_ptr popAcctTransaction(std::shared_ptr const& tx); - /** Get a ledger's hash by sequence number using the cache + /** + * Get a ledger's hash by sequence number using the cache */ uint256 getHashBySeq(std::uint32_t index); - /** Walk to a ledger's hash using the skip list */ + /** + * Walk to a ledger's hash using the skip list + */ std::optional walkHashBySeq(std::uint32_t index, InboundLedger::Reason reason); - /** Walk the chain of ledger hashes to determine the hash of the - ledger with the specified index. The referenceLedger is used as - the base of the chain and should be fully validated and must not - precede the target index. This function may throw if nodes - from the reference ledger or any prior ledger are not present - in the node store. - */ + /** + * Walk the chain of ledger hashes to determine the hash of the + * ledger with the specified index. The referenceLedger is used as + * the base of the chain and should be fully validated and must not + * precede the target index. This function may throw if nodes + * from the reference ledger or any prior ledger are not present + * in the node store. + */ std::optional walkHashBySeq( std::uint32_t index, @@ -255,7 +262,9 @@ public: std::size_t getFetchPackCacheSize() const; - //! Whether we have ever fully validated a ledger. + /** + * Whether we have ever fully validated a ledger. + */ bool haveValidated() { diff --git a/src/xrpld/app/ledger/LedgerPersistence.h b/src/xrpld/app/ledger/LedgerPersistence.h index f466c32296..e2e442cb30 100644 --- a/src/xrpld/app/ledger/LedgerPersistence.h +++ b/src/xrpld/app/ledger/LedgerPersistence.h @@ -14,15 +14,16 @@ namespace xrpl { class ServiceRegistry; struct Fees; -/** Save, or arrange to save, a fully-validated ledger. - - @param registry The service registry providing access to required services. - @param ledger The fully-validated ledger to save. - @param isSynchronous If true, wait for the save to complete. - @param isCurrent If true, the ledger is the current validated ledger. - - @return false on error. -*/ +/** + * Save, or arrange to save, a fully-validated ledger. + * + * @param registry The service registry providing access to required services. + * @param ledger The fully-validated ledger to save. + * @param isSynchronous If true, wait for the save to complete. + * @param isCurrent If true, the ledger is the current validated ledger. + * + * @return false on error. + */ bool pendSaveValidated( ServiceRegistry& registry, @@ -30,15 +31,16 @@ pendSaveValidated( bool isSynchronous, bool isCurrent); -/** Make ledger using info loaded from database. - - @param info Ledger information. - @param rules Rules to use (may be overwritten by setup()). - @param fees Fees to use (may be overwritten by setup()). - @param registry Service registry for dependency injection. - @param acquire Acquire the ledger if not found locally. - @return Shared pointer to the ledger. -*/ +/** + * Make ledger using info loaded from database. + * + * @param info Ledger information. + * @param rules Rules to use (may be overwritten by setup()). + * @param fees Fees to use (may be overwritten by setup()). + * @param registry Service registry for dependency injection. + * @param acquire Acquire the ledger if not found locally. + * @return Shared pointer to the ledger. + */ std::shared_ptr loadLedgerHelper( LedgerHeader const& info, @@ -47,15 +49,16 @@ loadLedgerHelper( ServiceRegistry& registry, bool acquire); -/** Load a ledger by its sequence number. - - @param ledgerIndex The sequence number of the ledger to load. - @param rules Rules to use (may be overwritten by setup()). - @param fees Fees to use (may be overwritten by setup()). - @param registry Service registry for dependency injection. - @param acquire Acquire the ledger if not found locally. - @return Shared pointer to the ledger, or nullptr if not found. -*/ +/** + * Load a ledger by its sequence number. + * + * @param ledgerIndex The sequence number of the ledger to load. + * @param rules Rules to use (may be overwritten by setup()). + * @param fees Fees to use (may be overwritten by setup()). + * @param registry Service registry for dependency injection. + * @param acquire Acquire the ledger if not found locally. + * @return Shared pointer to the ledger, or nullptr if not found. + */ std::shared_ptr loadByIndex( std::uint32_t ledgerIndex, @@ -64,15 +67,16 @@ loadByIndex( ServiceRegistry& registry, bool acquire = true); -/** Load a ledger by its hash. - - @param ledgerHash The hash of the ledger to load. - @param rules Rules to use (may be overwritten by setup()). - @param fees Fees to use (may be overwritten by setup()). - @param registry Service registry for dependency injection. - @param acquire Acquire the ledger if not found locally. - @return Shared pointer to the ledger, or nullptr if not found. -*/ +/** + * Load a ledger by its hash. + * + * @param ledgerHash The hash of the ledger to load. + * @param rules Rules to use (may be overwritten by setup()). + * @param fees Fees to use (may be overwritten by setup()). + * @param registry Service registry for dependency injection. + * @param acquire Acquire the ledger if not found locally. + * @return Shared pointer to the ledger, or nullptr if not found. + */ std::shared_ptr loadByHash( uint256 const& ledgerHash, @@ -81,13 +85,14 @@ loadByHash( ServiceRegistry& registry, bool acquire = true); -/** Fetch the ledger with the highest sequence contained in the database. - - @param rules Rules to use (may be overwritten by setup()). - @param fees Fees to use (may be overwritten by setup()). - @param registry Service registry for dependency injection. - @return Tuple of (ledger, sequence, hash), or empty if not found. -*/ +/** + * Fetch the ledger with the highest sequence contained in the database. + * + * @param rules Rules to use (may be overwritten by setup()). + * @param fees Fees to use (may be overwritten by setup()). + * @param registry Service registry for dependency injection. + * @return Tuple of (ledger, sequence, hash), or empty if not found. + */ std::tuple, std::uint32_t, uint256> getLatestLedger(Rules const& rules, Fees const& fees, ServiceRegistry& registry); diff --git a/src/xrpld/app/ledger/LedgerReplay.h b/src/xrpld/app/ledger/LedgerReplay.h index 2dc4911ade..6a2da92007 100644 --- a/src/xrpld/app/ledger/LedgerReplay.h +++ b/src/xrpld/app/ledger/LedgerReplay.h @@ -25,7 +25,8 @@ public: std::shared_ptr replay, std::map>&& orderedTxns); - /** @return The parent of the ledger to replay + /** + * @return The parent of the ledger to replay */ [[nodiscard]] std::shared_ptr const& parent() const @@ -33,7 +34,8 @@ public: return parent_; } - /** @return The ledger to replay + /** + * @return The ledger to replay */ [[nodiscard]] std::shared_ptr const& replay() const @@ -41,7 +43,8 @@ public: return replay_; } - /** @return Transactions in the order they should be replayed + /** + * @return Transactions in the order they should be replayed */ [[nodiscard]] std::map> const& orderedTxns() const diff --git a/src/xrpld/app/ledger/LedgerReplayTask.h b/src/xrpld/app/ledger/LedgerReplayTask.h index 09329761c1..d908a36fc0 100644 --- a/src/xrpld/app/ledger/LedgerReplayTask.h +++ b/src/xrpld/app/ledger/LedgerReplayTask.h @@ -64,7 +64,9 @@ public: bool update(uint256 const& hash, std::uint32_t seq, std::vector const& sList); - /** check if this task can be merged into an existing task */ + /** + * check if this task can be merged into an existing task + */ [[nodiscard]] bool canMergeInto(TaskParameter const& existingTask) const; }; @@ -87,7 +89,9 @@ public: ~LedgerReplayTask() override; - /** Start the task */ + /** + * Start the task + */ void init(); @@ -105,7 +109,9 @@ public: return parameter_; } - /** return if the task is finished */ + /** + * return if the task is finished + */ bool finished() const; diff --git a/src/xrpld/app/ledger/LedgerReplayer.h b/src/xrpld/app/ledger/LedgerReplayer.h index d44289121c..6feb187df6 100644 --- a/src/xrpld/app/ledger/LedgerReplayer.h +++ b/src/xrpld/app/ledger/LedgerReplayer.h @@ -78,7 +78,9 @@ public: void replay(InboundLedger::Reason r, uint256 const& finishLedgerHash, std::uint32_t totalNumLedgers); - /** Create LedgerDeltaAcquire subtasks for the LedgerReplayTask task */ + /** + * Create LedgerDeltaAcquire subtasks for the LedgerReplayTask task + */ void createDeltas(std::shared_ptr task); @@ -102,7 +104,9 @@ public: LedgerHeader const& info, std::map>&& txns); - /** Remove completed tasks */ + /** + * Remove completed tasks + */ void sweep(); diff --git a/src/xrpld/app/ledger/LedgerToJson.h b/src/xrpld/app/ledger/LedgerToJson.h index c9939fd2f4..1eac4d68f1 100644 --- a/src/xrpld/app/ledger/LedgerToJson.h +++ b/src/xrpld/app/ledger/LedgerToJson.h @@ -44,17 +44,22 @@ struct LedgerFill std::optional closeTime; }; -/** Given a Ledger and options, fill a json::Value with a - description of the ledger. +/** + * Given a Ledger and options, fill a json::Value with a + * description of the ledger. */ void addJson(json::Value&, LedgerFill const&); -/** Return a new json::Value representing the ledger with given options.*/ +/** + * Return a new json::Value representing the ledger with given options. + */ json::Value getJson(LedgerFill const&); -/** Copy all the keys and values from one object into another. */ +/** + * Copy all the keys and values from one object into another. + */ void copyFrom(json::Value& to, json::Value const& from); diff --git a/src/xrpld/app/ledger/OpenLedger.h b/src/xrpld/app/ledger/OpenLedger.h index 3e0577a9be..4a0aa105f9 100644 --- a/src/xrpld/app/ledger/OpenLedger.h +++ b/src/xrpld/app/ledger/OpenLedger.h @@ -36,7 +36,9 @@ using OrderedTxs = CanonicalTXSet; //------------------------------------------------------------------------------ -/** Represents the open ledger. */ +/** + * Represents the open ledger. + */ class OpenLedger { private: @@ -47,17 +49,18 @@ private: std::shared_ptr current_; public: - /** Signature for modification functions. - - The modification function is called during - apply and modify with an OpenView to accumulate - changes and the Journal to use for logging. - - A return value of `true` informs OpenLedger - that changes were made. Always returning - `true` won't cause harm, but it may be - sub-optimal. - */ + /** + * Signature for modification functions. + * + * The modification function is called during + * apply and modify with an OpenView to accumulate + * changes and the Journal to use for logging. + * + * A return value of `true` informs OpenLedger + * that changes were made. Always returning + * `true` won't cause harm, but it may be + * sub-optimal. + */ using modify_type = std::function; OpenLedger() = delete; @@ -65,90 +68,95 @@ public: OpenLedger& operator=(OpenLedger const&) = delete; - /** Create a new open ledger object. - - @param ledger A closed ledger - */ + /** + * Create a new open ledger object. + * + * @param ledger A closed ledger + */ explicit OpenLedger( std::shared_ptr const& ledger, CachedSLEs& cache, beast::Journal journal); - /** Returns `true` if there are no transactions. - - The behavior of ledger closing can be different - depending on whether or not transactions exist - in the open ledger. - - @note The value returned is only meaningful for - that specific instant in time. An open, - empty ledger can become non empty from - subsequent modifications. Caller is - responsible for synchronizing the meaning of - the return value. - */ + /** + * Returns `true` if there are no transactions. + * + * The behavior of ledger closing can be different + * depending on whether or not transactions exist + * in the open ledger. + * + * @note The value returned is only meaningful for + * that specific instant in time. An open, + * empty ledger can become non empty from + * subsequent modifications. Caller is + * responsible for synchronizing the meaning of + * the return value. + */ bool empty() const; - /** Returns a view to the current open ledger. - - Thread safety: - Can be called concurrently from any thread. - - Effects: - The caller is given ownership of a - non-modifiable snapshot of the open ledger - at the time of the call. - */ + /** + * Returns a view to the current open ledger. + * + * Thread safety: + * Can be called concurrently from any thread. + * + * Effects: + * The caller is given ownership of a + * non-modifiable snapshot of the open ledger + * at the time of the call. + */ std::shared_ptr current() const; - /** Modify the open ledger - - Thread safety: - Can be called concurrently from any thread. - - If `f` returns `true`, the changes made in the - OpenView will be published to the open ledger. - - @return `true` if the open view was changed - */ + /** + * Modify the open ledger + * + * Thread safety: + * Can be called concurrently from any thread. + * + * If `f` returns `true`, the changes made in the + * OpenView will be published to the open ledger. + * + * @return `true` if the open view was changed + */ bool modify(modify_type const& f); - /** Accept a new ledger. - - Thread safety: - Can be called concurrently from any thread. - - Effects: - - A new open view based on the accepted ledger - is created, and the list of retriable - transactions is optionally applied first - depending on the value of `retriesFirst`. - - The transactions in the current open view - are applied to the new open view. - - The list of local transactions are applied - to the new open view. - - The optional modify function f is called - to perform further modifications to the - open view, atomically. Changes made in - the modify function are not visible to - callers until accept() returns. - - Any failed, retriable transactions are left - in `retries` for the caller. - - The current view is atomically set to the - new open view. - - @param rules The rules for the open ledger - @param ledger A new closed ledger - */ + /** + * Accept a new ledger. + * + * Thread safety: + * Can be called concurrently from any thread. + * + * Effects: + * + * A new open view based on the accepted ledger + * is created, and the list of retriable + * transactions is optionally applied first + * depending on the value of `retriesFirst`. + * + * The transactions in the current open view + * are applied to the new open view. + * + * The list of local transactions are applied + * to the new open view. + * + * The optional modify function f is called + * to perform further modifications to the + * open view, atomically. Changes made in + * the modify function are not visible to + * callers until accept() returns. + * + * Any failed, retriable transactions are left + * in `retries` for the caller. + * + * The current view is atomically set to the + * new open view. + * + * @param rules The rules for the open ledger + * @param ledger A new closed ledger + */ void accept( Application& app, @@ -162,11 +170,12 @@ public: modify_type const& f = {}); private: - /** Algorithm for applying transactions. - - This has the retry logic and ordering semantics - used for consensus and building the open ledger. - */ + /** + * Algorithm for applying transactions. + * + * This has the retry logic and ordering semantics + * used for consensus and building the open ledger. + */ template static void apply( diff --git a/src/xrpld/app/ledger/OrderBookDBImpl.h b/src/xrpld/app/ledger/OrderBookDBImpl.h index d57d051cce..5f436a6946 100644 --- a/src/xrpld/app/ledger/OrderBookDBImpl.h +++ b/src/xrpld/app/ledger/OrderBookDBImpl.h @@ -20,19 +20,22 @@ namespace xrpl { -/** Configuration for OrderBookDB */ +/** + * Configuration for OrderBookDB + */ struct OrderBookDBConfig { int pathSearchMax; bool standalone; }; -/** Create an OrderBookDB instance. - - @param registry Service registry for accessing other services - @param config Configuration parameters - @return A new OrderBookDB instance -*/ +/** + * Create an OrderBookDB instance. + * + * @param registry Service registry for accessing other services + * @param config Configuration parameters + * @return A new OrderBookDB instance + */ std::unique_ptr makeOrderBookDb(ServiceRegistry& registry, OrderBookDBConfig const& config); diff --git a/src/xrpld/app/ledger/detail/BuildLedger.cpp b/src/xrpld/app/ledger/detail/BuildLedger.cpp index d11e0610ba..e9c01c7133 100644 --- a/src/xrpld/app/ledger/detail/BuildLedger.cpp +++ b/src/xrpld/app/ledger/detail/BuildLedger.cpp @@ -81,15 +81,16 @@ buildLedgerImpl( return built; } -/** Apply a set of consensus transactions to a ledger. - - @param app Handle to application - @param txns the set of transactions to apply, - @param failed set of transactions that failed to apply - @param view ledger to apply to - @param j Journal for logging - @return number of transactions applied; transactions to retry left in txns -*/ +/** + * Apply a set of consensus transactions to a ledger. + * + * @param app Handle to application + * @param txns the set of transactions to apply, + * @param failed set of transactions that failed to apply + * @param view ledger to apply to + * @param j Journal for logging + * @return number of transactions applied; transactions to retry left in txns + */ std::size_t applyTransactions( diff --git a/src/xrpld/app/ledger/detail/InboundLedger.cpp b/src/xrpld/app/ledger/detail/InboundLedger.cpp index 4d34f60374..627a5d574f 100644 --- a/src/xrpld/app/ledger/detail/InboundLedger.cpp +++ b/src/xrpld/app/ledger/detail/InboundLedger.cpp @@ -336,7 +336,8 @@ InboundLedger::tryDB(NodeStore::Database& srcDB) } } -/** Called with a lock by the PeerSet when the timer expires +/** + * Called with a lock by the PeerSet when the timer expires */ void InboundLedger::onTimer(bool wasProgress, ScopedLockType&) @@ -385,7 +386,9 @@ InboundLedger::onTimer(bool wasProgress, ScopedLockType&) } } -/** Add more peers to the set, if possible */ +/** + * Add more peers to the set, if possible + */ void InboundLedger::addPeers() { @@ -454,7 +457,8 @@ InboundLedger::done() }); } -/** Request more nodes, perhaps from a specific peer +/** + * Request more nodes, perhaps from a specific peer */ void InboundLedger::trigger(std::shared_ptr const& peer, TriggerReason reason) @@ -769,9 +773,10 @@ InboundLedger::filterNodes( recentNodes_.insert(n.second); } -/** Take ledger header data - Call with a lock -*/ +/** + * Take ledger header data + * Call with a lock + */ // data must not have hash prefix bool InboundLedger::takeHeader(std::string const& data) @@ -815,9 +820,10 @@ InboundLedger::takeHeader(std::string const& data) return true; } -/** Process node data received from a peer - Call with a lock -*/ +/** + * Process node data received from a peer + * Call with a lock + */ void InboundLedger::receiveNode(protocol::TMLedgerData const& packet, SHAMapAddNode& san) { @@ -911,9 +917,10 @@ InboundLedger::receiveNode(protocol::TMLedgerData const& packet, SHAMapAddNode& } } -/** Process AS root node received from a peer - Call with a lock -*/ +/** + * Process AS root node received from a peer + * Call with a lock + */ bool InboundLedger::takeAsRootNode(Slice const& data, SHAMapAddNode& san) { @@ -937,9 +944,10 @@ InboundLedger::takeAsRootNode(Slice const& data, SHAMapAddNode& san) return san.isGood(); } -/** Process AS root node received from a peer - Call with a lock -*/ +/** + * Process AS root node received from a peer + * Call with a lock + */ bool InboundLedger::takeTxRootNode(Slice const& data, SHAMapAddNode& san) { @@ -994,9 +1002,10 @@ InboundLedger::getNeededHashes() return ret; } -/** Stash a TMLedgerData received from a peer for later processing - Returns 'true' if we need to dispatch -*/ +/** + * Stash a TMLedgerData received from a peer for later processing + * Returns 'true' if we need to dispatch + */ bool InboundLedger::gotData( std::weak_ptr peer, @@ -1016,9 +1025,10 @@ InboundLedger::gotData( return true; } -/** Process one TMLedgerData - Returns the number of useful nodes -*/ +/** + * Process one TMLedgerData + * Returns the number of useful nodes + */ // VFALCO NOTE, it is not necessary to pass the entire Peer, // we can get away with just a Resource::Consumer endpoint. // @@ -1193,9 +1203,10 @@ struct PeerDataCounts }; } // namespace detail -/** Process pending TMLedgerData - Query the a random sample of the 'best' peers -*/ +/** + * Process pending TMLedgerData + * Query the a random sample of the 'best' peers + */ void InboundLedger::runData() { diff --git a/src/xrpld/app/ledger/detail/InboundLedgers.cpp b/src/xrpld/app/ledger/detail/InboundLedgers.cpp index 07daa7560e..dc361694cf 100644 --- a/src/xrpld/app/ledger/detail/InboundLedgers.cpp +++ b/src/xrpld/app/ledger/detail/InboundLedgers.cpp @@ -70,7 +70,9 @@ public: { } - /** @callgraph */ + /** + * @callgraph + */ std::shared_ptr acquire(uint256 const& hash, std::uint32_t seq, InboundLedger::Reason reason) override { @@ -182,7 +184,8 @@ public: // means "We got some data from an inbound ledger" // VFALCO TODO Remove the dependency on the Peer object. - /** We received a TMLedgerData from a peer. + /** + * We received a TMLedgerData from a peer. */ bool gotLedgerData( @@ -236,12 +239,13 @@ public: return recentFailures_.find(h) != recentFailures_.end(); } - /** We got some data for a ledger we are no longer acquiring Since we paid - the price to receive it, we might as well stash it in case we need it. - - Nodes are received in wire format and must be stashed/hashed in prefix - format - */ + /** + * We got some data for a ledger we are no longer acquiring Since we paid + * the price to receive it, we might as well stash it in case we need it. + * + * Nodes are received in wire format and must be stashed/hashed in prefix + * format + */ void gotStaleData(std::shared_ptr packetPtr) override { diff --git a/src/xrpld/app/ledger/detail/InboundTransactions.cpp b/src/xrpld/app/ledger/detail/InboundTransactions.cpp index d744075869..9b50a1584f 100644 --- a/src/xrpld/app/ledger/detail/InboundTransactions.cpp +++ b/src/xrpld/app/ledger/detail/InboundTransactions.cpp @@ -119,7 +119,8 @@ public: return {}; } - /** We received a TMLedgerData from a peer. + /** + * We received a TMLedgerData from a peer. */ void gotData( diff --git a/src/xrpld/app/ledger/detail/LedgerCleaner.cpp b/src/xrpld/app/ledger/detail/LedgerCleaner.cpp index b96f01e577..d3ece3c036 100644 --- a/src/xrpld/app/ledger/detail/LedgerCleaner.cpp +++ b/src/xrpld/app/ledger/detail/LedgerCleaner.cpp @@ -260,13 +260,14 @@ private: return hash ? *hash : beast::kZero; // kludge } - /** Process a single ledger - @param ledgerIndex The index of the ledger to process. - @param ledgerHash The known correct hash of the ledger. - @param doNodes Ensure all ledger nodes are in the node db. - @param doTxns Reprocess (account) transactions to SQL databases. - @return `true` if the ledger was cleaned. - */ + /** + * Process a single ledger + * @param ledgerIndex The index of the ledger to process. + * @param ledgerHash The known correct hash of the ledger. + * @param doNodes Ensure all ledger nodes are in the node db. + * @param doTxns Reprocess (account) transactions to SQL databases. + * @return `true` if the ledger was cleaned. + */ bool doLedger( LedgerIndex const& ledgerIndex, @@ -320,11 +321,12 @@ private: return true; } - /** Returns the hash of the specified ledger. - @param ledgerIndex The index of the desired ledger. - @param referenceLedger [out] An optional known good subsequent ledger. - @return The hash of the ledger. This will be all-bits-zero if not found. - */ + /** + * Returns the hash of the specified ledger. + * @param ledgerIndex The index of the desired ledger. + * @param referenceLedger [out] An optional known good subsequent ledger. + * @return The hash of the ledger. This will be all-bits-zero if not found. + */ LedgerHash getHash(LedgerIndex const& ledgerIndex, std::shared_ptr& referenceLedger) { @@ -373,7 +375,9 @@ private: return ledgerHash; } - /** Run the ledger cleaner. */ + /** + * Run the ledger cleaner. + */ void doLedgerCleaner() { diff --git a/src/xrpld/app/ledger/detail/LedgerMaster.cpp b/src/xrpld/app/ledger/detail/LedgerMaster.cpp index bab0dca827..2bd83b0f18 100644 --- a/src/xrpld/app/ledger/detail/LedgerMaster.cpp +++ b/src/xrpld/app/ledger/detail/LedgerMaster.cpp @@ -455,11 +455,12 @@ LedgerMaster::storeLedger(std::shared_ptr ledger) return ledgerHistory_.insert(ledger, validated); } -/** Apply held transactions to the open ledger - This is normally called as we close the ledger. - The open ledger remains open to handle new transactions - until a new open ledger is built. -*/ +/** + * Apply held transactions to the open ledger + * This is normally called as we close the ledger. + * The open ledger remains open to handle new transactions + * until a new open ledger is built. + */ void LedgerMaster::applyHeldTransactions() { @@ -710,7 +711,8 @@ LedgerMaster::tryFill(std::shared_ptr ledger) } } -/** Request a fetch pack to get to the specified ledger +/** + * Request a fetch pack to get to the specified ledger */ void LedgerMaster::getFetchPack(LedgerIndex missing, InboundLedger::Reason reason) @@ -1081,7 +1083,9 @@ LedgerMaster::checkAccept(std::shared_ptr const& ledger) } } -/** Report that the consensus process built a particular ledger */ +/** + * Report that the consensus process built a particular ledger + */ void LedgerMaster::consensusBuilt( std::shared_ptr const& ledger, @@ -1511,7 +1515,8 @@ LedgerMaster::newOrderBookDB() return newPFWork("PthFindOBDB", ml); } -/** A thread needs to be dispatched to handle pathfinding work of some kind. +/** + * A thread needs to be dispatched to handle pathfinding work of some kind. */ bool LedgerMaster::newPFWork(char const* name, std::unique_lock&) @@ -1996,30 +2001,31 @@ LedgerMaster::gotFetchPack(bool progress, std::uint32_t seq) } } -/** Populate a fetch pack with data from the map the recipient wants. - - A recipient may or may not have the map that they are asking for. If - they do, we can optimize the transfer by not including parts of the - map that they are already have. - - @param have The map that the recipient already has (if any). - @param cnt The maximum number of nodes to return. - @param into The protocol object into which we add information. - @param seq The sequence number of the ledger the map is a part of. - @param withLeaves True if leaf nodes should be included. - - @note: The withLeaves parameter is configurable even though the - code, so far, only ever sets the parameter to true. - - The rationale is that for transaction trees, it may make - sense to not include the leaves if the fetch pack is being - constructed for someone attempting to get a recent ledger - for which they already have the transactions. - - However, for historical ledgers, which is the only use we - have for fetch packs right now, it makes sense to include - the transactions because the caller is unlikely to have - them. +/** + * Populate a fetch pack with data from the map the recipient wants. + * + * A recipient may or may not have the map that they are asking for. If + * they do, we can optimize the transfer by not including parts of the + * map that they are already have. + * + * @param have The map that the recipient already has (if any). + * @param cnt The maximum number of nodes to return. + * @param into The protocol object into which we add information. + * @param seq The sequence number of the ledger the map is a part of. + * @param withLeaves True if leaf nodes should be included. + * + * @note: The withLeaves parameter is configurable even though the + * code, so far, only ever sets the parameter to true. + * + * The rationale is that for transaction trees, it may make + * sense to not include the leaves if the fetch pack is being + * constructed for someone attempting to get a recent ledger + * for which they already have the transactions. + * + * However, for historical ledgers, which is the only use we + * have for fetch packs right now, it makes sense to include + * the transactions because the caller is unlikely to have + * them. */ static void populateFetchPack( diff --git a/src/xrpld/app/ledger/detail/TimeoutCounter.h b/src/xrpld/app/ledger/detail/TimeoutCounter.h index ab4dd28e47..682abf1537 100644 --- a/src/xrpld/app/ledger/detail/TimeoutCounter.h +++ b/src/xrpld/app/ledger/detail/TimeoutCounter.h @@ -18,39 +18,39 @@ namespace xrpl { /** - This class is an "active" object. It maintains its own timer - and dispatches work to a job queue. Implementations derive - from this class and override the abstract hook functions in - the base. - - This class implements an asynchronous loop: - - 1. The entry point is `setTimer`. - - 2. After `timerInterval_`, `queueJob` is called, which schedules a job to - call `invokeOnTimer` (or loops back to setTimer if there are too many - concurrent jobs). - - 3. The job queue calls `invokeOnTimer` which either breaks the loop if - `isDone` or calls `onTimer`. - - 4. `onTimer` is the only "real" virtual method in this class. It is the - callback for when the timeout expires. Generally, its only responsibility - is to set `failed_ = true`. However, if it wants to implement a policy of - retries, then it has a chance to just increment a count of expired - timeouts. - - 5. Once `onTimer` returns, if the object is still not `isDone`, then - `invokeOnTimer` sets another timeout by looping back to setTimer. - - This loop executes concurrently with another asynchronous sequence, - implemented by the subtype, that is trying to make progress and eventually - set `complete_ = true`. While it is making progress but not complete, it - should set `progress_ = true`, which is passed to onTimer so it can decide - whether to postpone failure and reset the timeout. However, if it can - complete all its work in one synchronous step (while it holds the lock), then - it can ignore `progress_`. -*/ + * This class is an "active" object. It maintains its own timer + * and dispatches work to a job queue. Implementations derive + * from this class and override the abstract hook functions in + * the base. + * + * This class implements an asynchronous loop: + * + * 1. The entry point is `setTimer`. + * + * 2. After `timerInterval_`, `queueJob` is called, which schedules a job to + * call `invokeOnTimer` (or loops back to setTimer if there are too many + * concurrent jobs). + * + * 3. The job queue calls `invokeOnTimer` which either breaks the loop if + * `isDone` or calls `onTimer`. + * + * 4. `onTimer` is the only "real" virtual method in this class. It is the + * callback for when the timeout expires. Generally, its only responsibility + * is to set `failed_ = true`. However, if it wants to implement a policy of + * retries, then it has a chance to just increment a count of expired + * timeouts. + * + * 5. Once `onTimer` returns, if the object is still not `isDone`, then + * `invokeOnTimer` sets another timeout by looping back to setTimer. + * + * This loop executes concurrently with another asynchronous sequence, + * implemented by the subtype, that is trying to make progress and eventually + * set `complete_ = true`. While it is making progress but not complete, it + * should set `progress_ = true`, which is passed to onTimer so it can decide + * whether to postpone failure and reset the timeout. However, if it can + * complete all its work in one synchronous step (while it holds the lock), then + * it can ignore `progress_`. + */ class TimeoutCounter { public: @@ -84,19 +84,27 @@ protected: QueueJobParameter&& jobParameter, beast::Journal journal); - /** Schedule a call to queueJob() after timerInterval_. */ + /** + * Schedule a call to queueJob() after timerInterval_. + */ void setTimer(ScopedLockType&); - /** Queue a job to call invokeOnTimer(). */ + /** + * Queue a job to call invokeOnTimer(). + */ void queueJob(ScopedLockType&); - /** Hook called from invokeOnTimer(). */ + /** + * Hook called from invokeOnTimer(). + */ virtual void onTimer(bool progress, ScopedLockType&) = 0; - /** Return a weak pointer to this. */ + /** + * Return a weak pointer to this. + */ virtual std::weak_ptr pmDowncast() = 0; @@ -112,21 +120,28 @@ protected: beast::Journal journal_; mutable std::recursive_mutex mtx_; - /** The hash of the object (in practice, always a ledger) we are trying to - * fetch. */ + /** + * The hash of the object (in practice, always a ledger) we are trying to + * fetch. + */ uint256 const hash_; int timeouts_{0}; bool complete_{false}; bool failed_{false}; - /** Whether forward progress has been made. */ + /** + * Whether forward progress has been made. + */ bool progress_{false}; - /** The minimum time to wait between calls to execute(). */ + /** + * The minimum time to wait between calls to execute(). + */ std::chrono::milliseconds timerInterval_; QueueJobParameter queueJobParameter_; private: - /** Calls onTimer() if in the right state. + /** + * Calls onTimer() if in the right state. * Only called by queueJob(). */ void diff --git a/src/xrpld/app/main/Application.h b/src/xrpld/app/main/Application.h index 33876b97b9..225275afe4 100644 --- a/src/xrpld/app/main/Application.h +++ b/src/xrpld/app/main/Application.h @@ -129,7 +129,9 @@ public: // --- // - /** Returns a 64-bit instance identifier, generated at startup */ + /** + * Returns a 64-bit instance identifier, generated at startup + */ [[nodiscard]] virtual std::uint64_t instanceID() const = 0; @@ -152,12 +154,16 @@ public: [[nodiscard]] virtual int fdRequired() const = 0; - /** Ensure that a newly-started validator does not sign proposals older - * than the last ledger it persisted. */ + /** + * Ensure that a newly-started validator does not sign proposals older + * than the last ledger it persisted. + */ virtual LedgerIndex getMaxDisallowedLedger() = 0; - /** Returns the number of io_context (I/O worker) threads used by the application. */ + /** + * Returns the number of io_context (I/O worker) threads used by the application. + */ [[nodiscard]] virtual size_t getNumberOfThreads() const = 0; }; diff --git a/src/xrpld/app/main/CollectorManager.h b/src/xrpld/app/main/CollectorManager.h index e695ddc956..fac72b4de1 100644 --- a/src/xrpld/app/main/CollectorManager.h +++ b/src/xrpld/app/main/CollectorManager.h @@ -10,7 +10,9 @@ namespace xrpl { -/** Provides the beast::insight::Collector service. */ +/** + * Provides the beast::insight::Collector service. + */ class CollectorManager { public: diff --git a/src/xrpld/app/main/LoadManager.h b/src/xrpld/app/main/LoadManager.h index 794048567a..5d3f07e996 100644 --- a/src/xrpld/app/main/LoadManager.h +++ b/src/xrpld/app/main/LoadManager.h @@ -12,17 +12,18 @@ namespace xrpl { class Application; -/** Manages load sources. - - This object creates an associated thread to maintain a clock. - - When the server is overloaded by a particular peer it issues a warning - first. This allows friendly peers to reduce their consumption of resources, - or disconnect from the server. - - The warning system is used instead of merely dropping, because hostile - peers can just reconnect anyway. -*/ +/** + * Manages load sources. + * + * This object creates an associated thread to maintain a clock. + * + * When the server is overloaded by a particular peer it issues a warning + * first. This allows friendly peers to reduce their consumption of resources, + * or disconnect from the server. + * + * The warning system is used instead of merely dropping, because hostile + * peers can just reconnect anyway. + */ class LoadManager { LoadManager(Application& app, beast::Journal journal); @@ -33,20 +34,22 @@ public: LoadManager& operator=(LoadManager const&) = delete; - /** Destroy the manager. - - The destructor returns only after the thread has stopped. - */ + /** + * Destroy the manager. + * + * The destructor returns only after the thread has stopped. + */ ~LoadManager(); - /** Turn on stall detection. - - The stall detector begins in a disabled state. After this function - is called, it will report stalls using a separate thread whenever - the reset function is not called at least once per 10 seconds. - - @see resetStallDetector - */ + /** + * Turn on stall detection. + * + * The stall detector begins in a disabled state. After this function + * is called, it will report stalls using a separate thread whenever + * the reset function is not called at least once per 10 seconds. + * + * @see resetStallDetector + */ // VFALCO NOTE it seems that the stall detector has an "armed" state // to prevent it from going off during program startup if // there's a lengthy initialization operation taking place? @@ -54,11 +57,12 @@ public: void activateStallDetector(); - /** Reset the stall detection timer. - - A dedicated thread monitors the stall timer, and if too much - time passes it will produce log warnings. - */ + /** + * Reset the stall detection timer. + * + * A dedicated thread monitors the stall timer, and if too much + * time passes it will produce log warnings. + */ void heartbeat(); diff --git a/src/xrpld/app/main/NodeIdentity.h b/src/xrpld/app/main/NodeIdentity.h index 789d061021..117acffdb1 100644 --- a/src/xrpld/app/main/NodeIdentity.h +++ b/src/xrpld/app/main/NodeIdentity.h @@ -11,10 +11,11 @@ namespace xrpl { -/** The cryptographic credentials identifying this server instance. - - @param app The application object - @param cmdline The command line parameters passed into the application. +/** + * The cryptographic credentials identifying this server instance. + * + * @param app The application object + * @param cmdline The command line parameters passed into the application. */ std::pair getNodeIdentity(Application& app, boost::program_options::variables_map const& cmdline); diff --git a/src/xrpld/app/main/NodeStoreScheduler.h b/src/xrpld/app/main/NodeStoreScheduler.h index 48e606bb45..8bfd1607ae 100644 --- a/src/xrpld/app/main/NodeStoreScheduler.h +++ b/src/xrpld/app/main/NodeStoreScheduler.h @@ -6,7 +6,9 @@ namespace xrpl { -/** A NodeStore::Scheduler which uses the JobQueue. */ +/** + * A NodeStore::Scheduler which uses the JobQueue. + */ class NodeStoreScheduler : public NodeStore::Scheduler { public: diff --git a/src/xrpld/app/misc/DeliverMax.h b/src/xrpld/app/misc/DeliverMax.h index fefa59d46b..73ccc95800 100644 --- a/src/xrpld/app/misc/DeliverMax.h +++ b/src/xrpld/app/misc/DeliverMax.h @@ -9,13 +9,13 @@ class Value; namespace xrpl::RPC { /** - Copy `Amount` field to `DeliverMax` field in transaction output JSON. - This only applies to Payment transaction type, all others are ignored. - - When apiVersion > 1 will also remove `Amount` field, forcing users - to access this value using new `DeliverMax` field only. - @{ + * Copy `Amount` field to `DeliverMax` field in transaction output JSON. + * This only applies to Payment transaction type, all others are ignored. + * + * When apiVersion > 1 will also remove `Amount` field, forcing users + * to access this value using new `DeliverMax` field only. */ +/** @{ */ void insertDeliverMax(json::Value& txJson, TxType txnType, unsigned int apiVersion); diff --git a/src/xrpld/app/misc/FeeVote.h b/src/xrpld/app/misc/FeeVote.h index d6b4b0fc6a..22b2d888f3 100644 --- a/src/xrpld/app/misc/FeeVote.h +++ b/src/xrpld/app/misc/FeeVote.h @@ -12,25 +12,29 @@ namespace xrpl { -/** Manager to process fee votes. */ +/** + * Manager to process fee votes. + */ class FeeVote { public: virtual ~FeeVote() = default; - /** Add local fee preference to validation. - - @param lastClosedLedger - @param baseValidation - */ + /** + * Add local fee preference to validation. + * + * @param lastClosedLedger + * @param baseValidation + */ virtual void doValidation(Fees const& lastFees, Rules const& rules, STValidation& val) = 0; - /** Cast our local vote on the fee. - - @param lastClosedLedger - @param initialPosition - */ + /** + * Cast our local vote on the fee. + * + * @param lastClosedLedger + * @param initialPosition + */ virtual void doVoting( std::shared_ptr const& lastClosedLedger, @@ -39,10 +43,11 @@ public: }; struct FeeSetup; -/** Create an instance of the FeeVote logic. - @param setup The fee schedule to vote for. - @param journal Where to log. -*/ +/** + * Create an instance of the FeeVote logic. + * @param setup The fee schedule to vote for. + * @param journal Where to log. + */ std::unique_ptr makeFeeVote(FeeSetup const& setup, beast::Journal journal); diff --git a/src/xrpld/app/misc/NegativeUNLVote.h b/src/xrpld/app/misc/NegativeUNLVote.h index 2896962a84..a01bf04dcf 100644 --- a/src/xrpld/app/misc/NegativeUNLVote.h +++ b/src/xrpld/app/misc/NegativeUNLVote.h @@ -141,8 +141,8 @@ private: * Pick one candidate from a vector of candidates. * * @param randomPadData the data used for picking a candidate. - * @note Nodes must use the same randomPadData for picking the same - * candidate. The hash of the parent ledger is used. + * @note Nodes must use the same randomPadData for picking the same + * candidate. The hash of the parent ledger is used. * @param candidates the vector of candidates * @return the picked candidate */ diff --git a/src/xrpld/app/misc/NetworkOPs.cpp b/src/xrpld/app/misc/NetworkOPs.cpp index 4d40247a29..14d23b26d5 100644 --- a/src/xrpld/app/misc/NetworkOPs.cpp +++ b/src/xrpld/app/misc/NetworkOPs.cpp @@ -266,7 +266,9 @@ class NetworkOPsImp final : public NetworkOPs } }; - //! Server fees published on `server` subscription + /** + * Server fees published on `server` subscription + */ struct ServerFeeSummary { ServerFeeSummary() = default; @@ -469,9 +471,10 @@ public: void setStandAlone() override; - /** Called to initially start our timers. - Not called for stand-alone mode. - */ + /** + * Called to initially start our timers. + * Not called for stand-alone mode. + */ void setStateTimer() override; @@ -838,7 +841,8 @@ private: LedgerMaster& ledgerMaster_; - /** Maps each order book to its current set of subscribers. + /** + * Maps each order book to its current set of subscribers. * Outer key: the Book (currency pair + optional domain). * Inner key: InfoSub::seq (unique per connection). * Inner value: weak_ptr so that a dropped connection does not prevent diff --git a/src/xrpld/app/misc/SHAMapStore.h b/src/xrpld/app/misc/SHAMapStore.h index 9f12546463..df696c685f 100644 --- a/src/xrpld/app/misc/SHAMapStore.h +++ b/src/xrpld/app/misc/SHAMapStore.h @@ -25,7 +25,9 @@ class SHAMapStore public: virtual ~SHAMapStore() = default; - /** Called by LedgerMaster every time a ledger validates. */ + /** + * Called by LedgerMaster every time a ledger validates. + */ virtual void onLedgerClosed(std::shared_ptr const& ledger) = 0; @@ -44,44 +46,54 @@ public: virtual std::unique_ptr makeNodeStore(int readThreads) = 0; - /** Highest ledger that may be deleted. */ + /** + * Highest ledger that may be deleted. + */ virtual LedgerIndex setCanDelete(LedgerIndex canDelete) = 0; - /** Whether advisory delete is enabled. */ + /** + * Whether advisory delete is enabled. + */ [[nodiscard]] virtual bool advisoryDelete() const = 0; - /** Maximum ledger that has been deleted, or will be deleted if + /** + * Maximum ledger that has been deleted, or will be deleted if * currently in the act of online deletion. */ virtual LedgerIndex getLastRotated() = 0; - /** Highest ledger that may be deleted. */ + /** + * Highest ledger that may be deleted. + */ virtual LedgerIndex getCanDelete() = 0; - /** Returns the number of file descriptors that are needed. */ + /** + * Returns the number of file descriptors that are needed. + */ [[nodiscard]] virtual int fdRequired() const = 0; - /** The minimum ledger to try and maintain in our database. - - This defines the lower bound for attempting to acquire historical - ledgers over the peer to peer network. - - If online_delete is enabled, then each time online_delete executes - and just prior to clearing SQL databases of historical ledgers, - move the value forward to one past the greatest ledger being deleted. - This minimizes fetching of ledgers that are in the process of being - deleted. Without online_delete or before online_delete is - executed, this value is always the minimum value persisted in the - ledger database, if any. - - @return The minimum ledger sequence to keep online based on the - description above. If not set, then an unseated optional. - */ + /** + * The minimum ledger to try and maintain in our database. + * + * This defines the lower bound for attempting to acquire historical + * ledgers over the peer to peer network. + * + * If online_delete is enabled, then each time online_delete executes + * and just prior to clearing SQL databases of historical ledgers, + * move the value forward to one past the greatest ledger being deleted. + * This minimizes fetching of ledgers that are in the process of being + * deleted. Without online_delete or before online_delete is + * executed, this value is always the minimum value persisted in the + * ledger database, if any. + * + * @return The minimum ledger sequence to keep online based on the + * description above. If not set, then an unseated optional. + */ [[nodiscard]] virtual std::optional minimumOnline() const = 0; }; diff --git a/src/xrpld/app/misc/SHAMapStoreImp.h b/src/xrpld/app/misc/SHAMapStoreImp.h index 4025236868..a0ca59ecc8 100644 --- a/src/xrpld/app/misc/SHAMapStoreImp.h +++ b/src/xrpld/app/misc/SHAMapStoreImp.h @@ -101,10 +101,12 @@ private: std::uint32_t deleteBatch_ = 100; std::chrono::milliseconds backOff_{100}; std::chrono::seconds ageThreshold_{60}; - /// If the node is out of sync during an online_delete healthWait() - /// call, sleep the thread for this time, and continue checking until - /// recovery. - /// See also: "recovery_wait_seconds" in xrpld-example.cfg + /** + * If the node is out of sync during an online_delete healthWait() + * call, sleep the thread for this time, and continue checking until + * recovery. + * See also: "recovery_wait_seconds" in xrpld-example.cfg + */ std::chrono::seconds recoveryWaitTime_{5}; // these do not exist upon SHAMapStore creation, but do exist @@ -197,7 +199,8 @@ private: return false; } - /** delete from sqlite table in batches to not lock the db excessively. + /** + * delete from sqlite table in batches to not lock the db excessively. * Pause briefly to extend access time to other users. * Call with mutex object unlocked. */ diff --git a/src/xrpld/app/misc/Transaction.h b/src/xrpld/app/misc/Transaction.h index ab0fa1f4d8..b6b6d1a8d5 100644 --- a/src/xrpld/app/misc/Transaction.h +++ b/src/xrpld/app/misc/Transaction.h @@ -310,38 +310,46 @@ public: { std::variant, ClosedInterval> locator; - // @return true if transaction was found, false otherwise - // - // Call this function first to determine the type of the contained info. - // Calling the wrong getter function will throw an exception. - // See documentation for the getter functions for more details + /** + * @return true if transaction was found, false otherwise + * + * Call this function first to determine the type of the contained info. + * Calling the wrong getter function will throw an exception. + * See documentation for the getter functions for more details + */ [[nodiscard]] bool isFound() const { return std::holds_alternative>(locator); } - // @return key used to find transaction in nodestore - // - // Throws if isFound() returns false + /** + * @return key used to find transaction in nodestore + * + * @throws if isFound() returns false + */ uint256 const& getNodestoreHash() { return std::get>(locator).first; } - // @return sequence of ledger containing the transaction - // - // Throws is isFound() returns false + /** + * @return sequence of ledger containing the transaction + * + * @throws if isFound() returns false + */ uint32_t getLedgerSequence() { return std::get>(locator).second; } - // @return range of ledgers searched - // - // Throws if isFound() returns true + /** + * @return range of ledgers searched + * + * @throws if isFound() returns true + */ ClosedInterval const& getLedgerRangeSearched() { @@ -400,7 +408,9 @@ private: */ bool applying_ = false; - /** different ways for transaction to be accepted */ + /** + * different ways for transaction to be accepted + */ SubmitResult submitResult_; std::optional currentLedgerState_; diff --git a/src/xrpld/app/misc/TxQ.h b/src/xrpld/app/misc/TxQ.h index 135cd592f0..65b4e9778c 100644 --- a/src/xrpld/app/misc/TxQ.h +++ b/src/xrpld/app/misc/TxQ.h @@ -37,160 +37,203 @@ class Application; class Config; /** - Transaction Queue. Used to manage transactions in conjunction with - fee escalation. - - Once enough transactions are added to the open ledger, the required - fee will jump dramatically. If additional transactions are added, - the fee will grow exponentially from there. - - Transactions that don't have a high enough fee to be applied to - the ledger are added to the queue in order from highest fee level to - lowest. Whenever a new ledger is accepted as validated, transactions - are first applied from the queue to the open ledger in fee level order - until either all transactions are applied or the fee again jumps - too high for the remaining transactions. - - For further information and a high-level overview of how transactions - are processed with the `TxQ`, see FeeEscalation.md -*/ + * Transaction Queue. Used to manage transactions in conjunction with + * fee escalation. + * + * Once enough transactions are added to the open ledger, the required + * fee will jump dramatically. If additional transactions are added, + * the fee will grow exponentially from there. + * + * Transactions that don't have a high enough fee to be applied to + * the ledger are added to the queue in order from highest fee level to + * lowest. Whenever a new ledger is accepted as validated, transactions + * are first applied from the queue to the open ledger in fee level order + * until either all transactions are applied or the fee again jumps + * too high for the remaining transactions. + * + * For further information and a high-level overview of how transactions + * are processed with the `TxQ`, see FeeEscalation.md + */ class TxQ { public: - /// Fee level for single-signed reference transaction. + /** + * Fee level for single-signed reference transaction. + */ static constexpr FeeLevel64 kBaseLevel{256}; /** - Structure used to customize @ref TxQ behavior. - */ + * Structure used to customize @ref TxQ behavior. + */ struct Setup { - /// Default constructor + /** + * Default constructor + */ explicit Setup() = default; - /** Number of ledgers' worth of transactions to allow - in the queue. For example, if the last ledger had - 150 transactions, then up to 3000 transactions can - be queued. - - Can be overridden by @ref queueSizeMin - */ + /** + * Number of ledgers' worth of transactions to allow + * in the queue. For example, if the last ledger had + * 150 transactions, then up to 3000 transactions can + * be queued. + * + * Can be overridden by @ref queueSizeMin + */ std::size_t ledgersInQueue = 20; - /** The smallest limit the queue is allowed. - - Will allow more than `ledgersInQueue` in the queue - if ledgers are small. - */ + /** + * The smallest limit the queue is allowed. + * + * Will allow more than `ledgersInQueue` in the queue + * if ledgers are small. + */ std::size_t queueSizeMin = 2000; - /** Extra percentage required on the fee level of a queued - transaction to replace that transaction with another - with the same SeqProxy. - - If queued transaction for account "Alice" with seq 45 - has a fee level of 512, a replacement transaction for - "Alice" with seq 45 must have a fee level of at least - 512 * (1 + 0.25) = 640 to be considered. - */ + /** + * Extra percentage required on the fee level of a queued + * transaction to replace that transaction with another + * with the same SeqProxy. + * + * If queued transaction for account "Alice" with seq 45 + * has a fee level of 512, a replacement transaction for + * "Alice" with seq 45 must have a fee level of at least + * 512 * (1 + 0.25) = 640 to be considered. + */ std::uint32_t retrySequencePercent = 25; - /// Minimum value of the escalation multiplier, regardless - /// of the prior ledger's median fee level. + /** + * Minimum value of the escalation multiplier, regardless + * of the prior ledger's median fee level. + */ FeeLevel64 minimumEscalationMultiplier = kBaseLevel * 500; - /// Minimum number of transactions to allow into the ledger - /// before escalation, regardless of the prior ledger's size. + /** + * Minimum number of transactions to allow into the ledger + * before escalation, regardless of the prior ledger's size. + */ std::uint32_t minimumTxnInLedger = 32; - /// Like @ref minimumTxnInLedger for standalone mode. - /// Primarily so that tests don't need to worry about queuing. + /** + * Like @ref minimumTxnInLedger for standalone mode. + * Primarily so that tests don't need to worry about queuing. + */ std::uint32_t minimumTxnInLedgerSA = 1000; - /// Number of transactions per ledger that fee escalation "works - /// towards". + /** + * Number of transactions per ledger that fee escalation "works + * towards". + */ std::uint32_t targetTxnInLedger = 256; - /** Optional maximum allowed value of transactions per ledger before - fee escalation kicks in. By default, the maximum is an emergent - property of network, validator, and consensus performance. This - setting can override that behavior to prevent fee escalation from - allowing more than `maximumTxnInLedger` "cheap" transactions into - the open ledger. - - @todo ximinez. This setting seems to go against our goals and - values. Can it be removed? - */ + /** + * Optional maximum allowed value of transactions per ledger before + * fee escalation kicks in. By default, the maximum is an emergent + * property of network, validator, and consensus performance. This + * setting can override that behavior to prevent fee escalation from + * allowing more than `maximumTxnInLedger` "cheap" transactions into + * the open ledger. + * + * @todo ximinez. This setting seems to go against our goals and + * values. Can it be removed? + */ std::optional maximumTxnInLedger; - /** When the ledger has more transactions than "expected", and - performance is humming along nicely, the expected ledger size - is updated to the previous ledger size plus this percentage. - - Calculations are subject to configured limits, and the recent - transactions counts buffer. - - Example: If the "expectation" is for 500 transactions, and a - ledger is validated normally with 501 transactions, then the - expected ledger size will be updated to 601. - */ + /** + * When the ledger has more transactions than "expected", and + * performance is humming along nicely, the expected ledger size + * is updated to the previous ledger size plus this percentage. + * + * Calculations are subject to configured limits, and the recent + * transactions counts buffer. + * + * Example: If the "expectation" is for 500 transactions, and a + * ledger is validated normally with 501 transactions, then the + * expected ledger size will be updated to 601. + */ std::uint32_t normalConsensusIncreasePercent = 20; - /** When consensus takes longer than appropriate, the expected - ledger size is updated to the lesser of the previous ledger - size and the current expected ledger size minus this - percentage. - - Calculations are subject to configured limits. - - Example: If the ledger has 15000 transactions, and it is - validated slowly, then the expected ledger size will be - updated to 7500. If there are only 6 transactions, the - expected ledger size will be updated to 5, assuming the - default minimum. - */ + /** + * When consensus takes longer than appropriate, the expected + * ledger size is updated to the lesser of the previous ledger + * size and the current expected ledger size minus this + * percentage. + * + * Calculations are subject to configured limits. + * + * Example: If the ledger has 15000 transactions, and it is + * validated slowly, then the expected ledger size will be + * updated to 7500. If there are only 6 transactions, the + * expected ledger size will be updated to 5, assuming the + * default minimum. + */ std::uint32_t slowConsensusDecreasePercent = 50; - /// Maximum number of transactions that can be queued by one account. + /** + * Maximum number of transactions that can be queued by one account. + */ std::uint32_t maximumTxnPerAccount = 10; - /** Minimum difference between the current ledger sequence and a - transaction's `LastLedgerSequence` for the transaction to be - queueable. Decreases the chance a transaction will get queued - and broadcast only to expire before it gets a chance to be - processed. - */ + /** + * Minimum difference between the current ledger sequence and a + * transaction's `LastLedgerSequence` for the transaction to be + * queueable. Decreases the chance a transaction will get queued + * and broadcast only to expire before it gets a chance to be + * processed. + */ std::uint32_t minimumLastLedgerBuffer = 2; - /// Use standalone mode behavior. + /** + * Use standalone mode behavior. + */ bool standAlone = false; }; /** - Structure returned by @ref TxQ::getMetrics, expressed in - reference fee level units. - */ + * Structure returned by @ref TxQ::getMetrics, expressed in + * reference fee level units. + */ struct Metrics { - /// Default constructor + /** + * Default constructor + */ explicit Metrics() = default; - /// Number of transactions in the queue + /** + * Number of transactions in the queue + */ std::size_t txCount{}; - /// Max transactions currently allowed in queue + /** + * Max transactions currently allowed in queue + */ std::optional txQMaxSize; - /// Number of transactions currently in the open ledger + /** + * Number of transactions currently in the open ledger + */ std::size_t txInLedger{}; - /// Number of transactions expected per ledger + /** + * Number of transactions expected per ledger + */ std::size_t txPerLedger{}; - /// Reference transaction fee level + /** + * Reference transaction fee level + */ FeeLevel64 referenceFeeLevel{}; - /// Minimum fee level for a transaction to be considered for - /// the open ledger or the queue + /** + * Minimum fee level for a transaction to be considered for + * the open ledger or the queue + */ FeeLevel64 minProcessingFeeLevel{}; - /// Median fee level of the last ledger + /** + * Median fee level of the last ledger + */ FeeLevel64 medFeeLevel{}; - /// Minimum fee level to get into the current open ledger, - /// bypassing the queue + /** + * Minimum fee level to get into the current open ledger, + * bypassing the queue + */ FeeLevel64 openLedgerFeeLevel{}; }; /** - Structure that describes a transaction in the queue - waiting to be applied to the current open ledger. - A collection of these is returned by @ref TxQ::getTxs. - */ + * Structure that describes a transaction in the queue + * waiting to be applied to the current open ledger. + * A collection of these is returned by @ref TxQ::getTxs. + */ struct TxDetails { - /// Full initialization + /** + * Full initialization + */ TxDetails( FeeLevel64 feeLevel, std::optional const& lastValid, @@ -213,60 +256,78 @@ public: { } - /// Fee level of the queued transaction + /** + * Fee level of the queued transaction + */ FeeLevel64 feeLevel; - /// LastValidLedger field of the queued transaction, if any + /** + * LastValidLedger field of the queued transaction, if any + */ std::optional lastValid; - /** Potential @ref TxConsequences of applying the queued transaction - to the open ledger. - */ + /** + * Potential @ref TxConsequences of applying the queued transaction + * to the open ledger. + */ TxConsequences consequences; - /// The account the transaction is queued for + /** + * The account the transaction is queued for + */ AccountID account; - /// SeqProxy of the transaction + /** + * SeqProxy of the transaction + */ SeqProxy seqProxy; - /// The full transaction + /** + * The full transaction + */ std::shared_ptr txn; - /** Number of times the transactor can return a retry / `ter` result - when attempting to apply this transaction to the open ledger - from the queue. If the transactor returns `ter` and no retries are - left, this transaction will be dropped. - */ + /** + * Number of times the transactor can return a retry / `ter` result + * when attempting to apply this transaction to the open ledger + * from the queue. If the transactor returns `ter` and no retries are + * left, this transaction will be dropped. + */ int retriesRemaining; - /** The *intermediate* result returned by @ref preflight before - this transaction was queued, or after it is queued, but before - a failed attempt to `apply` it to the open ledger. This will - usually be `tesSUCCESS`, but there are some edge cases where - it has another value. Those edge cases are interesting enough - that this value is made available here. Specifically, if the - `rules` change between attempts, `preflight` will be run again - in `TxQ::MaybeTx::apply`. - */ + /** + * The *intermediate* result returned by @ref preflight before + * this transaction was queued, or after it is queued, but before + * a failed attempt to `apply` it to the open ledger. This will + * usually be `tesSUCCESS`, but there are some edge cases where + * it has another value. Those edge cases are interesting enough + * that this value is made available here. Specifically, if the + * `rules` change between attempts, `preflight` will be run again + * in `TxQ::MaybeTx::apply`. + */ TER preflightResult; - /** If the transactor attempted to apply the transaction to the open - ledger from the queue and *failed*, then this is the transactor - result from the last attempt. Should never be a `tec`, `tef`, - `tem`, or `tesSUCCESS`, because those results cause the - transaction to be removed from the queue. - */ + /** + * If the transactor attempted to apply the transaction to the open + * ledger from the queue and *failed*, then this is the transactor + * result from the last attempt. Should never be a `tec`, `tef`, + * `tem`, or `tesSUCCESS`, because those results cause the + * transaction to be removed from the queue. + */ std::optional lastResult; }; - /// Constructor + /** + * Constructor + */ TxQ(Setup const& setup, beast::Journal j); - /// Destructor + /** + * Destructor + */ virtual ~TxQ(); /** - Add a new transaction to the open ledger, hold it in the queue, - or reject it. - - @return A pair with the `TER` and a `bool` indicating - whether or not the transaction was applied to - the open ledger. If the transaction is queued, - will return `{ terQUEUED, false }`. - */ + * Add a new transaction to the open ledger, hold it in the queue, + * or reject it. + * + * @return A pair with the `TER` and a `bool` indicating + * whether or not the transaction was applied to + * the open ledger. If the transaction is queued, + * will return `{ terQUEUED, false }`. + */ ApplyResult apply( Application& app, @@ -276,39 +337,42 @@ public: beast::Journal j); /** - Fill the new open ledger with transactions from the queue. - - @note As more transactions are applied to the ledger, the - required fee may increase. The required fee may rise above - the fee level of the queued items before the queue is emptied, - which will end the process, leaving those in the queue for - the next open ledger. - - @return Whether any transactions were added to the `view`. - */ + * Fill the new open ledger with transactions from the queue. + * + * @note As more transactions are applied to the ledger, the + * required fee may increase. The required fee may rise above + * the fee level of the queued items before the queue is emptied, + * which will end the process, leaving those in the queue for + * the next open ledger. + * + * @return Whether any transactions were added to the `view`. + */ bool accept(Application& app, OpenView& view); /** - Update fee metrics and clean up the queue in preparation for - the next ledger. - - @note Fee metrics are updated based on the fee levels of the - txs in the validated ledger and whether consensus is slow. - Maximum queue size is adjusted to be enough to hold - `ledgersInQueue` ledgers or `queueSizeMin` transactions. - Any transactions for which the `LastLedgerSequence` has - passed are removed from the queue, and any account objects - that have no candidates under them are removed. - */ + * Update fee metrics and clean up the queue in preparation for + * the next ledger. + * + * @note Fee metrics are updated based on the fee levels of the + * txs in the validated ledger and whether consensus is slow. + * Maximum queue size is adjusted to be enough to hold + * `ledgersInQueue` ledgers or `queueSizeMin` transactions. + * Any transactions for which the `LastLedgerSequence` has + * passed are removed from the queue, and any account objects + * that have no candidates under them are removed. + */ void processClosedLedger(Application& app, ReadView const& view, bool timeLeap); - /** Return the next sequence that would go in the TxQ for an account. */ + /** + * Return the next sequence that would go in the TxQ for an account. + */ SeqProxy nextQueuableSeq(SLE::const_ref sleAccount) const; - /** Returns fee metrics in reference fee level units. + /** + * Returns fee metrics in reference fee level units. */ Metrics getMetrics(OpenView const& view) const; @@ -327,33 +391,36 @@ public: * @param view current open ledger * @param tx the transaction * @return minimum required fee, first sequence in the ledger - * and first available sequence + * and first available sequence */ FeeAndSeq getTxRequiredFeeAndSeq(OpenView const& view, std::shared_ptr const& tx) const; - /** Returns information about the transactions currently - in the queue for the account. - - @returns Empty `vector` if the account has no transactions - in the queue. - */ + /** + * Returns information about the transactions currently + * in the queue for the account. + * + * @return Empty `vector` if the account has no transactions + * in the queue. + */ std::vector getAccountTxs(AccountID const& account) const; - /** Returns information about all transactions currently - in the queue. - - @returns Empty `vector` if there are no transactions - in the queue. - */ + /** + * Returns information about all transactions currently + * in the queue. + * + * @return Empty `vector` if there are no transactions + * in the queue. + */ std::vector getTxs() const; - /** Summarize current fee metrics for the `fee` RPC command. - - @returns a `Json objectvalue` - */ + /** + * Summarize current fee metrics for the `fee` RPC command. + * + * @return a `Json objectvalue` + */ json::Value doRPC(Application& app) const; @@ -363,35 +430,51 @@ private: nextQueuableSeqImpl(SLE::const_ref sleAccount, std::scoped_lock const&) const; /** - Track and use the fee escalation metrics of the - current open ledger. Does the work of scaling fees - as the open ledger grows. - */ + * Track and use the fee escalation metrics of the + * current open ledger. Does the work of scaling fees + * as the open ledger grows. + */ class FeeMetrics { private: - /// Minimum value of txnsExpected. + /** + * Minimum value of txnsExpected. + */ std::size_t const minimumTxnCount_; - /// Number of transactions per ledger that fee escalation "works - /// towards". + /** + * Number of transactions per ledger that fee escalation "works + * towards". + */ std::size_t const targetTxnCount_; - /// Maximum value of txnsExpected + /** + * Maximum value of txnsExpected + */ std::optional const maximumTxnCount_; - /// Number of transactions expected per ledger. - /// One more than this value will be accepted - /// before escalation kicks in. + /** + * Number of transactions expected per ledger. + * One more than this value will be accepted + * before escalation kicks in. + */ std::size_t txnsExpected_; - /// Recent history of transaction counts that - /// exceed the targetTxnCount_ + /** + * Recent history of transaction counts that + * exceed the targetTxnCount_ + */ boost::circular_buffer recentTxnCounts_; - /// Based on the median fee of the LCL. Used - /// when fee escalation kicks in. + /** + * Based on the median fee of the LCL. Used + * when fee escalation kicks in. + */ FeeLevel64 escalationMultiplier_; - /// Journal + /** + * Journal + */ beast::Journal const j_; public: - /// Constructor + /** + * Constructor + */ FeeMetrics(Setup const& setup, beast::Journal j) : minimumTxnCount_( setup.standAlone ? setup.minimumTxnInLedgerSA : setup.minimumTxnInLedger) @@ -412,20 +495,22 @@ private: } /** - Updates fee metrics based on the transactions in the ReadView - for use in fee escalation calculations. - - @param app Xrpld Application object. - @param view View of the LCL that was just closed or received. - @param timeLeap Indicates that xrpld is under load so fees - should grow faster. - @param setup Customization params. - */ + * Updates fee metrics based on the transactions in the ReadView + * for use in fee escalation calculations. + * + * @param app Xrpld Application object. + * @param view View of the LCL that was just closed or received. + * @param timeLeap Indicates that xrpld is under load so fees + * should grow faster. + * @param setup Customization params. + */ std::size_t update(Application& app, ReadView const& view, bool timeLeap, TxQ::Setup const& setup); - /// Snapshot of the externally relevant FeeMetrics - /// fields at any given time. + /** + * Snapshot of the externally relevant FeeMetrics + * fields at any given time. + */ struct Snapshot { // Number of transactions expected per ledger. @@ -437,54 +522,57 @@ private: FeeLevel64 const escalationMultiplier; }; - /// Get the current @ref Snapshot + /** + * Get the current @ref Snapshot + */ [[nodiscard]] Snapshot getSnapshot() const { return {.txnsExpected = txnsExpected_, .escalationMultiplier = escalationMultiplier_}; } - /** Use the number of transactions in the current open ledger - to compute the fee level a transaction must pay to bypass the - queue. - - @param view Current open ledger. - - @return A fee level value. - */ + /** + * Use the number of transactions in the current open ledger + * to compute the fee level a transaction must pay to bypass the + * queue. + * + * @param view Current open ledger. + * + * @return A fee level value. + */ static FeeLevel64 scaleFeeLevel(Snapshot const& snapshot, OpenView const& view); /** - Computes the total fee level for all transactions in a series. - Assumes that there are already more than @ref txnsExpected_ txns - between the view and `extraCount`. If there aren't, the result - will be sensible (e.g. there won't be any underflows or - overflows), but the level will be higher than actually required. - - @note A "series" is a set of transactions for the same account. - In the context of this function, the series is already in - the queue, and the series starts with the account's current - sequence number. This function is called by - @ref tryClearAccountQueueUpThruTx to figure out if a newly - submitted transaction is paying enough to get all of the queued - transactions plus itself out of the queue and into the open - ledger while accounting for the escalating fee as each one - is processed. The idea is that if a series of transactions - are taking too long to get out of the queue, a user can - "rescue" them without having to resubmit each one with an - individually higher fee. - - @param view Current open / working ledger. (May be a sandbox.) - @param extraCount Number of additional transactions to count as - in the ledger. (If `view` is a sandbox, should be the number of - transactions in the parent ledger.) - @param seriesSize Total number of transactions in the series to be - processed. - - @return A `std::pair` indicating - whether the calculation result overflows. - */ + * Computes the total fee level for all transactions in a series. + * Assumes that there are already more than @ref txnsExpected_ txns + * between the view and `extraCount`. If there aren't, the result + * will be sensible (e.g. there won't be any underflows or + * overflows), but the level will be higher than actually required. + * + * @note A "series" is a set of transactions for the same account. + * In the context of this function, the series is already in + * the queue, and the series starts with the account's current + * sequence number. This function is called by + * @ref tryClearAccountQueueUpThruTx to figure out if a newly + * submitted transaction is paying enough to get all of the queued + * transactions plus itself out of the queue and into the open + * ledger while accounting for the escalating fee as each one + * is processed. The idea is that if a series of transactions + * are taking too long to get out of the queue, a user can + * "rescue" them without having to resubmit each one with an + * individually higher fee. + * + * @param view Current open / working ledger. (May be a sandbox.) + * @param extraCount Number of additional transactions to count as + * in the ledger. (If `view` is a sandbox, should be the number of + * transactions in the parent ledger.) + * @param seriesSize Total number of transactions in the series to be + * processed. + * + * @return A `std::pair` indicating + * whether the calculation result overflows. + */ static std::pair escalatedSeriesFeeLevel( Snapshot const& snapshot, @@ -494,90 +582,112 @@ private: }; /** - Represents a transaction in the queue which may be applied - later to the open ledger. - */ + * Represents a transaction in the queue which may be applied + * later to the open ledger. + */ class MaybeTx { public: - /// Used by the TxQ::FeeHook and TxQ::FeeMultiSet below - /// to put each MaybeTx object into more than one - /// set without copies, pointers, etc. + /** + * Used by the TxQ::FeeHook and TxQ::FeeMultiSet below + * to put each MaybeTx object into more than one + * set without copies, pointers, etc. + */ boost::intrusive::set_member_hook<> byFeeListHook; - /// The complete transaction. + /** + * The complete transaction. + */ std::shared_ptr txn; - /// Computed fee level that the transaction will pay. + /** + * Computed fee level that the transaction will pay. + */ FeeLevel64 const feeLevel; - /// Transaction ID. + /** + * Transaction ID. + */ TxID const txID; - /// Account submitting the transaction. + /** + * Account submitting the transaction. + */ AccountID const account; - /// Expiration ledger for the transaction - /// (`sfLastLedgerSequence` field). + /** + * Expiration ledger for the transaction + * (`sfLastLedgerSequence` field). + */ std::optional const lastValid; - /// Transaction SeqProxy number - /// (`sfSequence` or `sfTicketSequence` field). + /** + * Transaction SeqProxy number + * (`sfSequence` or `sfTicketSequence` field). + */ SeqProxy const seqProxy; /** - A transaction at the front of the queue will be given - several attempts to succeed before being dropped from - the queue. If dropped, one of the account's penalty - flags will be set, and other transactions may have - their `retriesRemaining` forced down as part of the - penalty. - */ + * A transaction at the front of the queue will be given + * several attempts to succeed before being dropped from + * the queue. If dropped, one of the account's penalty + * flags will be set, and other transactions may have + * their `retriesRemaining` forced down as part of the + * penalty. + */ int retriesRemaining{kRetriesAllowed}; - /// Flags provided to `apply`. If the transaction is later - /// attempted with different flags, it will need to be - /// `preflight`ed again. + /** + * Flags provided to `apply`. If the transaction is later + * attempted with different flags, it will need to be + * `preflight`ed again. + */ ApplyFlags const flags; - /** If the transactor attempted to apply the transaction to the open - ledger from the queue and *failed*, then this is the transactor - result from the last attempt. Should never be a `tec`, `tef`, - `tem`, or `tesSUCCESS`, because those results cause the - transaction to be removed from the queue. - */ + /** + * If the transactor attempted to apply the transaction to the open + * ledger from the queue and *failed*, then this is the transactor + * result from the last attempt. Should never be a `tec`, `tef`, + * `tem`, or `tesSUCCESS`, because those results cause the + * transaction to be removed from the queue. + */ std::optional lastResult; - /** Cached result of the `preflight` operation. Because - `preflight` is expensive, minimize the number of times - it needs to be done. - @invariant `pfResult` is never allowed to be empty. The - `std::optional` is leveraged to allow `emplace`d - construction and replacement without a copy - assignment operation. - */ + /** + * Cached result of the `preflight` operation. Because + * `preflight` is expensive, minimize the number of times + * it needs to be done. + * @invariant `pfResult` is never allowed to be empty. The + * `std::optional` is leveraged to allow `emplace`d + * construction and replacement without a copy + * assignment operation. + */ std::optional pfResult; - /** Starting retry count for newly queued transactions. - - In TxQ::accept, the required fee level may be low - enough that this transaction gets a chance to apply - to the ledger, but it may get a retry ter result for - another reason (eg. insufficient balance). When that - happens, the transaction is left in the queue to try - again later, but it shouldn't be allowed to fail - indefinitely. The number of failures allowed is - essentially arbitrary. It should be large enough to - allow temporary failures to clear up, but small enough - that the queue doesn't fill up with stale transactions - which prevent lower fee level transactions from queuing. - */ + /** + * Starting retry count for newly queued transactions. + * + * In TxQ::accept, the required fee level may be low + * enough that this transaction gets a chance to apply + * to the ledger, but it may get a retry ter result for + * another reason (eg. insufficient balance). When that + * happens, the transaction is left in the queue to try + * again later, but it shouldn't be allowed to fail + * indefinitely. The number of failures allowed is + * essentially arbitrary. It should be large enough to + * allow temporary failures to clear up, but small enough + * that the queue doesn't fill up with stale transactions + * which prevent lower fee level transactions from queuing. + */ static constexpr int kRetriesAllowed = 10; - /** The hash of the parent ledger. - - This is used to pseudo-randomize the transaction order when - populating byFee_, by XORing it with the transaction hash (txID). - Using a single static and doing the XOR operation every time was - tested to be as fast or faster than storing the computed "sort key", - and obviously uses less memory. + /** + * The hash of the parent ledger. + * + * This is used to pseudo-randomize the transaction order when + * populating byFee_, by XORing it with the transaction hash (txID). + * Using a single static and doing the XOR operation every time was + * tested to be as fast or faster than storing the computed "sort key", + * and obviously uses less memory. */ static LedgerHash parentHashComp; public: - /// Constructor + /** + * Constructor + */ MaybeTx( std::shared_ptr const&, TxID const& txID, @@ -585,12 +695,16 @@ private: ApplyFlags const flags, PreflightResult const& pfResult); - /// Attempt to apply the queued transaction to the open ledger. + /** + * Attempt to apply the queued transaction to the open ledger. + */ ApplyResult apply(Application& app, OpenView& view, beast::Journal j); - /// Potential @ref TxConsequences of applying this transaction - /// to the open ledger. + /** + * Potential @ref TxConsequences of applying this transaction + * to the open ledger. + */ [[nodiscard]] TxConsequences const& consequences() const { @@ -598,7 +712,9 @@ private: // pfResult is never empty } - /// Return a TxDetails based on contained information. + /** + * Return a TxDetails based on contained information. + */ [[nodiscard]] TxDetails getTxDetails() const { @@ -616,14 +732,19 @@ private: } }; - /// Used for sorting @ref MaybeTx + /** + * Used for sorting @ref MaybeTx + */ class OrderCandidates { public: - /// Default constructor + /** + * Default constructor + */ explicit OrderCandidates() = default; - /** Sort @ref MaybeTx by `feeLevel` descending, then by + /** + * Sort @ref MaybeTx by `feeLevel` descending, then by * pseudo-randomized transaction ID ascending * * The transaction queue is ordered such that transactions @@ -636,7 +757,6 @@ private: * unpredictable. This allows validators to build similar queues * in the same order, and thus have more similar initial * proposals. - * */ bool operator()(MaybeTx const& lhs, MaybeTx const& rhs) const @@ -647,17 +767,22 @@ private: } }; - /** Used to represent an account to the queue, and stores the - transactions queued for that account by SeqProxy. - */ + /** + * Used to represent an account to the queue, and stores the + * transactions queued for that account by SeqProxy. + */ class TxQAccount { public: using TxMap = std::map; - /// The account + /** + * The account + */ AccountID const account; - /// Sequence number will be used as the key. + /** + * Sequence number will be used as the key. + */ TxMap transactions; /* If this account has had any transaction retry more than `retriesAllowed` times so that it was dropped from the @@ -675,38 +800,51 @@ private: bool dropPenalty = false; public: - /// Construct from a transaction + /** + * Construct from a transaction + */ explicit TxQAccount(std::shared_ptr const& txn); - /// Construct from an account + /** + * Construct from an account + */ explicit TxQAccount(AccountID const& account); - /// Return the number of transactions currently queued for this account + /** + * Return the number of transactions currently queued for this account + */ [[nodiscard]] std::size_t getTxnCount() const { return transactions.size(); } - /// Checks if this account has no transactions queued + /** + * Checks if this account has no transactions queued + */ [[nodiscard]] bool empty() const { return getTxnCount() == 0u; } - /// Find the entry in transactions that precedes seqProx, if one does. + /** + * Find the entry in transactions that precedes seqProx, if one does. + */ [[nodiscard]] TxMap::const_iterator getPrevTx(SeqProxy seqProx) const; - /// Add a transaction candidate to this account for queuing + /** + * Add a transaction candidate to this account for queuing + */ MaybeTx& add(MaybeTx&&); - /** Remove the candidate with given SeqProxy value from this - account. - - @return Whether a candidate was removed - */ + /** + * Remove the candidate with given SeqProxy value from this + * account. + * + * @return Whether a candidate was removed + */ bool remove(SeqProxy seqProx); }; @@ -743,56 +881,68 @@ private: using AccountMap = std::map; - /// Setup parameters used to control the behavior of the queue + /** + * Setup parameters used to control the behavior of the queue + */ Setup const setup_; - /// Journal + /** + * Journal + */ beast::Journal const j_; - /** Tracks the current state of the queue. - @note This member must always and only be accessed under - locked mutex_ - */ + /** + * Tracks the current state of the queue. + * @note This member must always and only be accessed under + * locked mutex_ + */ FeeMetrics feeMetrics_; - /** The queue itself: the collection of transactions ordered - by fee level. - @note This member must always and only be accessed under - locked mutex_ - */ + /** + * The queue itself: the collection of transactions ordered + * by fee level. + * @note This member must always and only be accessed under + * locked mutex_ + */ FeeMultiSet byFee_; - /** All of the accounts which currently have any transactions - in the queue. Entries are created and destroyed dynamically - as transactions are added and removed. - @note This member must always and only be accessed under - locked mutex_ - */ + /** + * All of the accounts which currently have any transactions + * in the queue. Entries are created and destroyed dynamically + * as transactions are added and removed. + * @note This member must always and only be accessed under + * locked mutex_ + */ AccountMap byAccount_; - /** Maximum number of transactions allowed in the queue based - on the current metrics. If uninitialized, there is no limit, - but that condition cannot last for long in practice. - @note This member must always and only be accessed under - locked mutex_ - */ + /** + * Maximum number of transactions allowed in the queue based + * on the current metrics. If uninitialized, there is no limit, + * but that condition cannot last for long in practice. + * @note This member must always and only be accessed under + * locked mutex_ + */ std::optional maxSize_; /** - parentHash_ used for logging only - */ + * parentHash_ used for logging only + */ LedgerHash parentHash_{beast::kZero}; - /** Most queue operations are done under the master lock, - but use this mutex for the RPC "fee" command, which isn't. - */ + /** + * Most queue operations are done under the master lock, + * but use this mutex for the RPC "fee" command, which isn't. + */ std::mutex mutable mutex_; private: - /// Is the queue at least `fillPercentage` full? + /** + * Is the queue at least `fillPercentage` full? + */ template bool isFull() const; - /** Checks if the indicated transaction fits the conditions - for being stored in the queue. - */ + /** + * Checks if the indicated transaction fits the conditions + * for being stored in the queue. + */ TER canBeHeld( STTx const&, @@ -803,14 +953,19 @@ private: std::optional const&, std::scoped_lock const& lock); - /// Erase and return the next entry in byFee_ (lower fee level) + /** + * Erase and return the next entry in byFee_ (lower fee level) + */ FeeMultiSet::iterator_type erase(FeeMultiSet::const_iterator_type); - /** Erase and return the next entry for the account (if fee level - is higher), or next entry in byFee_ (lower fee level). - Used to get the next "applicable" MaybeTx for accept(). - */ + /** + * Erase and return the next entry for the account (if fee level + * is higher), or next entry in byFee_ (lower fee level). + * Used to get the next "applicable" MaybeTx for accept(). + */ FeeMultiSet::iterator_type eraseAndAdvance(FeeMultiSet::const_iterator_type); - /// Erase a range of items, based on TxQAccount::TxMap iterators + /** + * Erase a range of items, based on TxQAccount::TxMap iterators + */ TxQAccount::TxMap::iterator erase( TxQAccount& txQAccount, @@ -818,10 +973,10 @@ private: TxQAccount::TxMap::const_iterator end); /** - All-or-nothing attempt to try to apply the queued txs for - `accountIter` up to and including `tx`. Transactions following - `tx` are not cleared. - */ + * All-or-nothing attempt to try to apply the queued txs for + * `accountIter` up to and including `tx`. Transactions following + * `tx` are not cleared. + */ ApplyResult tryClearAccountQueueUpThruTx( Application& app, @@ -838,8 +993,8 @@ private: }; /** - Build a @ref TxQ::Setup object from application configuration. -*/ + * Build a @ref TxQ::Setup object from application configuration. + */ TxQ::Setup setupTxQ(Config const&); diff --git a/src/xrpld/app/misc/ValidatorKeys.h b/src/xrpld/app/misc/ValidatorKeys.h index df88ec33a3..100b16a511 100644 --- a/src/xrpld/app/misc/ValidatorKeys.h +++ b/src/xrpld/app/misc/ValidatorKeys.h @@ -13,9 +13,10 @@ namespace xrpl { class Config; -/** Validator keys and manifest as set in configuration file. Values will be - empty if not configured as a validator or not configured with a manifest. -*/ +/** + * Validator keys and manifest as set in configuration file. Values will be + * empty if not configured as a validator or not configured with a manifest. + */ class ValidatorKeys { public: diff --git a/src/xrpld/app/misc/ValidatorList.h b/src/xrpld/app/misc/ValidatorList.h index 0db3eee284..3f9039eab8 100644 --- a/src/xrpld/app/misc/ValidatorList.h +++ b/src/xrpld/app/misc/ValidatorList.h @@ -46,31 +46,49 @@ class STValidation; The "better" dispositions have lower values than the "worse" dispositions */ enum class ListDisposition { - /// List is valid + /** + * List is valid + */ Accepted = 0, - /// List is expired, but has the largest non-pending sequence seen so far + /** + * List is expired, but has the largest non-pending sequence seen so far + */ Expired, - /// List will be valid in the future + /** + * List will be valid in the future + */ Pending, - /// Same sequence as current list + /** + * Same sequence as current list + */ SameSequence, - /// Future sequence already seen + /** + * Future sequence already seen + */ KnownSequence, - /// Trusted publisher key, but seq is too old + /** + * Trusted publisher key, but seq is too old + */ Stale, - /// List signed by untrusted publisher key + /** + * List signed by untrusted publisher key + */ Untrusted, - /// List version is not supported + /** + * List version is not supported + */ UnsupportedVersion, - /// Invalid format or signature + /** + * Invalid format or signature + */ Invalid }; @@ -95,7 +113,8 @@ enum class PublisherStatus { std::string to_string(ListDisposition disposition); -/** Changes in trusted nodes after updating validator list +/** + * Changes in trusted nodes after updating validator list */ struct TrustChanges { @@ -105,7 +124,9 @@ struct TrustChanges hash_set removed; }; -/** Used to represent the information stored in the blobs_v2 Json array */ +/** + * Used to represent the information stored in the blobs_v2 Json array + */ struct ValidatorBlobInfo { // base-64 encoded JSON containing the validator list. @@ -118,50 +139,50 @@ struct ValidatorBlobInfo }; /** - Trusted Validators List - ----------------------- - - Xrpld accepts ledger proposals and validations from trusted validator - nodes. A ledger is considered fully-validated once the number of received - trusted validations for a ledger meets or exceeds a quorum value. - - This class manages the set of validation public keys the local xrpld node - trusts. The list of trusted keys is populated using the keys listed in the - configuration file as well as lists signed by trusted publishers. The - trusted publisher public keys are specified in the config. - - New lists are expected to include the following data: - - @li @c "blob": Base64-encoded JSON string containing a @c "sequence", @c - "validFrom", @c "validUntil", and @c "validators" field. @c "validFrom" - contains the XRPL timestamp (seconds since January 1st, 2000 (00:00 - UTC)) for when the list becomes valid. @c "validUntil" contains the - XRPL timestamp for when the list expires. @c "validators" contains - an array of objects with a @c "validation_public_key" and optional - @c "manifest" field. @c "validation_public_key" should be the - hex-encoded master public key. @c "manifest" should be the - base64-encoded validator manifest. - - @li @c "manifest": Base64-encoded serialization of a manifest containing the - publisher's master and signing public keys. - - @li @c "signature": Hex-encoded signature of the blob using the publisher's - signing key. - - @li @c "version": 1 - - Individual validator lists are stored separately by publisher. The number of - lists on which a validator's public key appears is also tracked. - - The list of trusted validation public keys is reset at the start of each - consensus round to take into account the latest known lists as well as the - set of validators from whom validations are being received. Listed - validation public keys are shuffled and then sorted by the number of lists - they appear on. (The shuffling makes the order/rank of validators with the - same number of listings non-deterministic.) A quorum value is calculated for - the new trusted validator list. If there is only one list, all listed keys - are trusted. Otherwise, the trusted list size is set to 125% of the quorum. -*/ + * Trusted Validators List + * ----------------------- + * + * Xrpld accepts ledger proposals and validations from trusted validator + * nodes. A ledger is considered fully-validated once the number of received + * trusted validations for a ledger meets or exceeds a quorum value. + * + * This class manages the set of validation public keys the local xrpld node + * trusts. The list of trusted keys is populated using the keys listed in the + * configuration file as well as lists signed by trusted publishers. The + * trusted publisher public keys are specified in the config. + * + * New lists are expected to include the following data: + * + * @li @c "blob": Base64-encoded JSON string containing a @c "sequence", @c + * "validFrom", @c "validUntil", and @c "validators" field. @c "validFrom" + * contains the XRPL timestamp (seconds since January 1st, 2000 (00:00 + * UTC)) for when the list becomes valid. @c "validUntil" contains the + * XRPL timestamp for when the list expires. @c "validators" contains + * an array of objects with a @c "validation_public_key" and optional + * @c "manifest" field. @c "validation_public_key" should be the + * hex-encoded master public key. @c "manifest" should be the + * base64-encoded validator manifest. + * + * @li @c "manifest": Base64-encoded serialization of a manifest containing the + * publisher's master and signing public keys. + * + * @li @c "signature": Hex-encoded signature of the blob using the publisher's + * signing key. + * + * @li @c "version": 1 + * + * Individual validator lists are stored separately by publisher. The number of + * lists on which a validator's public key appears is also tracked. + * + * The list of trusted validation public keys is reset at the start of each + * consensus round to take into account the latest known lists as well as the + * set of validators from whom validations are being received. Listed + * validation public keys are shuffled and then sorted by the number of lists + * they appear on. (The shuffling makes the order/rank of validators with the + * same number of listings non-deterministic.) A quorum value is calculated for + * the new trusted validator list. If there is only one list, all listed keys + * are trusted. Otherwise, the trusted list size is set to 125% of the quorum. + */ class ValidatorList { struct PublisherList @@ -276,11 +297,12 @@ public: std::optional minimumQuorum = std::nullopt); ~ValidatorList() = default; - /** Describes the result of processing a Validator List (UNL), - including some of the information from the list which can - be used by the caller to know which list publisher is - involved. - */ + /** + * Describes the result of processing a Validator List (UNL), + * including some of the information from the list which can + * be used by the caller to know which list publisher is + * involved. + */ struct PublisherListStats { explicit PublisherListStats() = default; @@ -314,23 +336,24 @@ public: std::size_t numVLs = 0; }; - /** Load configured trusted keys. - - @param localSigningKey This node's validation public key - - @param configKeys List of trusted keys from config. Each entry - consists of a base58 encoded validation public key, optionally followed - by a comment. - - @param publisherKeys List of trusted publisher public keys. Each - entry contains a base58 encoded account public key. - - @par Thread Safety - - May be called concurrently - - @return `false` if an entry is invalid or unparsable - */ + /** + * Load configured trusted keys. + * + * @param localSigningKey This node's validation public key + * + * @param configKeys List of trusted keys from config. Each entry + * consists of a base58 encoded validation public key, optionally followed + * by a comment. + * + * @param publisherKeys List of trusted publisher public keys. Each + * entry contains a base58 encoded account public key. + * + * @par Thread Safety + * + * May be called concurrently + * + * @return `false` if an entry is invalid or unparsable + */ bool load( std::optional const& localSigningKey, @@ -338,10 +361,11 @@ public: std::vector const& publisherKeys, std::optional listThreshold = {}); - /** Pull the blob/signature/manifest information out of the appropriate Json - body fields depending on the version. - - @return An empty vector indicates malformed Json. + /** + * Pull the blob/signature/manifest information out of the appropriate Json + * body fields depending on the version. + * + * @return An empty vector indicates malformed Json. */ static std::vector parseBlobs(std::uint32_t version, json::Value const& body); @@ -375,35 +399,36 @@ public: std::vector& messages, std::size_t maxSize = kMaximumMessageSize); - /** Apply multiple published lists of public keys, then broadcast it to all - peers that have not seen it or sent it. - - @param manifest base64-encoded publisher key manifest - - @param version Version of published list format - - @param blobs Vector of BlobInfos representing one or more encoded - validator lists and signatures (and optional manifests) - - @param siteUri Uri of the site from which the list was validated - - @param hash Hash of the data parameters - - @param overlay Overlay object which will handle sending the message - - @param hashRouter HashRouter object which will determine which - peers not to send to - - @param networkOPs NetworkOPs object which will be informed if there - is a valid VL - - @return `ListDisposition::Accepted`, plus some of the publisher - information, if list was successfully applied - - @par Thread Safety - - May be called concurrently - */ + /** + * Apply multiple published lists of public keys, then broadcast it to all + * peers that have not seen it or sent it. + * + * @param manifest base64-encoded publisher key manifest + * + * @param version Version of published list format + * + * @param blobs Vector of BlobInfos representing one or more encoded + * validator lists and signatures (and optional manifests) + * + * @param siteUri Uri of the site from which the list was validated + * + * @param hash Hash of the data parameters + * + * @param overlay Overlay object which will handle sending the message + * + * @param hashRouter HashRouter object which will determine which + * peers not to send to + * + * @param networkOPs NetworkOPs object which will be informed if there + * is a valid VL + * + * @return `ListDisposition::Accepted`, plus some of the publisher + * information, if list was successfully applied + * + * @par Thread Safety + * + * May be called concurrently + */ PublisherListStats applyListsAndBroadcast( std::string const& manifest, @@ -415,26 +440,27 @@ public: HashRouter& hashRouter, NetworkOPs& networkOPs); - /** Apply multiple published lists of public keys. - - @param manifest base64-encoded publisher key manifest - - @param version Version of published list format - - @param blobs Vector of BlobInfos representing one or more encoded - validator lists and signatures (and optional manifests) - - @param siteUri Uri of the site from which the list was validated - - @param hash Optional hash of the data parameters - - @return `ListDisposition::Accepted`, plus some of the publisher - information, if list was successfully applied - - @par Thread Safety - - May be called concurrently - */ + /** + * Apply multiple published lists of public keys. + * + * @param manifest base64-encoded publisher key manifest + * + * @param version Version of published list format + * + * @param blobs Vector of BlobInfos representing one or more encoded + * validator lists and signatures (and optional manifests) + * + * @param siteUri Uri of the site from which the list was validated + * + * @param hash Optional hash of the data parameters + * + * @return `ListDisposition::Accepted`, plus some of the publisher + * information, if list was successfully applied + * + * @par Thread Safety + * + * May be called concurrently + */ PublisherListStats applyLists( std::string const& manifest, @@ -443,33 +469,35 @@ public: std::string siteUri, std::optional const& hash = {}); - /* Attempt to read previously stored list files. Expected to only be - called when loading from URL fails. - - @return A list of valid file:// URLs, if any. - - @par Thread Safety - - May be called concurrently - */ + /** + * Attempt to read previously stored list files. Expected to only be + * called when loading from URL fails. + * + * @return A list of valid file:// URLs, if any. + * + * @par Thread Safety + * + * May be called concurrently + */ std::vector loadLists(); - /** Update trusted nodes - - Reset the trusted nodes based on latest manifests, received validations, - and lists. - - @param seenValidators Set of NodeIDs of validators that have signed - recently received validations - - @return TrustedKeyChanges instance with newly trusted or untrusted - node identities. - - @par Thread Safety - - May be called concurrently - */ + /** + * Update trusted nodes + * + * Reset the trusted nodes based on latest manifests, received validations, + * and lists. + * + * @param seenValidators Set of NodeIDs of validators that have signed + * recently received validations + * + * @return TrustedKeyChanges instance with newly trusted or untrusted + * node identities. + * + * @par Thread Safety + * + * May be called concurrently + */ TrustChanges updateTrusted( hash_set const& seenValidators, @@ -478,139 +506,148 @@ public: Overlay& overlay, HashRouter& hashRouter); - /** Get quorum value for current trusted key set - - The quorum is the minimum number of validations needed for a ledger to - be fully validated. It can change when the set of trusted validation - keys is updated (at the start of each consensus round) and primarily - depends on the number of trusted keys. - - @par Thread Safety - - May be called concurrently - - @return quorum value - */ + /** + * Get quorum value for current trusted key set + * + * The quorum is the minimum number of validations needed for a ledger to + * be fully validated. It can change when the set of trusted validation + * keys is updated (at the start of each consensus round) and primarily + * depends on the number of trusted keys. + * + * @par Thread Safety + * + * May be called concurrently + * + * @return quorum value + */ std::size_t quorum() const { return quorum_; } - /** Returns `true` if public key is trusted - - @param identity Validation public key - - @par Thread Safety - - May be called concurrently - */ + /** + * Returns `true` if public key is trusted + * + * @param identity Validation public key + * + * @par Thread Safety + * + * May be called concurrently + */ bool trusted(PublicKey const& identity) const; - /** Returns `true` if public key is included on any lists - - @param identity Validation public key - - @par Thread Safety - - May be called concurrently - */ + /** + * Returns `true` if public key is included on any lists + * + * @param identity Validation public key + * + * @par Thread Safety + * + * May be called concurrently + */ bool listed(PublicKey const& identity) const; - /** Returns master public key if public key is trusted - - @param identity Validation public key - - @return `std::nullopt` if key is not trusted - - @par Thread Safety - - May be called concurrently - */ + /** + * Returns master public key if public key is trusted + * + * @param identity Validation public key + * + * @return `std::nullopt` if key is not trusted + * + * @par Thread Safety + * + * May be called concurrently + */ std::optional getTrustedKey(PublicKey const& identity) const; - /** Returns listed master public if public key is included on any lists - - @param identity Validation public key - - @return `std::nullopt` if key is not listed - - @par Thread Safety - - May be called concurrently - */ + /** + * Returns listed master public if public key is included on any lists + * + * @param identity Validation public key + * + * @return `std::nullopt` if key is not listed + * + * @par Thread Safety + * + * May be called concurrently + */ std::optional getListedKey(PublicKey const& identity) const; - /** Returns `true` if public key is a trusted publisher - - @param identity Publisher public key - - @par Thread Safety - - May be called concurrently - */ + /** + * Returns `true` if public key is a trusted publisher + * + * @param identity Publisher public key + * + * @par Thread Safety + * + * May be called concurrently + */ bool trustedPublisher(PublicKey const& identity) const; - /** This function returns the local validator public key + /** + * This function returns the local validator public key * or a std::nullopt - - @par Thread Safety - - May be called concurrently - */ + * + * @par Thread Safety + * + * May be called concurrently + */ std::optional localPublicKey() const; - /** Invokes the callback once for every listed validation public key. - - @note Undefined behavior results when calling ValidatorList members from - within the callback - - The arguments passed into the lambda are: - - @li The validation public key - - @li A boolean indicating whether this is a trusted key - - @par Thread Safety - - May be called concurrently - */ + /** + * Invokes the callback once for every listed validation public key. + * + * @note Undefined behavior results when calling ValidatorList members from + * within the callback + * + * The arguments passed into the lambda are: + * + * @li The validation public key + * + * @li A boolean indicating whether this is a trusted key + * + * @par Thread Safety + * + * May be called concurrently + */ void forEachListed(std::function func) const; - /** Invokes the callback once for every available publisher list's raw - data members - - @note Undefined behavior results when calling ValidatorList members - from within the callback - - The arguments passed into the lambda are: - - @li The raw manifest string - - @li The raw "blob" string containing the values for the validator list - - @li The signature string used to sign the blob - - @li The version number - - @li The `PublicKey` of the blob signer (matches the value from - [validator_list_keys]) - - @li The sequence number of the "blob" - - @li The precomputed hash of the original / raw elements - - @par Thread Safety - - May be called concurrently - */ + /** + * Invokes the callback once for every available publisher list's raw + * data members + * + * @note Undefined behavior results when calling ValidatorList members + * from within the callback + * + * The arguments passed into the lambda are: + * + * @li The raw manifest string + * + * @li The raw "blob" string containing the values for the validator list + * + * @li The signature string used to sign the blob + * + * @li The version number + * + * @li The `PublicKey` of the blob signer (matches the value from + * [validator_list_keys]) + * + * @li The sequence number of the "blob" + * + * @li The precomputed hash of the original / raw elements + * + * @par Thread Safety + * + * May be called concurrently + */ void forEachAvailable( std::function func) const; - /** Returns the current valid list for the given publisher key, - if available, as a Json object. - */ + /** + * Returns the current valid list for the given publisher key, + * if available, as a Json object. + */ std::optional getAvailable(std::string_view pubKey, std::optional forceVersion = {}); - /** Return the number of configured validator list sites. */ + /** + * Return the number of configured validator list sites. + */ std::size_t count() const; - /** Return the time when the validator list will expire - - @note This may be a time in the past if a published list has not - been updated since its validUntil. It will be std::nullopt if any - configured published list has not been fetched. - - @par Thread Safety - May be called concurrently - */ + /** + * Return the time when the validator list will expire + * + * @note This may be a time in the past if a published list has not + * been updated since its validUntil. It will be std::nullopt if any + * configured published list has not been fetched. + * + * @par Thread Safety + * May be called concurrently + */ std::optional expires() const; - /** Return a JSON representation of the state of the validator list - - @par Thread Safety - May be called concurrently - */ + /** + * Return a JSON representation of the state of the validator list + * + * @par Thread Safety + * May be called concurrently + */ json::Value getJson() const; using QuorumKeys = std::pair>; - /** Get the quorum and all of the trusted keys. + /** + * Get the quorum and all of the trusted keys. * * @return quorum and keys. */ @@ -701,68 +744,74 @@ public: negativeUNLFilter(std::vector>&& validations) const; private: - /** Return the number of configured validator list sites. */ + /** + * Return the number of configured validator list sites. + */ std::size_t count(shared_lock const&) const; - /** Returns `true` if public key is trusted - - @param identity Validation public key - - @par Thread Safety - - May be called concurrently - */ + /** + * Returns `true` if public key is trusted + * + * @param identity Validation public key + * + * @par Thread Safety + * + * May be called concurrently + */ bool trusted(shared_lock const&, PublicKey const& identity) const; - /** Returns master public key if public key is trusted - - @param identity Validation public key - - @return `std::nullopt` if key is not trusted - - @par Thread Safety - - May be called concurrently - */ + /** + * Returns master public key if public key is trusted + * + * @param identity Validation public key + * + * @return `std::nullopt` if key is not trusted + * + * @par Thread Safety + * + * May be called concurrently + */ std::optional getTrustedKey(shared_lock const&, PublicKey const& identity) const; - /** Return the time when the validator list will expire - - @note This may be a time in the past if a published list has not - been updated since its expiration. It will be std::nullopt if any - configured published list has not been fetched. - - @par Thread Safety - May be called concurrently - */ + /** + * Return the time when the validator list will expire + * + * @note This may be a time in the past if a published list has not + * been updated since its expiration. It will be std::nullopt if any + * configured published list has not been fetched. + * + * @par Thread Safety + * May be called concurrently + */ std::optional expires(shared_lock const&) const; - /** Apply published list of public keys - - @param manifest base64-encoded publisher key manifest - - @param blob base64-encoded json containing published validator list - - @param signature Signature of the decoded blob - - @param version Version of published list format - - @param siteUri Uri of the site from which the list was validated - - @param hash Optional hash of the data parameters. - Defaults to uninitialized - - @return `ListDisposition::Accepted`, plus some of the publisher - information, if list was successfully applied - - @par Thread Safety - - May be called concurrently - */ + /** + * Apply published list of public keys + * + * @param manifest base64-encoded publisher key manifest + * + * @param blob base64-encoded json containing published validator list + * + * @param signature Signature of the decoded blob + * + * @param version Version of published list format + * + * @param siteUri Uri of the site from which the list was validated + * + * @param hash Optional hash of the data parameters. + * Defaults to uninitialized + * + * @return `ListDisposition::Accepted`, plus some of the publisher + * information, if list was successfully applied + * + * @par Thread Safety + * + * May be called concurrently + */ PublisherListStats applyList( std::string const& globalManifest, @@ -814,23 +863,26 @@ private: HashRouter& hashRouter, beast::Journal j); - /** Get the filename used for caching UNLs + /** + * Get the filename used for caching UNLs */ boost::filesystem::path getCacheFileName(scoped_lock const&, PublicKey const& pubKey) const; - /** Build a Json representation of the collection, suitable for - writing to a cache file, or serving to a /vl/ query - */ + /** + * Build a Json representation of the collection, suitable for + * writing to a cache file, or serving to a /vl/ query + */ static json::Value buildFileData( std::string const& pubKey, PublisherListCollection const& pubCollection, beast::Journal j); - /** Build a Json representation of the collection, suitable for - writing to a cache file, or serving to a /vl/ query - */ + /** + * Build a Json representation of the collection, suitable for + * writing to a cache file, or serving to a /vl/ query + */ static json::Value buildFileData( std::string const& pubKey, @@ -846,19 +898,21 @@ private: hash_append(h, pl.rawManifest, buildBlobInfos(pl), pl.rawVersion); } - /** Write a JSON UNL to a cache file + /** + * Write a JSON UNL to a cache file */ void cacheValidatorFile(scoped_lock const& lock, PublicKey const& pubKey) const; - /** Check response for trusted valid published list - - @return `ListDisposition::Accepted` if list can be applied - - @par Thread Safety - - Calling public member function is expected to lock mutex - */ + /** + * Check response for trusted valid published list + * + * @return `ListDisposition::Accepted` if list can be applied + * + * @par Thread Safety + * + * Calling public member function is expected to lock mutex + */ std::pair> verify( scoped_lock const&, @@ -867,29 +921,31 @@ private: std::string const& blob, std::string const& signature); - /** Stop trusting publisher's list of keys. - - @param publisherKey Publisher public key - - @return `false` if key was not trusted - - @par Thread Safety - - Calling public member function is expected to lock mutex - */ + /** + * Stop trusting publisher's list of keys. + * + * @param publisherKey Publisher public key + * + * @return `false` if key was not trusted + * + * @par Thread Safety + * + * Calling public member function is expected to lock mutex + */ bool removePublisherList(scoped_lock const&, PublicKey const& publisherKey, PublisherStatus reason); - /** Return quorum for trusted validator set - - @param unlSize Number of trusted validator keys - - @param effectiveUnlSize Number of trusted validator keys that are not in - the NegativeUNL - - @param seenSize Number of trusted validators that have signed - recently received validations - */ + /** + * Return quorum for trusted validator set + * + * @param unlSize Number of trusted validator keys + * + * @param effectiveUnlSize Number of trusted validator keys that are not in + * the NegativeUNL + * + * @param seenSize Number of trusted validators that have signed + * recently received validations + */ std::size_t calculateQuorum(std::size_t unlSize, std::size_t effectiveUnlSize, std::size_t seenSize); }; diff --git a/src/xrpld/app/misc/ValidatorSite.h b/src/xrpld/app/misc/ValidatorSite.h index 7302ebbb52..7e7ad098eb 100644 --- a/src/xrpld/app/misc/ValidatorSite.h +++ b/src/xrpld/app/misc/ValidatorSite.h @@ -23,35 +23,35 @@ namespace xrpl { /** - Validator Sites - --------------- - - This class manages the set of configured remote sites used to fetch the - latest published recommended validator lists. - - Lists are fetched at a regular interval. - Fetched lists are expected to be in JSON format and contain the following - fields: - - @li @c "blob": Base64-encoded JSON string containing a @c "sequence", @c - "validUntil", and @c "validators" field. @c "validUntil" contains the - XRPL timestamp (seconds since January 1st, 2000 (00:00 UTC)) for when - the list expires. @c "validators" contains an array of objects with a - @c "validation_public_key" and optional @c "manifest" field. - @c "validation_public_key" should be the hex-encoded master public key. - @c "manifest" should be the base64-encoded validator manifest. - - @li @c "manifest": Base64-encoded serialization of a manifest containing the - publisher's master and signing public keys. - - @li @c "signature": Hex-encoded signature of the blob using the publisher's - signing key. - - @li @c "version": 1 - - @li @c "refreshInterval" (optional, integer minutes). - This value is clamped internally to [1,1440] (1 min - 1 day) -*/ + * Validator Sites + * --------------- + * + * This class manages the set of configured remote sites used to fetch the + * latest published recommended validator lists. + * + * Lists are fetched at a regular interval. + * Fetched lists are expected to be in JSON format and contain the following + * fields: + * + * @li @c "blob": Base64-encoded JSON string containing a @c "sequence", @c + * "validUntil", and @c "validators" field. @c "validUntil" contains the + * XRPL timestamp (seconds since January 1st, 2000 (00:00 UTC)) for when + * the list expires. @c "validators" contains an array of objects with a + * @c "validation_public_key" and optional @c "manifest" field. + * @c "validation_public_key" should be the hex-encoded master public key. + * @c "manifest" should be the base64-encoded validator manifest. + * + * @li @c "manifest": Base64-encoded serialization of a manifest containing the + * publisher's master and signing public keys. + * + * @li @c "signature": Hex-encoded signature of the blob using the publisher's + * signing key. + * + * @li @c "version": 1 + * + * @li @c "refreshInterval" (optional, integer minutes). + * This value is clamped internally to [1,1440] (1 min - 1 day) + */ class ValidatorSite { friend class Work; @@ -79,17 +79,23 @@ private: explicit Site(std::string uri); - /// the original uri as loaded from config + /** + * the original uri as loaded from config + */ std::shared_ptr loadedResource; - /// the resource to request at - /// intervals. same as loadedResource - /// except in the case of a permanent redir. + /** + * the resource to request at + * intervals. same as loadedResource + * except in the case of a permanent redir. + */ std::shared_ptr startingResource; - /// the active resource being requested. - /// same as startingResource except - /// when we've gotten a temp redirect + /** + * the active resource being requested. + * same as startingResource except + * when we've gotten a temp redirect + */ std::shared_ptr activeResource; unsigned short redirCount{0}; @@ -132,74 +138,89 @@ public: std::chrono::seconds timeout = std::chrono::seconds{20}); ~ValidatorSite(); - /** Load configured site URIs. - - @param siteURIs List of URIs to fetch published validator lists - - @par Thread Safety - - May be called concurrently - - @return `false` if an entry is invalid or unparsable - */ + /** + * Load configured site URIs. + * + * @param siteURIs List of URIs to fetch published validator lists + * + * @par Thread Safety + * + * May be called concurrently + * + * @return `false` if an entry is invalid or unparsable + */ bool load(std::vector const& siteURIs); - /** Start fetching lists from sites - - This does nothing if list fetching has already started - - @par Thread Safety - - May be called concurrently - */ + /** + * Start fetching lists from sites + * + * This does nothing if list fetching has already started + * + * @par Thread Safety + * + * May be called concurrently + */ void start(); - /** Wait for current fetches from sites to complete - - @par Thread Safety - - May be called concurrently - */ + /** + * Wait for current fetches from sites to complete + * + * @par Thread Safety + * + * May be called concurrently + */ void join(); - /** Stop fetching lists from sites - - This blocks until list fetching has stopped - - @par Thread Safety - - May be called concurrently - */ + /** + * Stop fetching lists from sites + * + * This blocks until list fetching has stopped + * + * @par Thread Safety + * + * May be called concurrently + */ void stop(); - /** Return JSON representation of configured validator sites + /** + * Return JSON representation of configured validator sites */ json::Value getJson() const; private: - /// Load configured site URIs. + /** + * Load configured site URIs. + */ bool load(std::vector const& siteURIs, std::scoped_lock const&); - /// Queue next site to be fetched - /// lock over site_mutex_ and state_mutex_ required + /** + * Queue next site to be fetched + * lock over site_mutex_ and state_mutex_ required + */ void setTimer(std::scoped_lock const&, std::scoped_lock const&); - /// request took too long + /** + * request took too long + */ void onRequestTimeout(std::size_t siteIdx, error_code const& ec); - /// Fetch site whose time has come + /** + * Fetch site whose time has come + */ void onTimer(std::size_t siteIdx, error_code const& ec); - /// Store latest list fetched from site + /** + * Store latest list fetched from site + */ void onSiteFetch( boost::system::error_code const& ec, @@ -207,36 +228,46 @@ private: detail::response_type const& res, std::size_t siteIdx); - /// Store latest list fetched from anywhere + /** + * Store latest list fetched from anywhere + */ void onTextFetch(boost::system::error_code const& ec, std::string const& res, std::size_t siteIdx); - /// Initiate request to given resource. - /// lock over sites_mutex_ required + /** + * Initiate request to given resource. + * lock over sites_mutex_ required + */ void makeRequest( std::shared_ptr resource, std::size_t siteIdx, std::scoped_lock const&); - /// Parse json response from validator list site. - /// lock over sites_mutex_ required + /** + * Parse json response from validator list site. + * lock over sites_mutex_ required + */ void parseJsonResponse( std::string const& res, std::size_t siteIdx, std::scoped_lock const&); - /// Interpret a redirect response. - /// lock over sites_mutex_ required + /** + * Interpret a redirect response. + * lock over sites_mutex_ required + */ std::shared_ptr processRedirect( detail::response_type const& res, std::size_t siteIdx, std::scoped_lock const&); - /// If no sites are provided, or a site fails to load, - /// get a list of local cache files from the ValidatorList. + /** + * If no sites are provided, or a site fails to load, + * get a list of local cache files from the ValidatorList. + */ bool missingSite(std::scoped_lock const&); }; diff --git a/src/xrpld/app/misc/detail/AmendmentTable.cpp b/src/xrpld/app/misc/detail/AmendmentTable.cpp index 694268752e..4af556d3e7 100644 --- a/src/xrpld/app/misc/detail/AmendmentTable.cpp +++ b/src/xrpld/app/misc/detail/AmendmentTable.cpp @@ -81,24 +81,25 @@ parseSection(Section const& section) return names; } -/** TrustedVotes records the most recent votes from trusted validators. - We keep a record in an effort to avoid "flapping" while amendment voting - is in process. - - If a trusted validator loses synchronization near a flag ledger their - amendment votes may be lost during that round. If the validator is a - bit flaky, then this can cause an amendment to appear to repeatedly - gain and lose support. - - TrustedVotes addresses the problem by holding on to the last vote seen - from every trusted validator. So if any given validator is off line near - a flag ledger we can assume that they did not change their vote. - - If we haven't seen any STValidations from a validator for several hours we - lose confidence that the validator hasn't changed their position. So - there's a timeout. We remove upVotes if they haven't been updated in - several hours. -*/ +/** + * TrustedVotes records the most recent votes from trusted validators. + * We keep a record in an effort to avoid "flapping" while amendment voting + * is in process. + * + * If a trusted validator loses synchronization near a flag ledger their + * amendment votes may be lost during that round. If the validator is a + * bit flaky, then this can cause an amendment to appear to repeatedly + * gain and lose support. + * + * TrustedVotes addresses the problem by holding on to the last vote seen + * from every trusted validator. So if any given validator is off line near + * a flag ledger we can assume that they did not change their vote. + * + * If we haven't seen any STValidations from a validator for several hours we + * lose confidence that the validator hasn't changed their position. So + * there's a timeout. We remove upVotes if they haven't been updated in + * several hours. + */ class TrustedVotes { private: @@ -107,10 +108,11 @@ private: struct UpvotesAndTimeout { std::vector upVotes; - /** An unseated timeout indicates that either - 1. No validations have ever been received - 2. The validator has not been heard from in long enough that the - timeout passed, and votes expired. + /** + * An unseated timeout indicates that either + * 1. No validations have ever been received + * 2. The validator has not been heard from in long enough that the + * timeout passed, and votes expired. */ std::optional timeout; }; @@ -279,32 +281,42 @@ public: } }; -/** Current state of an amendment. - Tells if a amendment is supported, enabled or vetoed. A vetoed amendment - means the node will never announce its support. -*/ +/** + * Current state of an amendment. + * Tells if a amendment is supported, enabled or vetoed. A vetoed amendment + * means the node will never announce its support. + */ struct AmendmentState { - /** If an amendment is down-voted, a server will not vote to enable it */ + /** + * If an amendment is down-voted, a server will not vote to enable it + */ AmendmentVote vote = AmendmentVote::Down; - /** Indicates that the amendment has been enabled. - This is a one-way switch: once an amendment is enabled - it can never be disabled, but it can be superseded by - a subsequent amendment. - */ + /** + * Indicates that the amendment has been enabled. + * This is a one-way switch: once an amendment is enabled + * it can never be disabled, but it can be superseded by + * a subsequent amendment. + */ bool enabled = false; - /** Indicates an amendment that this server has code support for. */ + /** + * Indicates an amendment that this server has code support for. + */ bool supported = false; - /** The name of this amendment, possibly empty. */ + /** + * The name of this amendment, possibly empty. + */ std::string name; explicit AmendmentState() = default; }; -/** The status of all amendments requested in a given window. */ +/** + * The status of all amendments requested in a given window. + */ class AmendmentSet { private: @@ -376,12 +388,13 @@ public: //------------------------------------------------------------------------------ -/** Track the list of "amendments" - - An "amendment" is an option that can affect transaction processing rules. - Amendments are proposed and then adopted or rejected by the network. An - Amendment is uniquely identified by its AmendmentID, a 256-bit key. -*/ +/** + * Track the list of "amendments" + * + * An "amendment" is an option that can affect transaction processing rules. + * Amendments are proposed and then adopted or rejected by the network. An + * Amendment is uniquely identified by its AmendmentID, a 256-bit key. + */ class AmendmentTableImpl final : public AmendmentTable { private: diff --git a/src/xrpld/app/misc/setup_HashRouter.h b/src/xrpld/app/misc/setup_HashRouter.h index 665366ba03..86c472da7f 100644 --- a/src/xrpld/app/misc/setup_HashRouter.h +++ b/src/xrpld/app/misc/setup_HashRouter.h @@ -7,7 +7,9 @@ namespace xrpl { // Forward declaration class Config; -/** Create HashRouter setup from configuration */ +/** + * Create HashRouter setup from configuration + */ HashRouter::Setup setupHashRouter(Config const& config); diff --git a/src/xrpld/consensus/Consensus.h b/src/xrpld/consensus/Consensus.h index fa41f25be1..440191939b 100644 --- a/src/xrpld/consensus/Consensus.h +++ b/src/xrpld/consensus/Consensus.h @@ -28,25 +28,26 @@ namespace xrpl { -/** Determines whether the current ledger should close at this time. - - This function should be called when a ledger is open and there is no close - in progress, or when a transaction is received and no close is in progress. - - @param anyTransactions indicates whether any transactions have been received - @param prevProposers proposers in the last closing - @param proposersClosed proposers who have currently closed this ledger - @param proposersValidated proposers who have validated the last closed - ledger - @param prevRoundTime time for the previous ledger to reach consensus - @param timeSincePrevClose time since the previous ledger's (possibly - rounded) close time - @param openTime duration this ledger has been open - @param idleInterval the network's desired idle interval - @param parms Consensus constant parameters - @param j journal for logging - @param clog log object to which to append -*/ +/** + * Determines whether the current ledger should close at this time. + * + * This function should be called when a ledger is open and there is no close + * in progress, or when a transaction is received and no close is in progress. + * + * @param anyTransactions indicates whether any transactions have been received + * @param prevProposers proposers in the last closing + * @param proposersClosed proposers who have currently closed this ledger + * @param proposersValidated proposers who have validated the last closed + * ledger + * @param prevRoundTime time for the previous ledger to reach consensus + * @param timeSincePrevClose time since the previous ledger's (possibly + * rounded) close time + * @param openTime duration this ledger has been open + * @param idleInterval the network's desired idle interval + * @param parms Consensus constant parameters + * @param j journal for logging + * @param clog log object to which to append + */ bool shouldCloseLedger( bool anyTransactions, @@ -61,25 +62,26 @@ shouldCloseLedger( beast::Journal j, std::unique_ptr const& clog = {}); -/** Determine whether the network reached consensus and whether we joined. - - @param prevProposers proposers in the last closing (not including us) - @param currentProposers proposers in this closing so far (not including us) - @param currentAgree proposers who agree with us - @param currentFinished proposers who have validated a ledger after this one - @param previousAgreeTime how long, in milliseconds, it took to agree on the - last ledger - @param currentAgreeTime how long, in milliseconds, we've been trying to - agree - @param stalled the network appears to be stalled, where - neither we nor our peers have changed their vote on any disputes in a - while. This is undesirable, and should be rare, and will cause us to - end consensus without 80% agreement. - @param parms Consensus constant parameters - @param proposing whether we should count ourselves - @param j journal for logging - @param clog log object to which to append -*/ +/** + * Determine whether the network reached consensus and whether we joined. + * + * @param prevProposers proposers in the last closing (not including us) + * @param currentProposers proposers in this closing so far (not including us) + * @param currentAgree proposers who agree with us + * @param currentFinished proposers who have validated a ledger after this one + * @param previousAgreeTime how long, in milliseconds, it took to agree on the + * last ledger + * @param currentAgreeTime how long, in milliseconds, we've been trying to + * agree + * @param stalled the network appears to be stalled, where + * neither we nor our peers have changed their vote on any disputes in a + * while. This is undesirable, and should be rare, and will cause us to + * end consensus without 80% agreement. + * @param parms Consensus constant parameters + * @param proposing whether we should count ourselves + * @param j journal for logging + * @param clog log object to which to append + */ ConsensusState checkConsensus( std::size_t prevProposers, @@ -94,194 +96,195 @@ checkConsensus( beast::Journal j, std::unique_ptr const& clog = {}); -/** Generic implementation of consensus algorithm. - - Achieves consensus on the next ledger. - - Two things need consensus: - - 1. The set of transactions included in the ledger. - 2. The close time for the ledger. - - The basic flow: - - 1. A call to `startRound` places the node in the `Open` phase. In this - phase, the node is waiting for transactions to include in its open - ledger. - 2. Successive calls to `timerEntry` check if the node can close the ledger. - Once the node `Close`s the open ledger, it transitions to the - `Establish` phase. In this phase, the node shares/receives peer - proposals on which transactions should be accepted in the closed ledger. - 3. During a subsequent call to `timerEntry`, the node determines it has - reached consensus with its peers on which transactions to include. It - transitions to the `Accept` phase. In this phase, the node works on - applying the transactions to the prior ledger to generate a new closed - ledger. Once the new ledger is completed, the node shares the validated - ledger with the network, does some book-keeping, then makes a call to - `startRound` to start the cycle again. - - This class uses a generic interface to allow adapting Consensus for specific - applications. The Adaptor template implements a set of helper functions that - plug the consensus algorithm into a specific application. It also identifies - the types that play important roles in Consensus (transactions, ledgers, ...). - The code stubs below outline the interface and type requirements. The traits - types must be copy constructible and assignable. - - @warning The generic implementation is not thread safe and the public methods - are not intended to be run concurrently. When in a concurrent environment, - the application is responsible for ensuring thread-safety. Simply locking - whenever touching the Consensus instance is one option. - - @code - // A single transaction - struct Tx - { - // Unique identifier of transaction - using ID = ...; - - ID id() const; - - }; - - // A set of transactions - struct TxSet - { - // Unique ID of TxSet (not of Tx) - using ID = ...; - // Type of individual transaction comprising the TxSet - using Tx = Tx; - - bool exists(Tx::ID const &) const; - // Return value should have semantics like Tx const * - Tx const * find(Tx::ID const &) const ; - ID const & id() const; - - // Return set of transactions that are not common to this set or other - // boolean indicates which set it was in - std::map compare(TxSet const & other) const; - - // A mutable view of transactions - struct MutableTxSet - { - MutableTxSet(TxSet const &); - bool insert(Tx const &); - bool erase(Tx::ID const &); - }; - - // Construct from a mutable view. - TxSet(MutableTxSet const &); - - // Alternatively, if the TxSet is itself mutable - // just alias MutableTxSet = TxSet - - }; - - // Agreed upon state that consensus transactions will modify - struct Ledger - { - using ID = ...; - using Seq = ...; - - // Unique identifier of ledger - ID const id() const; - Seq seq() const; - auto closeTimeResolution() const; - auto closeAgree() const; - auto closeTime() const; - auto parentCloseTime() const; - json::Value getJson() const; - }; - - // Wraps a peer's ConsensusProposal - struct PeerPosition - { - ConsensusProposal< - std::uint32_t, //NodeID, - typename Ledger::ID, - typename TxSet::ID> const & - proposal() const; - - }; - - - class Adaptor - { - public: - //----------------------------------------------------------------------- - // Define consensus types - using Ledger_t = Ledger; - using NodeID_t = std::uint32_t; - using TxSet_t = TxSet; - using PeerPosition_t = PeerPosition; - - //----------------------------------------------------------------------- - // - // Attempt to acquire a specific ledger. - std::optional acquireLedger(Ledger::ID const & ledgerID); - - // Acquire the transaction set associated with a proposed position. - std::optional acquireTxSet(TxSet::ID const & setID); - - // Whether any transactions are in the open ledger - bool hasOpenTransactions() const; - - // Number of proposers that have validated the given ledger - std::size_t proposersValidated(Ledger::ID const & prevLedger) const; - - // Number of proposers that have validated a ledger descended from the - // given ledger; if prevLedger.id() != prevLedgerID, use prevLedgerID - // for the determination - std::size_t proposersFinished(Ledger const & prevLedger, - Ledger::ID const & prevLedger) const; - - // Return the ID of the last closed (and validated) ledger that the - // application thinks consensus should use as the prior ledger. - Ledger::ID getPrevLedger(Ledger::ID const & prevLedgerID, - Ledger const & prevLedger, - Mode mode); - - // Called whenever consensus operating mode changes - void onModeChange(ConsensusMode before, ConsensusMode after); - - // Called when ledger closes - Result onClose(Ledger const &, Ledger const & prev, Mode mode); - - // Called when ledger is accepted by consensus - void onAccept(Result const & result, - RCLCxLedger const & prevLedger, - NetClock::duration closeResolution, - CloseTimes const & rawCloseTimes, - Mode const & mode); - - // Called when ledger was forcibly accepted by consensus via the simulate - // function. - void onForceAccept(Result const & result, - RCLCxLedger const & prevLedger, - NetClock::duration closeResolution, - CloseTimes const & rawCloseTimes, - Mode const & mode); - - // Propose the position to peers. - void propose(ConsensusProposal<...> const & pos); - - // Share a received peer proposal with other peer's. - void share(PeerPosition_t const & prop); - - // Share a disputed transaction with peers - void share(Txn const & tx); - - // Share given transaction set with peers - void share(TxSet const &s); - - // Consensus timing parameters and constants - ConsensusParms const & - parms() const; - }; - @endcode - - @tparam Adaptor Defines types and provides helper functions needed to adapt - Consensus to the larger application. -*/ +/** + * Generic implementation of consensus algorithm. + * + * Achieves consensus on the next ledger. + * + * Two things need consensus: + * + * 1. The set of transactions included in the ledger. + * 2. The close time for the ledger. + * + * The basic flow: + * + * 1. A call to `startRound` places the node in the `Open` phase. In this + * phase, the node is waiting for transactions to include in its open + * ledger. + * 2. Successive calls to `timerEntry` check if the node can close the ledger. + * Once the node `Close`s the open ledger, it transitions to the + * `Establish` phase. In this phase, the node shares/receives peer + * proposals on which transactions should be accepted in the closed ledger. + * 3. During a subsequent call to `timerEntry`, the node determines it has + * reached consensus with its peers on which transactions to include. It + * transitions to the `Accept` phase. In this phase, the node works on + * applying the transactions to the prior ledger to generate a new closed + * ledger. Once the new ledger is completed, the node shares the validated + * ledger with the network, does some book-keeping, then makes a call to + * `startRound` to start the cycle again. + * + * This class uses a generic interface to allow adapting Consensus for specific + * applications. The Adaptor template implements a set of helper functions that + * plug the consensus algorithm into a specific application. It also identifies + * the types that play important roles in Consensus (transactions, ledgers, ...). + * The code stubs below outline the interface and type requirements. The traits + * types must be copy constructible and assignable. + * + * @warning The generic implementation is not thread safe and the public methods + * are not intended to be run concurrently. When in a concurrent environment, + * the application is responsible for ensuring thread-safety. Simply locking + * whenever touching the Consensus instance is one option. + * + * @code + * // A single transaction + * struct Tx + * { + * // Unique identifier of transaction + * using ID = ...; + * + * ID id() const; + * + * }; + * + * // A set of transactions + * struct TxSet + * { + * // Unique ID of TxSet (not of Tx) + * using ID = ...; + * // Type of individual transaction comprising the TxSet + * using Tx = Tx; + * + * bool exists(Tx::ID const &) const; + * // Return value should have semantics like Tx const * + * Tx const * find(Tx::ID const &) const ; + * ID const & id() const; + * + * // Return set of transactions that are not common to this set or other + * // boolean indicates which set it was in + * std::map compare(TxSet const & other) const; + * + * // A mutable view of transactions + * struct MutableTxSet + * { + * MutableTxSet(TxSet const &); + * bool insert(Tx const &); + * bool erase(Tx::ID const &); + * }; + * + * // Construct from a mutable view. + * TxSet(MutableTxSet const &); + * + * // Alternatively, if the TxSet is itself mutable + * // just alias MutableTxSet = TxSet + * + * }; + * + * // Agreed upon state that consensus transactions will modify + * struct Ledger + * { + * using ID = ...; + * using Seq = ...; + * + * // Unique identifier of ledger + * ID const id() const; + * Seq seq() const; + * auto closeTimeResolution() const; + * auto closeAgree() const; + * auto closeTime() const; + * auto parentCloseTime() const; + * json::Value getJson() const; + * }; + * + * // Wraps a peer's ConsensusProposal + * struct PeerPosition + * { + * ConsensusProposal< + * std::uint32_t, //NodeID, + * typename Ledger::ID, + * typename TxSet::ID> const & + * proposal() const; + * + * }; + * + * + * class Adaptor + * { + * public: + * //----------------------------------------------------------------------- + * // Define consensus types + * using Ledger_t = Ledger; + * using NodeID_t = std::uint32_t; + * using TxSet_t = TxSet; + * using PeerPosition_t = PeerPosition; + * + * //----------------------------------------------------------------------- + * // + * // Attempt to acquire a specific ledger. + * std::optional acquireLedger(Ledger::ID const & ledgerID); + * + * // Acquire the transaction set associated with a proposed position. + * std::optional acquireTxSet(TxSet::ID const & setID); + * + * // Whether any transactions are in the open ledger + * bool hasOpenTransactions() const; + * + * // Number of proposers that have validated the given ledger + * std::size_t proposersValidated(Ledger::ID const & prevLedger) const; + * + * // Number of proposers that have validated a ledger descended from the + * // given ledger; if prevLedger.id() != prevLedgerID, use prevLedgerID + * // for the determination + * std::size_t proposersFinished(Ledger const & prevLedger, + * Ledger::ID const & prevLedger) const; + * + * // Return the ID of the last closed (and validated) ledger that the + * // application thinks consensus should use as the prior ledger. + * Ledger::ID getPrevLedger(Ledger::ID const & prevLedgerID, + * Ledger const & prevLedger, + * Mode mode); + * + * // Called whenever consensus operating mode changes + * void onModeChange(ConsensusMode before, ConsensusMode after); + * + * // Called when ledger closes + * Result onClose(Ledger const &, Ledger const & prev, Mode mode); + * + * // Called when ledger is accepted by consensus + * void onAccept(Result const & result, + * RCLCxLedger const & prevLedger, + * NetClock::duration closeResolution, + * CloseTimes const & rawCloseTimes, + * Mode const & mode); + * + * // Called when ledger was forcibly accepted by consensus via the simulate + * // function. + * void onForceAccept(Result const & result, + * RCLCxLedger const & prevLedger, + * NetClock::duration closeResolution, + * CloseTimes const & rawCloseTimes, + * Mode const & mode); + * + * // Propose the position to peers. + * void propose(ConsensusProposal<...> const & pos); + * + * // Share a received peer proposal with other peer's. + * void share(PeerPosition_t const & prop); + * + * // Share a disputed transaction with peers + * void share(Txn const & tx); + * + * // Share given transaction set with peers + * void share(TxSet const &s); + * + * // Consensus timing parameters and constants + * ConsensusParms const & + * parms() const; + * }; + * @endcode + * + * @tparam Adaptor Defines types and provides helper functions needed to adapt + * Consensus to the larger application. + */ template class Consensus { @@ -319,34 +322,38 @@ class Consensus }; public: - //! Clock type for measuring time within the consensus code + /** + * Clock type for measuring time within the consensus code + */ using clock_type = beast::AbstractClock; Consensus(Consensus&&) noexcept = default; - /** Constructor. - - @param clock The clock used to internally sample consensus progress - @param adaptor The instance of the adaptor class - @param j The journal to log debug output - */ + /** + * Constructor. + * + * @param clock The clock used to internally sample consensus progress + * @param adaptor The instance of the adaptor class + * @param j The journal to log debug output + */ Consensus(clock_type const& clock, Adaptor& adaptor, beast::Journal j); - /** Kick-off the next round of consensus. - - Called by the client code to start each round of consensus. - - @param now The network adjusted time - @param prevLedgerID the ID of the last ledger - @param prevLedger The last ledger - @param nowUntrusted ID of nodes that are newly untrusted this round - @param proposing Whether we want to send proposals to peers this - round. - @param clog log object to which to append - - @note @b prevLedgerID is not required to the ID of @b prevLedger since - the ID may be known locally before the contents of the ledger arrive - */ + /** + * Kick-off the next round of consensus. + * + * Called by the client code to start each round of consensus. + * + * @param now The network adjusted time + * @param prevLedgerID the ID of the last ledger + * @param prevLedger The last ledger + * @param nowUntrusted ID of nodes that are newly untrusted this round + * @param proposing Whether we want to send proposals to peers this + * round. + * @param clog log object to which to append + * + * @note @b prevLedgerID is not required to the ID of @b prevLedger since + * the ID may be known locally before the contents of the ledger arrive + */ void startRound( NetClock::time_point const& now, @@ -356,61 +363,66 @@ public: bool proposing, std::unique_ptr const& clog = {}); - /** A peer has proposed a new position, adjust our tracking. - - @param now The network adjusted time - @param newProposal The new proposal from a peer - @return Whether we should do delayed relay of this proposal. - */ + /** + * A peer has proposed a new position, adjust our tracking. + * + * @param now The network adjusted time + * @param newProposal The new proposal from a peer + * @return Whether we should do delayed relay of this proposal. + */ bool peerProposal(NetClock::time_point const& now, PeerPosition_t const& newProposal); - /** Call periodically to drive consensus forward. - - @param now The network adjusted time - @param clog log object to which to append - */ + /** + * Call periodically to drive consensus forward. + * + * @param now The network adjusted time + * @param clog log object to which to append + */ void timerEntry( NetClock::time_point const& now, std::unique_ptr const& clog = {}); - /** Process a transaction set acquired from the network - - @param now The network adjusted time - @param txSet the transaction set - */ + /** + * Process a transaction set acquired from the network + * + * @param now The network adjusted time + * @param txSet the transaction set + */ void gotTxSet(NetClock::time_point const& now, TxSet_t const& txSet); - /** Simulate the consensus process without any network traffic. - - The end result, is that consensus begins and completes as if everyone - had agreed with whatever we propose. - - This function is only called from the rpc "ledger_accept" path with the - server in standalone mode and SHOULD NOT be used during the normal - consensus process. - - Simulate will call onForceAccept since clients are manually driving - consensus to the accept phase. - - @param now The current network adjusted time. - @param consensusDelay Duration to delay between closing and accepting the - ledger. Uses 100ms if unspecified. - */ + /** + * Simulate the consensus process without any network traffic. + * + * The end result, is that consensus begins and completes as if everyone + * had agreed with whatever we propose. + * + * This function is only called from the rpc "ledger_accept" path with the + * server in standalone mode and SHOULD NOT be used during the normal + * consensus process. + * + * Simulate will call onForceAccept since clients are manually driving + * consensus to the accept phase. + * + * @param now The current network adjusted time. + * @param consensusDelay Duration to delay between closing and accepting the + * ledger. Uses 100ms if unspecified. + */ void simulate( NetClock::time_point const& now, std::optional consensusDelay); - /** Get the previous ledger ID. - - The previous ledger is the last ledger seen by the consensus code and - should correspond to the most recent validated ledger seen by this peer. - - @return ID of previous ledger - */ + /** + * Get the previous ledger ID. + * + * The previous ledger is the last ledger seen by the consensus code and + * should correspond to the most recent validated ledger seen by this peer. + * + * @return ID of previous ledger + */ Ledger_t::ID prevLedgerID() const { @@ -423,13 +435,14 @@ public: return phase_; } - /** Get the Json state of the consensus process. - - Called by the consensus_info RPC. - - @param full True if verbose response desired. - @return The Json state. - */ + /** + * Get the Json state of the consensus process. + * + * Called by the consensus_info RPC. + * + * @param full True if verbose response desired. + * @return The Json state. + */ [[nodiscard]] json::Value getJson(bool full) const; @@ -446,46 +459,52 @@ private: void handleWrongLedger(Ledger_t::ID const& lgrId, std::unique_ptr const& clog); - /** Check if our previous ledger matches the network's. - - If the previous ledger differs, we are no longer in sync with - the network and need to bow out/switch modes. - */ + /** + * Check if our previous ledger matches the network's. + * + * If the previous ledger differs, we are no longer in sync with + * the network and need to bow out/switch modes. + */ void checkLedger(std::unique_ptr const& clog); - /** If we radically changed our consensus context for some reason, - we need to replay recent proposals so that they're not lost. - */ + /** + * If we radically changed our consensus context for some reason, + * we need to replay recent proposals so that they're not lost. + */ void playbackProposals(); - /** Handle a replayed or a new peer proposal. + /** + * Handle a replayed or a new peer proposal. */ bool peerProposalInternal(NetClock::time_point const& now, PeerPosition_t const& newProposal); - /** Handle pre-close phase. - - In the pre-close phase, the ledger is open as we wait for new - transactions. After enough time has elapsed, we will close the ledger, - switch to the establish phase and start the consensus process. - */ + /** + * Handle pre-close phase. + * + * In the pre-close phase, the ledger is open as we wait for new + * transactions. After enough time has elapsed, we will close the ledger, + * switch to the establish phase and start the consensus process. + */ void phaseOpen(std::unique_ptr const& clog); - /** Handle establish phase. - - In the establish phase, the ledger has closed and we work with peers - to reach consensus. Update our position only on the timer, and in this - phase. - - If we have consensus, move to the accepted phase. - */ + /** + * Handle establish phase. + * + * In the establish phase, the ledger has closed and we work with peers + * to reach consensus. Update our position only on the timer, and in this + * phase. + * + * If we have consensus, move to the accepted phase. + */ void phaseEstablish(std::unique_ptr const& clog); - /** Evaluate whether pausing increases likelihood of validation. + /** + * Evaluate whether pausing increases likelihood of validation. * * As a validator that has previously synced to the network, if our most * recent locally-validated ledger did not also achieve @@ -1245,7 +1264,8 @@ Consensus::shouldPause(std::unique_ptr const& clog) bool willPause = false; - /** Maximum phase with distinct thresholds to determine how + /** + * Maximum phase with distinct thresholds to determine how * many validators must be on our same ledger sequence number. * The threshold for the 1st (0) phase is >= the minimum number that * can achieve quorum. Threshold for the maximum phase is 100% @@ -1429,18 +1449,19 @@ Consensus::closeLedger(std::unique_ptr const& clog) } } -/** How many of the participants must agree to reach a given threshold? - -Note that the number may not precisely yield the requested percentage. -For example, with with size = 5 and percent = 70, we return 3, but -3 out of 5 works out to 60%. There are no security implications to -this. - -@param participants The number of participants (i.e. validators) -@param percent The percent that we want to reach - -@return the number of participants which must agree -*/ +/** + * How many of the participants must agree to reach a given threshold? + * + * Note that the number may not precisely yield the requested percentage. + * For example, with with size = 5 and percent = 70, we return 3, but + * 3 out of 5 works out to 60%. There are no security implications to + * this. + * + * @param participants The number of participants (i.e. validators) + * @param percent The percent that we want to reach + * + * @return the number of participants which must agree + */ inline int participantsNeeded(int participants, int percent) { diff --git a/src/xrpld/consensus/ConsensusParms.h b/src/xrpld/consensus/ConsensusParms.h index d00a5dc3c7..5fdbaa38bf 100644 --- a/src/xrpld/consensus/ConsensusParms.h +++ b/src/xrpld/consensus/ConsensusParms.h @@ -10,11 +10,12 @@ namespace xrpl { -/** Consensus algorithm parameters - - Parameters which control the consensus algorithm. This are not - meant to be changed arbitrarily. -*/ +/** + * Consensus algorithm parameters + * + * Parameters which control the consensus algorithm. This are not + * meant to be changed arbitrarily. + */ struct ConsensusParms { explicit ConsensusParms() = default; @@ -22,49 +23,63 @@ struct ConsensusParms //------------------------------------------------------------------------- // Validation and proposal durations are relative to NetClock times, so use // second resolution - /** The duration a validation remains current after its ledger's - close time. - - This is a safety to protect against very old validations and the time - it takes to adjust the close time accuracy window. - */ + /** + * The duration a validation remains current after its ledger's + * close time. + * + * This is a safety to protect against very old validations and the time + * it takes to adjust the close time accuracy window. + */ std::chrono::seconds const validationValidWall = std::chrono::minutes{5}; - /** Duration a validation remains current after first observed. - - The duration a validation remains current after the time we - first saw it. This provides faster recovery in very rare cases where the - number of validations produced by the network is lower than normal - */ + /** + * Duration a validation remains current after first observed. + * + * The duration a validation remains current after the time we + * first saw it. This provides faster recovery in very rare cases where the + * number of validations produced by the network is lower than normal + */ std::chrono::seconds const validationValidLocal = std::chrono::minutes{3}; - /** Duration pre-close in which validations are acceptable. - - The number of seconds before a close time that we consider a validation - acceptable. This protects against extreme clock errors - */ + /** + * Duration pre-close in which validations are acceptable. + * + * The number of seconds before a close time that we consider a validation + * acceptable. This protects against extreme clock errors + */ std::chrono::seconds const validationValidEarly = std::chrono::minutes{3}; - //! How long we consider a proposal fresh + /** + * How long we consider a proposal fresh + */ std::chrono::seconds const proposeFRESHNESS = std::chrono::seconds{20}; - //! How often we force generating a new proposal to keep ours fresh + /** + * How often we force generating a new proposal to keep ours fresh + */ std::chrono::seconds const proposeINTERVAL = std::chrono::seconds{12}; //------------------------------------------------------------------------- // Consensus durations are relative to the internal Consensus clock and use // millisecond resolution. - //! The percentage threshold above which we can declare consensus. + /** + * The percentage threshold above which we can declare consensus. + */ std::size_t const minConsensusPct = 80; - //! The duration a ledger may remain idle before closing + /** + * The duration a ledger may remain idle before closing + */ std::chrono::milliseconds const ledgerIdleInterval = std::chrono::seconds{15}; - //! The number of seconds we wait minimum to ensure participation + /** + * The number of seconds we wait minimum to ensure participation + */ std::chrono::milliseconds const ledgerMinConsensus = std::chrono::milliseconds{1950}; - /** The maximum amount of time to spend pausing for laggards. + /** + * The maximum amount of time to spend pausing for laggards. * * This should be sufficiently less than validationFRESHNESS so that * validators don't appear to be offline that are merely waiting for @@ -72,13 +87,19 @@ struct ConsensusParms */ std::chrono::milliseconds const ledgerMaxConsensus = std::chrono::seconds{15}; - //! Minimum number of seconds to wait to ensure others have computed the LCL + /** + * Minimum number of seconds to wait to ensure others have computed the LCL + */ std::chrono::milliseconds const ledgerMinClose = std::chrono::seconds{2}; - //! How often we check state or change positions + /** + * How often we check state or change positions + */ std::chrono::milliseconds const ledgerGRANULARITY = std::chrono::seconds{1}; - //! How long to wait before completely abandoning consensus + /** + * How long to wait before completely abandoning consensus + */ std::size_t const ledgerAbandonConsensusFactor = 10; /** @@ -89,16 +110,17 @@ struct ConsensusParms */ std::chrono::milliseconds const ledgerAbandonConsensus = std::chrono::seconds{120}; - /** The minimum amount of time to consider the previous round - to have taken. - - The minimum amount of time to consider the previous round - to have taken. This ensures that there is an opportunity - for a round at each avalanche threshold even if the - previous consensus was very fast. This should be at least - twice the interval between proposals (0.7s) divided by - the interval between mid and late consensus ([85-50]/100). - */ + /** + * The minimum amount of time to consider the previous round + * to have taken. + * + * The minimum amount of time to consider the previous round + * to have taken. This ensures that there is an opportunity + * for a round at each avalanche threshold even if the + * previous consensus was very fast. This should be at least + * twice the interval between proposals (0.7s) divided by + * the interval between mid and late consensus ([85-50]/100). + */ std::chrono::milliseconds const avMinConsensusTime = std::chrono::seconds{5}; //------------------------------------------------------------------------------ @@ -113,11 +135,13 @@ struct ConsensusParms std::size_t const consensusPct; AvalancheState const next; }; - //! Map the consensus requirement avalanche state to the amount of time that - //! must pass before moving to that state, the agreement percentage required - //! at that state, and the next state. "stuck" loops back on itself because - //! once we're stuck, we're stuck. - //! This structure allows for "looping" of states if needed. + /** + * Map the consensus requirement avalanche state to the amount of time that + * must pass before moving to that state, the agreement percentage required + * at that state, and the next state. "stuck" loops back on itself because + * once we're stuck, we're stuck. + * This structure allows for "looping" of states if needed. + */ std::map const avalancheCutoffs{ // {state, {time, percent, nextState}}, // Initial state: 50% of nodes must vote yes @@ -135,16 +159,22 @@ struct ConsensusParms {.consensusTime = 200, .consensusPct = 95, .next = AvalancheState::Stuck}}, }; - //! Percentage of nodes required to reach agreement on ledger close time + /** + * Percentage of nodes required to reach agreement on ledger close time + */ std::size_t const avCtConsensusPct = 75; - //! Number of rounds before certain actions can happen. + /** + * Number of rounds before certain actions can happen. + */ // (Moving to the next avalanche level, considering that votes are stalled // without consensus.) std::size_t const avMinRounds = 2; - //! Number of rounds before a stuck vote is considered unlikely to change - //! because voting stalled + /** + * Number of rounds before a stuck vote is considered unlikely to change + * because voting stalled + */ std::size_t const avStalledRounds = 4; }; diff --git a/src/xrpld/consensus/ConsensusProposal.h b/src/xrpld/consensus/ConsensusProposal.h index c27d5c2819..4586479286 100644 --- a/src/xrpld/consensus/ConsensusProposal.h +++ b/src/xrpld/consensus/ConsensusProposal.h @@ -13,28 +13,29 @@ #include namespace xrpl { -/** Represents a proposed position taken during a round of consensus. - - During consensus, peers seek agreement on a set of transactions to - apply to the prior ledger to generate the next ledger. Each peer takes a - position on whether to include or exclude potential transactions. - The position on the set of transactions is proposed to its peers as an - instance of the ConsensusProposal class. - - An instance of ConsensusProposal can be either our own proposal or one of - our peer's. - - As consensus proceeds, peers may change their position on the transaction, - or choose to abstain. Each successive proposal includes a strictly - monotonically increasing number (or, if a peer is choosing to abstain, - the special value `kSeqLeave`). - - Refer to @ref Consensus for requirements of the template arguments. - - @tparam NodeId Type used to uniquely identify nodes/peers - @tparam LedgerId Type used to uniquely identify ledgers - @tparam Position Type used to represent the position taken on transactions - under consideration during this round of consensus +/** + * Represents a proposed position taken during a round of consensus. + * + * During consensus, peers seek agreement on a set of transactions to + * apply to the prior ledger to generate the next ledger. Each peer takes a + * position on whether to include or exclude potential transactions. + * The position on the set of transactions is proposed to its peers as an + * instance of the ConsensusProposal class. + * + * An instance of ConsensusProposal can be either our own proposal or one of + * our peer's. + * + * As consensus proceeds, peers may change their position on the transaction, + * or choose to abstain. Each successive proposal includes a strictly + * monotonically increasing number (or, if a peer is choosing to abstain, + * the special value `kSeqLeave`). + * + * Refer to @ref Consensus for requirements of the template arguments. + * + * @tparam NodeId Type used to uniquely identify nodes/peers + * @tparam LedgerId Type used to uniquely identify ledgers + * @tparam Position Type used to represent the position taken on transactions + * under consideration during this round of consensus */ template class ConsensusProposal @@ -48,15 +49,16 @@ public: //< Sequence number when a peer wants to bow out and leave consensus static std::uint32_t const kSeqLeave = 0xffffffff; - /** Constructor - - @param prevLedger The previous ledger this proposal is building on. - @param seq The sequence number of this proposal. - @param position The position taken on transactions in this round. - @param closeTime Position of when this ledger closed. - @param now Time when the proposal was taken. - @param nodeID ID of node/peer taking this position. - */ + /** + * Constructor + * + * @param prevLedger The previous ledger this proposal is building on. + * @param seq The sequence number of this proposal. + * @param position The position taken on transactions in this round. + * @param closeTime Position of when this ledger closed. + * @param now Time when the proposal was taken. + * @param nodeID ID of node/peer taking this position. + */ ConsensusProposal( LedgerId const& prevLedger, std::uint32_t seq, @@ -73,83 +75,100 @@ public: { } - //! Identifying which peer took this position. + /** + * Identifying which peer took this position. + */ NodeId const& nodeID() const { return nodeID_; } - //! Get the proposed position. + /** + * Get the proposed position. + */ Position const& position() const { return position_; } - //! Get the prior accepted ledger this position is based on. + /** + * Get the prior accepted ledger this position is based on. + */ LedgerId const& prevLedger() const { return previousLedger_; } - /** Get the sequence number of this proposal - - Starting with an initial sequence number of `kSeqJoin`, successive - proposals from a peer will increase the sequence number. - - @return the sequence number - */ + /** + * Get the sequence number of this proposal + * + * Starting with an initial sequence number of `kSeqJoin`, successive + * proposals from a peer will increase the sequence number. + * + * @return the sequence number + */ std::uint32_t proposeSeq() const { return proposeSeq_; } - //! The current position on the consensus close time. + /** + * The current position on the consensus close time. + */ NetClock::time_point const& closeTime() const { return closeTime_; } - //! Get when this position was taken. + /** + * Get when this position was taken. + */ NetClock::time_point const& seenTime() const { return time_; } - /** Whether this is the first position taken during the current - consensus round. - */ + /** + * Whether this is the first position taken during the current + * consensus round. + */ bool isInitial() const { return proposeSeq_ == kSeqJoin; } - //! Get whether this node left the consensus process + /** + * Get whether this node left the consensus process + */ bool isBowOut() const { return proposeSeq_ == kSeqLeave; } - //! Get whether this position is stale relative to the provided cutoff + /** + * Get whether this position is stale relative to the provided cutoff + */ bool isStale(NetClock::time_point cutoff) const { return time_ <= cutoff; } - /** Update the position during the consensus process. This will increment - the proposal's sequence number if it has not already bowed out. - - @param newPosition The new position taken. - @param newCloseTime The new close time. - @param now the time The new position was taken + /** + * Update the position during the consensus process. This will increment + * the proposal's sequence number if it has not already bowed out. + * + * @param newPosition The new position taken. + * @param newCloseTime The new close time. + * @param now the time The new position was taken */ void changePosition( @@ -165,11 +184,12 @@ public: ++proposeSeq_; } - /** Leave consensus - - Update position to indicate the node left consensus. - - @param now Time when this node left consensus. + /** + * Leave consensus + * + * Update position to indicate the node left consensus. + * + * @param now Time when this node left consensus. */ void bowOut(NetClock::time_point now) @@ -190,7 +210,9 @@ public: return ss.str(); } - //! Get JSON representation for debugging + /** + * Get JSON representation for debugging + */ json::Value getJson() const { @@ -210,7 +232,9 @@ public: return ret; } - //! The digest for this proposal, used for signing purposes. + /** + * The digest for this proposal, used for signing purposes. + */ uint256 const& signingHash() const { @@ -228,25 +252,37 @@ public: } private: - //! Unique identifier of prior ledger this proposal is based on + /** + * Unique identifier of prior ledger this proposal is based on + */ LedgerId previousLedger_; - //! Unique identifier of the position this proposal is taking + /** + * Unique identifier of the position this proposal is taking + */ Position position_; - //! The ledger close time this position is taking + /** + * The ledger close time this position is taking + */ NetClock::time_point closeTime_; // !The time this position was last updated NetClock::time_point time_; - //! The sequence number of these positions taken by this node + /** + * The sequence number of these positions taken by this node + */ std::uint32_t proposeSeq_; - //! The identifier of the node taking this position + /** + * The identifier of the node taking this position + */ NodeId nodeID_; - //! The signing hash for this proposal + /** + * The signing hash for this proposal + */ mutable std::optional signingHash_; }; diff --git a/src/xrpld/consensus/ConsensusTypes.h b/src/xrpld/consensus/ConsensusTypes.h index 6b33f50662..4553e46f48 100644 --- a/src/xrpld/consensus/ConsensusTypes.h +++ b/src/xrpld/consensus/ConsensusTypes.h @@ -14,41 +14,50 @@ namespace xrpl { -/** Represents how a node currently participates in Consensus. - - A node participates in consensus in varying modes, depending on how - the node was configured by its operator and how well it stays in sync - with the network during consensus. - - @code - proposing observing - \ / - \---> wrongLedger <---/ - ^ - | - | - v - switchedLedger - @endcode - - We enter the round proposing or observing. If we detect we are working - on the wrong prior ledger, we go to wrongLedger and attempt to acquire - the right one. Once we acquire the right one, we go to the switchedLedger - mode. It is possible we fall behind again and find there is a new better - ledger, moving back and forth between wrongLedger and switchLedger as - we attempt to catch up. -*/ +/** + * Represents how a node currently participates in Consensus. + * + * A node participates in consensus in varying modes, depending on how + * the node was configured by its operator and how well it stays in sync + * with the network during consensus. + * + * @code + * proposing observing + * \ / + * \---> wrongLedger <---/ + * ^ + * | + * | + * v + * switchedLedger + * @endcode + * + * We enter the round proposing or observing. If we detect we are working + * on the wrong prior ledger, we go to wrongLedger and attempt to acquire + * the right one. Once we acquire the right one, we go to the switchedLedger + * mode. It is possible we fall behind again and find there is a new better + * ledger, moving back and forth between wrongLedger and switchLedger as + * we attempt to catch up. + */ enum class ConsensusMode { - //! We are normal participant in consensus and propose our position + /** + * We are normal participant in consensus and propose our position + */ Proposing, - //! We are observing peer positions, but not proposing our position + /** + * We are observing peer positions, but not proposing our position + */ Observing, - //! We have the wrong ledger and are attempting to acquire it + /** + * We have the wrong ledger and are attempting to acquire it + */ WrongLedger, - //! We switched ledgers since we started this consensus round but are now - //! running on what we believe is the correct ledger. This mode is as - //! if we entered the round observing, but is used to indicate we did - //! have the wrongLedger at some point. + /** + * We switched ledgers since we started this consensus round but are now + * running on what we believe is the correct ledger. This mode is as + * if we entered the round observing, but is used to indicate we did + * have the wrongLedger at some point. + */ SwitchedLedger }; @@ -70,32 +79,39 @@ to_string(ConsensusMode m) } } -/** Phases of consensus for a single ledger round. - - @code - "close" "accept" - open ------- > establish ---------> accepted - ^ | | - |---------------| | - ^ "startRound" | - |------------------------------------| - @endcode - - The typical transition goes from open to establish to accepted and - then a call to startRound begins the process anew. However, if a wrong prior - ledger is detected and recovered during the establish or accept phase, - consensus will internally go back to open (see Consensus::handleWrongLedger). -*/ +/** + * Phases of consensus for a single ledger round. + * + * @code + * "close" "accept" + * open ------- > establish ---------> accepted + * ^ | | + * |---------------| | + * ^ "startRound" | + * |------------------------------------| + * @endcode + * + * The typical transition goes from open to establish to accepted and + * then a call to startRound begins the process anew. However, if a wrong prior + * ledger is detected and recovered during the establish or accept phase, + * consensus will internally go back to open (see Consensus::handleWrongLedger). + */ enum class ConsensusPhase { - //! We haven't closed our ledger yet, but others might have + /** + * We haven't closed our ledger yet, but others might have + */ Open, - //! Establishing consensus by exchanging proposals with our peers + /** + * Establishing consensus by exchanging proposals with our peers + */ Establish, - //! We have accepted a new last closed ledger and are waiting on a call - //! to startRound to begin the next consensus round. No changes - //! to consensus phase occur while in this phase. + /** + * We have accepted a new last closed ledger and are waiting on a call + * to startRound to begin the next consensus round. No changes + * to consensus phase occur while in this phase. + */ Accepted, }; @@ -115,7 +131,8 @@ to_string(ConsensusPhase p) } } -/** Measures the duration of phases of consensus +/** + * Measures the duration of phases of consensus */ class ConsensusTimer { @@ -151,39 +168,47 @@ public: } }; -/** Stores the set of initial close times - - The initial consensus proposal from each peer has that peer's view of - when the ledger closed. This object stores all those close times for - analysis of clock drift between peers. -*/ +/** + * Stores the set of initial close times + * + * The initial consensus proposal from each peer has that peer's view of + * when the ledger closed. This object stores all those close times for + * analysis of clock drift between peers. + */ struct ConsensusCloseTimes { explicit ConsensusCloseTimes() = default; - //! Close time estimates, keep ordered for predictable traverse + /** + * Close time estimates, keep ordered for predictable traverse + */ std::map peers; - //! Our close time estimate + /** + * Our close time estimate + */ NetClock::time_point self; }; -/** Whether we have or don't have a consensus */ +/** + * Whether we have or don't have a consensus + */ enum class ConsensusState { - No, //!< We do not have consensus - MovedOn, //!< The network has consensus without us - Expired, //!< Consensus time limit has hard-expired - Yes //!< We have consensus along with the network + No, ///< We do not have consensus + MovedOn, ///< The network has consensus without us + Expired, ///< Consensus time limit has hard-expired + Yes ///< We have consensus along with the network }; -/** Encapsulates the result of consensus. - - Stores all relevant data for the outcome of consensus on a single - ledger. - - @tparam Traits Traits class defining the concrete consensus types used - by the application. -*/ +/** + * Encapsulates the result of consensus. + * + * Stores all relevant data for the outcome of consensus on a single + * ledger. + * + * @tparam Traits Traits class defining the concrete consensus types used + * by the application. + */ template struct ConsensusResult { @@ -200,13 +225,19 @@ struct ConsensusResult XRPL_ASSERT(txns.id() == position.position(), "xrpl::ConsensusResult : valid inputs"); } - //! The set of transactions consensus agrees go in the ledger + /** + * The set of transactions consensus agrees go in the ledger + */ TxSet_t txns; - //! Our proposed position on transactions/close time + /** + * Our proposed position on transactions/close time + */ Proposal_t position; - //! Transactions which are under dispute with our peers + /** + * Transactions which are under dispute with our peers + */ hash_map disputes; // Set of TxSet ids we have already compared/created disputes diff --git a/src/xrpld/consensus/DisputedTx.h b/src/xrpld/consensus/DisputedTx.h index 96df1536f5..12ed00d460 100644 --- a/src/xrpld/consensus/DisputedTx.h +++ b/src/xrpld/consensus/DisputedTx.h @@ -17,19 +17,20 @@ namespace xrpl { -/** A transaction discovered to be in dispute during consensus. - - During consensus, a @ref DisputedTx is created when a transaction - is discovered to be disputed. The object persists only as long as - the dispute. - - Undisputed transactions have no corresponding @ref DisputedTx object. - - Refer to @ref Consensus for details on the template type requirements. - - @tparam Tx The type for a transaction - @tparam NodeId The type for a node identifier -*/ +/** + * A transaction discovered to be in dispute during consensus. + * + * During consensus, a @ref DisputedTx is created when a transaction + * is discovered to be disputed. The object persists only as long as + * the dispute. + * + * Undisputed transactions have no corresponding @ref DisputedTx object. + * + * Refer to @ref Consensus for details on the template type requirements. + * + * @tparam Tx The type for a transaction + * @tparam NodeId The type for a node identifier + */ template class DisputedTx @@ -38,35 +39,42 @@ class DisputedTx using Map_t = boost::container::flat_map; public: - /** Constructor - - @param tx The transaction under dispute - @param ourVote Our vote on whether tx should be included - @param numPeers Anticipated number of peer votes - @param j Journal for debugging - */ + /** + * Constructor + * + * @param tx The transaction under dispute + * @param ourVote Our vote on whether tx should be included + * @param numPeers Anticipated number of peer votes + * @param j Journal for debugging + */ DisputedTx(Tx tx, bool ourVote, std::size_t numPeers, beast::Journal j) : ourVote_(ourVote), tx_(std::move(tx)), j_(j) { votes_.reserve(numPeers); } - //! The unique id/hash of the disputed transaction. + /** + * The unique id/hash of the disputed transaction. + */ [[nodiscard]] TxID_t const& id() const { return tx_.id(); } - //! Our vote on whether the transaction should be included. + /** + * Our vote on whether the transaction should be included. + */ [[nodiscard]] bool getOurVote() const { return ourVote_; } - //! Are we and our peers "stalled" where we probably won't change - //! our vote? + /** + * Are we and our peers "stalled" where we probably won't change + * our vote? + */ [[nodiscard]] bool stalled( ConsensusParms const& p, @@ -131,53 +139,62 @@ public: return stalled; } - //! The disputed transaction. + /** + * The disputed transaction. + */ [[nodiscard]] Tx const& tx() const { return tx_; } - //! Change our vote + /** + * Change our vote + */ void setOurVote(bool o) { ourVote_ = o; } - /** Change a peer's vote - - @param peer Identifier of peer. - @param votesYes Whether peer votes to include the disputed transaction. - - @return bool Whether the peer changed its vote. (A new vote counts as a - change.) - */ + /** + * Change a peer's vote + * + * @param peer Identifier of peer. + * @param votesYes Whether peer votes to include the disputed transaction. + * + * @return bool Whether the peer changed its vote. (A new vote counts as a + * change.) + */ [[nodiscard]] bool setVote(NodeId const& peer, bool votesYes); - /** Remove a peer's vote - - @param peer Identifier of peer. - */ + /** + * Remove a peer's vote + * + * @param peer Identifier of peer. + */ void unVote(NodeId const& peer); - /** Update our vote given progression of consensus. - - Updates our vote on this disputed transaction based on our peers' votes - and how far along consensus has proceeded. - - @param percentTime Percentage progress through consensus, e.g. 50% - through or 90%. - @param proposing Whether we are proposing to our peers in this round. - @param p Consensus parameters controlling thresholds for voting - @return Whether our vote changed - */ + /** + * Update our vote given progression of consensus. + * + * Updates our vote on this disputed transaction based on our peers' votes + * and how far along consensus has proceeded. + * + * @param percentTime Percentage progress through consensus, e.g. 50% + * through or 90%. + * @param proposing Whether we are proposing to our peers in this round. + * @param p Consensus parameters controlling thresholds for voting + * @return Whether our vote changed + */ bool updateVote(int percentTime, bool proposing, ConsensusParms const& p); - //! JSON representation of dispute, used for debugging + /** + * JSON representation of dispute, used for debugging + */ [[nodiscard]] json::Value getJson() const; @@ -187,11 +204,17 @@ private: bool ourVote_; //< Our vote (true is yes) Tx tx_; //< Transaction under dispute Map_t votes_; //< Map from NodeID to vote - //! The number of rounds we've gone without changing our vote + /** + * The number of rounds we've gone without changing our vote + */ std::size_t currentVoteCounter_ = 0; - //! Which minimum acceptance percentage phase we are currently in + /** + * Which minimum acceptance percentage phase we are currently in + */ ConsensusParms::AvalancheState avalancheState_ = ConsensusParms::AvalancheState::Init; - //! How long we have been in the current acceptance phase + /** + * How long we have been in the current acceptance phase + */ std::size_t avalancheCounter_ = 0; beast::Journal const j_; }; diff --git a/src/xrpld/consensus/LedgerTrie.h b/src/xrpld/consensus/LedgerTrie.h index a21eea2a8a..8b6d9b5bdb 100644 --- a/src/xrpld/consensus/LedgerTrie.h +++ b/src/xrpld/consensus/LedgerTrie.h @@ -19,7 +19,8 @@ namespace xrpl { -/** The tip of a span of ledger ancestry +/** + * The tip of a span of ledger ancestry */ template class SpanTip @@ -37,14 +38,15 @@ public: // The ID of the tip ledger ID id; - /** Lookup the ID of an ancestor of the tip ledger - - @param s The sequence number of the ancestor - @return The ID of the ancestor with that sequence number - - @note s must be less than or equal to the sequence number of the - tip ledger - */ + /** + * Lookup the ID of an ancestor of the tip ledger + * + * @param s The sequence number of the ancestor + * @return The ID of the ancestor with that sequence number + * + * @note s must be less than or equal to the sequence number of the + * tip ledger + */ [[nodiscard]] ID ancestor(Seq const& s) const { @@ -199,12 +201,13 @@ struct Node std::vector> children; Node* parent = nullptr; - /** Remove the given node from this Node's children - - @param child The address of the child node to remove - @note The child must be a member of the vector. The passed pointer - will be dangling as a result of this call - */ + /** + * Remove the given node from this Node's children + * + * @param child The address of the child node to remove + * @note The child must be a member of the vector. The passed pointer + * will be dangling as a result of this call + */ void erase(Node const* child) { @@ -245,83 +248,84 @@ struct Node }; } // namespace ledger_trie_detail -/** Ancestry trie of ledgers - - A compressed trie tree that maintains validation support of recent ledgers - based on their ancestry. - - The compressed trie structure comes from recognizing that ledger history - can be viewed as a string over the alphabet of ledger ids. That is, - a given ledger with sequence number `seq` defines a length `seq` string, - with i-th entry equal to the id of the ancestor ledger with sequence - number i. "Sequence" strings with a common prefix share those ancestor - ledgers in common. Tracking this ancestry information and relations across - all validated ledgers is done conveniently in a compressed trie. A node in - the trie is an ancestor of all its children. If a parent node has sequence - number `seq`, each child node has a different ledger starting at `seq+1`. - The compression comes from the invariant that any non-root node with 0 tip - support has either no children or multiple children. In other words, a - non-root 0-tip-support node can be combined with its single child. - - Each node has a tipSupport, which is the number of current validations for - that particular ledger. The node's branch support is the sum of the tip - support and the branch support of that node's children: - - @code - node->branchSupport = node->tipSupport; - for (child : node->children) - node->branchSupport += child->branchSupport; - @endcode - - The templated Ledger type represents a ledger which has a unique history. - It should be lightweight and cheap to copy. - - @code - // Identifier types that should be equality-comparable and copyable - struct ID; - struct Seq; - - struct Ledger - { - struct MakeGenesis{}; - - // The genesis ledger represents a ledger that prefixes all other - // ledgers - Ledger(MakeGenesis{}); - - Ledger(Ledger const&); - Ledger& operator=(Ledger const&); - - // Return the sequence number of this ledger - Seq seq() const; - - // Return the ID of this ledger's ancestor with given sequence number - // or ID{0} if unknown - ID - operator[](Seq s); - - }; - - // Return the sequence number of the first possible mismatching ancestor - // between two ledgers - Seq - mismatch(ledgerA, ledgerB); - @endcode - - The unique history invariant of ledgers requires any ledgers that agree - on the id of a given sequence number agree on ALL ancestors before that - ledger: - - @code - Ledger a,b; - // For all Seq s: - if(a[s] == b[s]); - for(Seq p = 0; p < s; ++p) - assert(a[p] == b[p]); - @endcode - - @tparam Ledger A type representing a ledger and its history -*/ +/** + * Ancestry trie of ledgers + * + * A compressed trie tree that maintains validation support of recent ledgers + * based on their ancestry. + * + * The compressed trie structure comes from recognizing that ledger history + * can be viewed as a string over the alphabet of ledger ids. That is, + * a given ledger with sequence number `seq` defines a length `seq` string, + * with i-th entry equal to the id of the ancestor ledger with sequence + * number i. "Sequence" strings with a common prefix share those ancestor + * ledgers in common. Tracking this ancestry information and relations across + * all validated ledgers is done conveniently in a compressed trie. A node in + * the trie is an ancestor of all its children. If a parent node has sequence + * number `seq`, each child node has a different ledger starting at `seq+1`. + * The compression comes from the invariant that any non-root node with 0 tip + * support has either no children or multiple children. In other words, a + * non-root 0-tip-support node can be combined with its single child. + * + * Each node has a tipSupport, which is the number of current validations for + * that particular ledger. The node's branch support is the sum of the tip + * support and the branch support of that node's children: + * + * @code + * node->branchSupport = node->tipSupport; + * for (child : node->children) + * node->branchSupport += child->branchSupport; + * @endcode + * + * The templated Ledger type represents a ledger which has a unique history. + * It should be lightweight and cheap to copy. + * + * @code + * // Identifier types that should be equality-comparable and copyable + * struct ID; + * struct Seq; + * + * struct Ledger + * { + * struct MakeGenesis{}; + * + * // The genesis ledger represents a ledger that prefixes all other + * // ledgers + * Ledger(MakeGenesis{}); + * + * Ledger(Ledger const&); + * Ledger& operator=(Ledger const&); + * + * // Return the sequence number of this ledger + * Seq seq() const; + * + * // Return the ID of this ledger's ancestor with given sequence number + * // or ID{0} if unknown + * ID + * operator[](Seq s); + * + * }; + * + * // Return the sequence number of the first possible mismatching ancestor + * // between two ledgers + * Seq + * mismatch(ledgerA, ledgerB); + * @endcode + * + * The unique history invariant of ledgers requires any ledgers that agree + * on the id of a given sequence number agree on ALL ancestors before that + * ledger: + * + * @code + * Ledger a,b; + * // For all Seq s: + * if(a[s] == b[s]); + * for(Seq p = 0; p < s; ++p) + * assert(a[p] == b[p]); + * @endcode + * + * @tparam Ledger A type representing a ledger and its history + */ template class LedgerTrie { @@ -338,12 +342,13 @@ class LedgerTrie // Count of the tip support for each sequence number std::map seqSupport_; - /** Find the node in the trie that represents the longest common ancestry - with the given ledger. - - @return Pair of the found node and the sequence number of the first - ledger difference. - */ + /** + * Find the node in the trie that represents the longest common ancestry + * with the given ledger. + * + * @return Pair of the found node and the sequence number of the first + * ledger difference. + */ [[nodiscard]] std::pair find(Ledger const& ledger) const { @@ -377,12 +382,13 @@ class LedgerTrie return std::make_pair(curr, pos); } - /** Find the node in the trie with an exact match to the given ledger ID - - @return the found node or nullptr if an exact match was not found. - - @note O(n) since this searches all nodes until a match is found - */ + /** + * Find the node in the trie with an exact match to the given ledger ID + * + * @return the found node or nullptr if an exact match was not found. + * + * @note O(n) since this searches all nodes until a match is found + */ Node* findByLedgerID(Ledger const& ledger, Node* parent = nullptr) const { @@ -420,10 +426,11 @@ public: { } - /** Insert and/or increment the support for the given ledger. - - @param ledger A ledger and its ancestry - @param count The count of support for this ledger + /** + * Insert and/or increment the support for the given ledger. + * + * @param ledger A ledger and its ancestry + * @param count The count of support for this ledger */ void insert(Ledger const& ledger, std::uint32_t count = 1) @@ -504,13 +511,14 @@ public: seqSupport_[ledger.seq()] += count; } - /** Decrease support for a ledger, removing and compressing if possible. - - @param ledger The ledger history to remove - @param count The amount of tip support to remove - - @return Whether a matching node was decremented and possibly removed. - */ + /** + * Decrease support for a ledger, removing and compressing if possible. + * + * @param ledger The ledger history to remove + * @param count The amount of tip support to remove + * + * @return Whether a matching node was decremented and possibly removed. + */ bool remove(Ledger const& ledger, std::uint32_t count = 1) { @@ -564,10 +572,11 @@ public: return true; } - /** Return count of tip support for the specific ledger. - - @param ledger The ledger to lookup - @return The number of entries in the trie for this *exact* ledger + /** + * Return count of tip support for the specific ledger. + * + * @param ledger The ledger to lookup + * @return The number of entries in the trie for this *exact* ledger */ [[nodiscard]] std::uint32_t tipSupport(Ledger const& ledger) const @@ -577,11 +586,12 @@ public: return 0; } - /** Return the count of branch support for the specific ledger - - @param ledger The ledger to lookup - @return The number of entries in the trie for this ledger or a - descendant + /** + * Return the count of branch support for the specific ledger + * + * @param ledger The ledger to lookup + * @return The number of entries in the trie for this ledger or a + * descendant */ [[nodiscard]] std::uint32_t branchSupport(Ledger const& ledger) const @@ -598,65 +608,66 @@ public: return loc ? loc->branchSupport : 0; } - /** Return the preferred ledger ID - - The preferred ledger is used to determine the working ledger - for consensus amongst competing alternatives. - - Recall that each validator is normally validating a chain of ledgers, - e.g. A->B->C->D. However, if due to network connectivity or other - issues, validators generate different chains - - @code - /->C - A->B - \->D->E - @endcode - - we need a way for validators to converge on the chain with the most - support. We call this the preferred ledger. Intuitively, the idea is to - be conservative and only switch to a different branch when you see - enough peer validations to *know* another branch won't have preferred - support. - - The preferred ledger is found by walking this tree of validated ledgers - starting from the common ancestor ledger. - - At each sequence number, we have - - - The prior sequence preferred ledger, e.g. B. - - The (tip) support of ledgers with this sequence number,e.g. the - number of validators whose last validation was for C or D. - - The (branch) total support of all descendants of the current - sequence number ledgers, e.g. the branch support of D is the - tip support of D plus the tip support of E; the branch support of - C is just the tip support of C. - - The number of validators that have yet to validate a ledger - with this sequence number (uncommitted support). Uncommitted - includes all validators whose last sequence number is smaller than - our last issued sequence number, since due to asynchrony, we may - not have heard from those nodes yet. - - The preferred ledger for this sequence number is then the ledger - with relative majority of support, where uncommitted support - can be given to ANY ledger at that sequence number - (including one not yet known). If no such preferred ledger exists, then - the prior sequence preferred ledger is the overall preferred ledger. - - In this example, for D to be preferred, the number of validators - supporting it or a descendant must exceed the number of validators - supporting C _plus_ the current uncommitted support. This is because if - all uncommitted validators end up validating C, that new support must - be less than that for D to be preferred. - - If a preferred ledger does exist, then we continue with the next - sequence using that ledger as the root. - - @param largestIssued The sequence number of the largest validation - issued by this node. - @return Pair with the sequence number and ID of the preferred ledger or - std::nullopt if no preferred ledger exists - */ + /** + * Return the preferred ledger ID + * + * The preferred ledger is used to determine the working ledger + * for consensus amongst competing alternatives. + * + * Recall that each validator is normally validating a chain of ledgers, + * e.g. A->B->C->D. However, if due to network connectivity or other + * issues, validators generate different chains + * + * @code + * /->C + * A->B + * \->D->E + * @endcode + * + * we need a way for validators to converge on the chain with the most + * support. We call this the preferred ledger. Intuitively, the idea is to + * be conservative and only switch to a different branch when you see + * enough peer validations to *know* another branch won't have preferred + * support. + * + * The preferred ledger is found by walking this tree of validated ledgers + * starting from the common ancestor ledger. + * + * At each sequence number, we have + * + * - The prior sequence preferred ledger, e.g. B. + * - The (tip) support of ledgers with this sequence number,e.g. the + * number of validators whose last validation was for C or D. + * - The (branch) total support of all descendants of the current + * sequence number ledgers, e.g. the branch support of D is the + * tip support of D plus the tip support of E; the branch support of + * C is just the tip support of C. + * - The number of validators that have yet to validate a ledger + * with this sequence number (uncommitted support). Uncommitted + * includes all validators whose last sequence number is smaller than + * our last issued sequence number, since due to asynchrony, we may + * not have heard from those nodes yet. + * + * The preferred ledger for this sequence number is then the ledger + * with relative majority of support, where uncommitted support + * can be given to ANY ledger at that sequence number + * (including one not yet known). If no such preferred ledger exists, then + * the prior sequence preferred ledger is the overall preferred ledger. + * + * In this example, for D to be preferred, the number of validators + * supporting it or a descendant must exceed the number of validators + * supporting C _plus_ the current uncommitted support. This is because if + * all uncommitted validators end up validating C, that new support must + * be less than that for D to be preferred. + * + * If a preferred ledger does exist, then we continue with the next + * sequence using that ledger as the root. + * + * @param largestIssued The sequence number of the largest validation + * issued by this node. + * @return Pair with the sequence number and ID of the preferred ledger or + * std::nullopt if no preferred ledger exists + */ [[nodiscard]] std::optional> getPreferred(Seq const largestIssued) const { @@ -758,7 +769,8 @@ public: return curr->span.tip(); } - /** Return whether the trie is tracking any ledgers + /** + * Return whether the trie is tracking any ledgers */ [[nodiscard]] bool empty() const @@ -766,7 +778,8 @@ public: return !root_ || root_->branchSupport == 0; } - /** Dump an ascii representation of the trie to the stream + /** + * Dump an ascii representation of the trie to the stream */ void dump(std::ostream& o) const @@ -774,7 +787,8 @@ public: dumpImpl(o, root_, 0); } - /** Dump JSON representation of trie state + /** + * Dump JSON representation of trie state */ [[nodiscard]] json::Value getJson() const @@ -787,7 +801,8 @@ public: return res; } - /** Check the compressed trie and support invariants. + /** + * Check the compressed trie and support invariants. */ [[nodiscard]] bool checkInvariants() const diff --git a/src/xrpld/consensus/Validations.h b/src/xrpld/consensus/Validations.h index a204cd0c78..2696804c86 100644 --- a/src/xrpld/consensus/Validations.h +++ b/src/xrpld/consensus/Validations.h @@ -26,48 +26,54 @@ namespace xrpl { -/** Timing parameters to control validation staleness and expiration. - - @note These are protocol level parameters that should not be changed without - careful consideration. They are *not* implemented as static constexpr - to allow simulation code to test alternate parameter settings. +/** + * Timing parameters to control validation staleness and expiration. + * + * @note These are protocol level parameters that should not be changed without + * careful consideration. They are *not* implemented as static constexpr + * to allow simulation code to test alternate parameter settings. */ struct ValidationParms { explicit ValidationParms() = default; - /** The number of seconds a validation remains current after its ledger's - close time. - - This is a safety to protect against very old validations and the time - it takes to adjust the close time accuracy window. - */ + /** + * The number of seconds a validation remains current after its ledger's + * close time. + * + * This is a safety to protect against very old validations and the time + * it takes to adjust the close time accuracy window. + */ std::chrono::seconds validationCurrentWall = std::chrono::minutes{5}; - /** Duration a validation remains current after first observed. - - The number of seconds a validation remains current after the time we - first saw it. This provides faster recovery in very rare cases where the - number of validations produced by the network is lower than normal - */ + /** + * Duration a validation remains current after first observed. + * + * The number of seconds a validation remains current after the time we + * first saw it. This provides faster recovery in very rare cases where the + * number of validations produced by the network is lower than normal + */ std::chrono::seconds validationCurrentLocal = std::chrono::minutes{3}; - /** Duration pre-close in which validations are acceptable. - - The number of seconds before a close time that we consider a validation - acceptable. This protects against extreme clock errors - */ + /** + * Duration pre-close in which validations are acceptable. + * + * The number of seconds before a close time that we consider a validation + * acceptable. This protects against extreme clock errors + */ std::chrono::seconds validationCurrentEarly = std::chrono::minutes{3}; - /** Duration a set of validations for a given ledger hash remain valid - - The number of seconds before a set of validations for a given ledger - hash can expire. This keeps validations for recent ledgers available - for a reasonable interval. - */ + /** + * Duration a set of validations for a given ledger hash remain valid + * + * The number of seconds before a set of validations for a given ledger + * hash can expire. This keeps validations for recent ledgers available + * for a reasonable interval. + */ std::chrono::seconds validationSetExpires = std::chrono::minutes{10}; - /** How long we consider a validation fresh. + /** + * How long we consider a validation fresh. * * The number of seconds since a validation has been seen for it to * be considered to accurately represent a live proposer's most recent @@ -78,12 +84,13 @@ struct ValidationParms std::chrono::seconds validationFRESHNESS = std::chrono::seconds{20}; }; -/** Enforce validation increasing sequence requirement. - - Helper class for enforcing that a validation must be larger than all - unexpired validation sequence numbers previously issued by the validator - tracked by the instance of this class. -*/ +/** + * Enforce validation increasing sequence requirement. + * + * Helper class for enforcing that a validation must be larger than all + * unexpired validation sequence numbers previously issued by the validator + * tracked by the instance of this class. + */ template class SeqEnforcer { @@ -92,18 +99,19 @@ class SeqEnforcer time_point when_; public: - /** Try advancing the largest observed validation ledger sequence - - Try setting the largest validation sequence observed, but return false - if it violates the invariant that a validation must be larger than all - unexpired validation sequence numbers. - - @param now The current time - @param s The sequence number we want to validate - @param p Validation parameters - - @return Whether the validation satisfies the invariant - */ + /** + * Try advancing the largest observed validation ledger sequence + * + * Try setting the largest validation sequence observed, but return false + * if it violates the invariant that a validation must be larger than all + * unexpired validation sequence numbers. + * + * @param now The current time + * @param s The sequence number we want to validate + * @param p Validation parameters + * + * @return Whether the validation satisfies the invariant + */ bool operator()(time_point now, Seq s, ValidationParms const& p) { @@ -123,17 +131,18 @@ public: } }; -/** Whether a validation is still current - - Determines whether a validation can still be considered the current - validation from a node based on when it was signed by that node and first - seen by this node. - - @param p ValidationParms with timing parameters - @param now Current time - @param signTime When the validation was signed - @param seenTime When the validation was first seen locally -*/ +/** + * Whether a validation is still current + * + * Determines whether a validation can still be considered the current + * validation from a node based on when it was signed by that node and first + * seen by this node. + * + * @param p ValidationParms with timing parameters + * @param now Current time + * @param signTime When the validation was signed + * @param seenTime When the validation was first seen locally + */ inline bool isCurrent( ValidationParms const& p, @@ -153,17 +162,29 @@ isCurrent( ((seenTime == NetClock::time_point{}) || (seenTime < (now + p.validationCurrentLocal))); } -/** Status of validation we received */ +/** + * Status of validation we received + */ enum class ValStatus { - /// This was a new validation and was added + /** + * This was a new validation and was added + */ Current, - /// Not current or was older than current from this node + /** + * Not current or was older than current from this node + */ Stale, - /// A validation violates the increasing seq requirement + /** + * A validation violates the increasing seq requirement + */ BadSeq, - /// Multiple validations by a validator for the same ledger + /** + * Multiple validations by a validator for the same ledger + */ Multiple, - /// Multiple validations by a validator for different ledgers + /** + * Multiple validations by a validator for different ledgers + */ Conflicting }; @@ -187,92 +208,93 @@ to_string(ValStatus m) } } -/** Maintains current and recent ledger validations. - - Manages storage and queries related to validations received on the network. - Stores the most current validation from nodes and sets of recent - validations grouped by ledger identifier. - - Stored validations are not necessarily from trusted nodes, so clients - and implementations should take care to use `trusted` member functions or - check the validation's trusted status. - - This class uses a generic interface to allow adapting Validations for - specific applications. The Adaptor template implements a set of helper - functions and type definitions. The code stubs below outline the - interface and type requirements. - - - @warning The Adaptor::MutexType is used to manage concurrent access to - private members of Validations but does not manage any data in the - Adaptor instance itself. - - @code - - // Conforms to the Ledger type requirements of LedgerTrie - struct Ledger; - - struct Validation - { - using NodeID = ...; - using NodeKey = ...; - - // Ledger ID associated with this validation - Ledger::ID ledgerID() const; - - // Sequence number of validation's ledger (0 means no sequence number) - Ledger::Seq seq() const - - // When the validation was signed - NetClock::time_point signTime() const; - - // When the validation was first observed by this node - NetClock::time_point seenTime() const; - - // Signing key of node that published the validation - NodeKey key() const; - - // Whether the publishing node was trusted at the time the validation - // arrived - bool trusted() const; - - // Set the validation as trusted - void setTrusted(); - - // Set the validation as untrusted - void setUntrusted(); - - // Whether this is a full or partial validation - bool full() const; - - // Identifier for this node that remains fixed even when rotating - // signing keys - NodeID nodeID() const; - - implementation_specific_t - unwrap() -> return the implementation-specific type being wrapped - - // ... implementation specific - }; - - class Adaptor - { - using Mutex = std::mutex; - using Validation = Validation; - using Ledger = Ledger; - - // Return the current network time (used to determine staleness) - NetClock::time_point now() const; - - // Attempt to acquire a specific ledger. - std::optional acquire(Ledger::ID const & ledgerID); - - // ... implementation specific - }; - @endcode - - @tparam Adaptor Provides type definitions and callbacks -*/ +/** + * Maintains current and recent ledger validations. + * + * Manages storage and queries related to validations received on the network. + * Stores the most current validation from nodes and sets of recent + * validations grouped by ledger identifier. + * + * Stored validations are not necessarily from trusted nodes, so clients + * and implementations should take care to use `trusted` member functions or + * check the validation's trusted status. + * + * This class uses a generic interface to allow adapting Validations for + * specific applications. The Adaptor template implements a set of helper + * functions and type definitions. The code stubs below outline the + * interface and type requirements. + * + * + * @warning The Adaptor::MutexType is used to manage concurrent access to + * private members of Validations but does not manage any data in the + * Adaptor instance itself. + * + * @code + * + * // Conforms to the Ledger type requirements of LedgerTrie + * struct Ledger; + * + * struct Validation + * { + * using NodeID = ...; + * using NodeKey = ...; + * + * // Ledger ID associated with this validation + * Ledger::ID ledgerID() const; + * + * // Sequence number of validation's ledger (0 means no sequence number) + * Ledger::Seq seq() const + * + * // When the validation was signed + * NetClock::time_point signTime() const; + * + * // When the validation was first observed by this node + * NetClock::time_point seenTime() const; + * + * // Signing key of node that published the validation + * NodeKey key() const; + * + * // Whether the publishing node was trusted at the time the validation + * // arrived + * bool trusted() const; + * + * // Set the validation as trusted + * void setTrusted(); + * + * // Set the validation as untrusted + * void setUntrusted(); + * + * // Whether this is a full or partial validation + * bool full() const; + * + * // Identifier for this node that remains fixed even when rotating + * // signing keys + * NodeID nodeID() const; + * + * implementation_specific_t + * unwrap() -> return the implementation-specific type being wrapped + * + * // ... implementation specific + * }; + * + * class Adaptor + * { + * using Mutex = std::mutex; + * using Validation = Validation; + * using Ledger = Ledger; + * + * // Return the current network time (used to determine staleness) + * NetClock::time_point now() const; + * + * // Attempt to acquire a specific ledger. + * std::optional acquire(Ledger::ID const & ledgerID); + * + * // ... implementation specific + * }; + * @endcode + * + * @tparam Adaptor Provides type definitions and callbacks + */ template class Validations { @@ -299,7 +321,9 @@ class Validations // Sequence of the largest validation received from each node hash_map> seqEnforcers_; - //! Validations from listed nodes, indexed by ledger id (partial and full) + /** + * Validations from listed nodes, indexed by ledger id (partial and full) + */ beast::aged_unordered_map< ID, hash_map, @@ -397,19 +421,20 @@ private: trie_.insert(ledger); } - /** Process a new validation - - Process a new trusted validation from a validator. This will be - reflected only after the validated ledger is successfully acquired by - the local node. In the interim, the prior validated ledger from this - node remains. - - @param lock Existing lock of mutex_ - @param nodeID The node identifier of the validating node - @param val The trusted validation issued by the node - @param prior If not none, the last current validated ledger Seq,ID of - key - */ + /** + * Process a new validation + * + * Process a new trusted validation from a validator. This will be + * reflected only after the validated ledger is successfully acquired by + * the local node. In the interim, the prior validated ledger from this + * node remains. + * + * @param lock Existing lock of mutex_ + * @param nodeID The node identifier of the validating node + * @param val The trusted validation issued by the node + * @param prior If not none, the last current validated ledger Seq,ID of + * key + */ void updateTrie( std::scoped_lock const& lock, @@ -452,18 +477,18 @@ private: } } - /** Use the trie for a calculation - - Accessing the trie through this helper ensures acquiring validations - are checked and any stale validations are flushed from the trie. - - @param lock Existing lock of mutex_ - @param f Invocable with signature (LedgerTrie &) - - @warning The invocable `f` is expected to be a simple transformation of - its arguments and will be called with mutex_ under lock. - - */ + /** + * Use the trie for a calculation + * + * Accessing the trie through this helper ensures acquiring validations + * are checked and any stale validations are flushed from the trie. + * + * @param lock Existing lock of mutex_ + * @param f Invocable with signature (LedgerTrie &) + * + * @warning The invocable `f` is expected to be a simple transformation of + * its arguments and will be called with mutex_ under lock. + */ template auto withTrie(std::scoped_lock const& lock, F&& f) @@ -474,21 +499,22 @@ private: return f(trie_); } - /** Iterate current validations. - - Iterate current validations, flushing any which are stale. - - @param lock Existing lock of mutex_ - @param pre Invocable with signature (std::size_t) called prior to - looping. - @param f Invocable with signature (NodeID const &, Validations const &) - for each current validation. - - @note The invocable `pre` is called _prior_ to checking for staleness - and reflects an upper-bound on the number of calls to `f. - @warning The invocable `f` is expected to be a simple transformation of - its arguments and will be called with mutex_ under lock. - */ + /** + * Iterate current validations. + * + * Iterate current validations, flushing any which are stale. + * + * @param lock Existing lock of mutex_ + * @param pre Invocable with signature (std::size_t) called prior to + * looping. + * @param f Invocable with signature (NodeID const &, Validations const &) + * for each current validation. + * + * @note The invocable `pre` is called _prior_ to checking for staleness + * and reflects an upper-bound on the number of calls to `f. + * @warning The invocable `f` is expected to be a simple transformation of + * its arguments and will be called with mutex_ under lock. + */ template void @@ -515,18 +541,19 @@ private: } } - /** Iterate the set of validations associated with a given ledger id - - @param lock Existing lock on mutex_ - @param ledgerID The identifier of the ledger - @param pre Invocable with signature(std::size_t) - @param f Invocable with signature (NodeID const &, Validation const &) - - @note The invocable `pre` is called prior to iterating validations. The - argument is the number of times `f` will be called. - @warning The invocable f is expected to be a simple transformation of - its arguments and will be called with mutex_ under lock. - */ + /** + * Iterate the set of validations associated with a given ledger id + * + * @param lock Existing lock on mutex_ + * @param ledgerID The identifier of the ledger + * @param pre Invocable with signature(std::size_t) + * @param f Invocable with signature (NodeID const &, Validation const &) + * + * @note The invocable `pre` is called prior to iterating validations. The + * argument is the number of times `f` will be called. + * @warning The invocable f is expected to be a simple transformation of + * its arguments and will be called with mutex_ under lock. + */ template void byLedger(std::scoped_lock const&, ID const& ledgerID, Pre&& pre, F&& f) @@ -543,12 +570,13 @@ private: } public: - /** Constructor - - @param p ValidationParms to control staleness/expiration of validations - @param c Clock to use for expiring validations stored by ledger - @param ts Parameters for constructing Adaptor instance - */ + /** + * Constructor + * + * @param p ValidationParms to control staleness/expiration of validations + * @param c Clock to use for expiring validations stored by ledger + * @param ts Parameters for constructing Adaptor instance + */ template Validations( ValidationParms const& p, @@ -558,7 +586,8 @@ public: { } - /** Return the adaptor instance + /** + * Return the adaptor instance */ Adaptor const& adaptor() const @@ -566,7 +595,8 @@ public: return adaptor_; } - /** Return the validation timing parameters + /** + * Return the validation timing parameters */ ValidationParms const& parms() const @@ -574,13 +604,14 @@ public: return parms_; } - /** Return whether the local node can issue a validation for the given - sequence number - - @param s The sequence number of the ledger the node wants to validate - @return Whether the validation satisfies the invariant, updating the - largest sequence number seen accordingly - */ + /** + * Return whether the local node can issue a validation for the given + * sequence number + * + * @param s The sequence number of the ledger the node wants to validate + * @return Whether the validation satisfies the invariant, updating the + * largest sequence number seen accordingly + */ bool canValidateSeq(Seq const s) { @@ -588,14 +619,15 @@ public: return localSeqEnforcer_(byLedger_.clock().now(), s, parms_); } - /** Add a new validation - - Attempt to add a new validation. - - @param nodeID The identity of the node issuing this validation - @param val The validation to store - @return The outcome - */ + /** + * Add a new validation + * + * Attempt to add a new validation. + * + * @param nodeID The identity of the node issuing this validation + * @param val The validation to store + * @return The outcome + */ ValStatus add(NodeID const& nodeID, Validation const& val) { @@ -696,11 +728,12 @@ public: toKeep_ = {low, high}; } - /** Expire old validation sets - - Remove validation sets that were accessed more than - validationSET_EXPIRES ago and were not asked to keep. - */ + /** + * Expire old validation sets + * + * Remove validation sets that were accessed more than + * validationSET_EXPIRES ago and were not asked to keep. + */ void expire(beast::Journal const& j) { @@ -751,15 +784,16 @@ public: << "ms"; } - /** Update trust status of validations - - Updates the trusted status of known validations to account for nodes - that have been added or removed from the UNL. This also updates the trie - to ensure only currently trusted nodes' validations are used. - - @param added Identifiers of nodes that are now trusted - @param removed Identifiers of nodes that are no longer trusted - */ + /** + * Update trust status of validations + * + * Updates the trusted status of known validations to account for nodes + * that have been added or removed from the UNL. This also updates the trie + * to ensure only currently trusted nodes' validations are used. + * + * @param added Identifiers of nodes that are now trusted + * @param removed Identifiers of nodes that are no longer trusted + */ void trustChanged(hash_set const& added, hash_set const& removed) { @@ -803,18 +837,19 @@ public: return trie_.getJson(); } - /** Return the sequence number and ID of the preferred working ledger - - A ledger is preferred if it has more support amongst trusted validators - and is *not* an ancestor of the current working ledger; otherwise it - remains the current working ledger. - - @param curr The local node's current working ledger - - @return The sequence and id of the preferred working ledger, - or std::nullopt if no trusted validations are available to - determine the preferred ledger. - */ + /** + * Return the sequence number and ID of the preferred working ledger + * + * A ledger is preferred if it has more support amongst trusted validators + * and is *not* an ancestor of the current working ledger; otherwise it + * remains the current working ledger. + * + * @param curr The local node's current working ledger + * + * @return The sequence and id of the preferred working ledger, + * or std::nullopt if no trusted validations are available to + * determine the preferred ledger. + */ std::optional> getPreferred(Ledger const& curr) { @@ -859,15 +894,16 @@ public: return std::make_pair(curr.seq(), curr.id()); } - /** Get the ID of the preferred working ledger that exceeds a minimum valid - ledger sequence number - - @param curr Current working ledger - @param minValidSeq Minimum allowed sequence number - - @return ID Of the preferred ledger, or curr if the preferred ledger - is not valid - */ + /** + * Get the ID of the preferred working ledger that exceeds a minimum valid + * ledger sequence number + * + * @param curr Current working ledger + * @param minValidSeq Minimum allowed sequence number + * + * @return ID Of the preferred ledger, or curr if the preferred ledger + * is not valid + */ ID getPreferred(Ledger const& curr, Seq minValidSeq) { @@ -877,22 +913,23 @@ public: return curr.id(); } - /** Determine the preferred last closed ledger for the next consensus round. - - Called before starting the next round of ledger consensus to determine - the preferred working ledger. Uses the dominant peerCount ledger if no - trusted validations are available. - - @param lcl Last closed ledger by this node - @param minSeq Minimum allowed sequence number of the trusted preferred - ledger - @param peerCounts Map from ledger ids to count of peers with that as the - last closed ledger - @return The preferred last closed ledger ID - - @note The minSeq does not apply to the peerCounts, since this function - does not know their sequence number - */ + /** + * Determine the preferred last closed ledger for the next consensus round. + * + * Called before starting the next round of ledger consensus to determine + * the preferred working ledger. Uses the dominant peerCount ledger if no + * trusted validations are available. + * + * @param lcl Last closed ledger by this node + * @param minSeq Minimum allowed sequence number of the trusted preferred + * ledger + * @param peerCounts Map from ledger ids to count of peers with that as the + * last closed ledger + * @return The preferred last closed ledger ID + * + * @note The minSeq does not apply to the peerCounts, since this function + * does not know their sequence number + */ ID getPreferredLCL(Ledger const& lcl, Seq minSeq, hash_map const& peerCounts) { @@ -915,17 +952,18 @@ public: return lcl.id(); } - /** Count the number of current trusted validators working on a ledger - after the specified one. - - @param ledger The working ledger - @param ledgerID The preferred ledger - @return The number of current trusted validators working on a descendant - of the preferred ledger - - @note If ledger.id() != ledgerID, only counts immediate child ledgers of - ledgerID - */ + /** + * Count the number of current trusted validators working on a ledger + * after the specified one. + * + * @param ledger The working ledger + * @param ledgerID The preferred ledger + * @return The number of current trusted validators working on a descendant + * of the preferred ledger + * + * @note If ledger.id() != ledgerID, only counts immediate child ledgers of + * ledgerID + */ std::size_t getNodesAfter(Ledger const& ledger, ID const& ledgerID) { @@ -946,10 +984,11 @@ public: }); } - /** Get the currently trusted full validations - - @return Vector of validations from currently trusted validators - */ + /** + * Get the currently trusted full validations + * + * @return Vector of validations from currently trusted validators + */ std::vector currentTrusted() { @@ -965,10 +1004,11 @@ public: return ret; } - /** Get the set of node ids associated with current validations - - @return The set of node ids for active, listed validators - */ + /** + * Get the set of node ids associated with current validations + * + * @return The set of node ids for active, listed validators + */ auto getCurrentNodeIDs() -> hash_set { @@ -982,11 +1022,12 @@ public: return ret; } - /** Count the number of trusted full validations for the given ledger - - @param ledgerID The identifier of ledger of interest - @return The number of trusted validations - */ + /** + * Count the number of trusted full validations for the given ledger + * + * @param ledgerID The identifier of ledger of interest + * @return The number of trusted validations + */ std::size_t numTrustedForLedger(ID const& ledgerID) { @@ -1003,12 +1044,13 @@ public: return count; } - /** Get trusted full validations for a specific ledger - - @param ledgerID The identifier of ledger of interest - @param seq The sequence number of ledger of interest - @return Trusted validations associated with ledger - */ + /** + * Get trusted full validations for a specific ledger + * + * @param ledgerID The identifier of ledger of interest + * @param seq The sequence number of ledger of interest + * @return Trusted validations associated with ledger + */ std::vector getTrustedForLedger(ID const& ledgerID, Seq const& seq) { @@ -1026,12 +1068,13 @@ public: return res; } - /** Returns fees reported by trusted full validators in the given ledger - - @param ledgerID The identifier of ledger of interest - @param baseFee The fee to report if not present in the validation - @return Vector of fees - */ + /** + * Returns fees reported by trusted full validators in the given ledger + * + * @param ledgerID The identifier of ledger of interest + * @param baseFee The fee to report if not present in the validation + * @return Vector of fees + */ std::vector fees(ID const& ledgerID, std::uint32_t baseFee) { @@ -1058,7 +1101,8 @@ public: return res; } - /** Flush all current validations + /** + * Flush all current validations */ void flush() @@ -1067,7 +1111,8 @@ public: current_.clear(); } - /** Return quantity of lagging proposers, and remove online proposers + /** + * Return quantity of lagging proposers, and remove online proposers * for purposes of evaluating whether to pause. * * Laggards are the trusted proposers whose sequence number is lower diff --git a/src/xrpld/core/Config.h b/src/xrpld/core/Config.h index 285ea7b9ac..852e46218a 100644 --- a/src/xrpld/core/Config.h +++ b/src/xrpld/core/Config.h @@ -44,26 +44,35 @@ enum class SizedItem : std::size_t { AccountIdCacheSize, }; -/** Fee schedule for startup / standalone, and to vote for. -During voting ledgers, the FeeVote logic will try to move towards -these values when injecting fee-setting transactions. -A default-constructed Setup contains recommended values. -*/ +/** + * Fee schedule for startup / standalone, and to vote for. + * During voting ledgers, the FeeVote logic will try to move towards + * these values when injecting fee-setting transactions. + * A default-constructed Setup contains recommended values. + */ struct FeeSetup { - /** The cost of a reference transaction in drops. */ + /** + * The cost of a reference transaction in drops. + */ XRPAmount referenceFee{10}; - /** The account reserve requirement in drops. */ + /** + * The account reserve requirement in drops. + */ XRPAmount accountReserve{10 * kDropsPerXrp}; - /** The per-owned item reserve requirement in drops. */ + /** + * The per-owned item reserve requirement in drops. + */ XRPAmount ownerReserve{2 * kDropsPerXrp}; /* (Remember to update the example cfg files when changing any of these * values.) */ - /** Convert to a Fees object for use with Ledger construction. */ + /** + * Convert to a Fees object for use with Ledger construction. + */ [[nodiscard]] Fees toFees() const { @@ -85,7 +94,9 @@ public: static char const* const kDatabaseDirName; static char const* const kValidatorsFileName; - /** Returns the full path and filename of the debug log file. */ + /** + * Returns the full path and filename of the debug log file. + */ [[nodiscard]] boost::filesystem::path getDebugLogFile() const; @@ -104,25 +115,27 @@ private: bool quiet_ = false; // Minimize logging verbosity. bool silent_ = false; // No output to console after startup. - /** Operate in stand-alone mode. - - In stand alone mode: - - - Peer connections are not attempted or accepted - - The ledger is not advanced automatically. - - If no ledger is loaded, the default ledger with the root - account is created. - */ + /** + * Operate in stand-alone mode. + * + * In stand alone mode: + * + * - Peer connections are not attempted or accepted + * - The ledger is not advanced automatically. + * - If no ledger is loaded, the default ledger with the root + * account is created. + */ bool runStandalone_ = false; bool useTxTables_ = true; - /** Determines if the server will sign a tx, given an account's secret seed. - - In the past, this was allowed, but this functionality can have security - implications. The new default is to not allow this functionality, but - a config option is included to enable this. - */ + /** + * Determines if the server will sign a tx, given an account's secret seed. + * + * In the past, this was allowed, but this functionality can have security + * implications. The new default is to not allow this functionality, but + * a config option is included to enable this. + */ bool signingEnabled_ = false; // The amount of RAM, in bytes, that we detected on this system. @@ -236,12 +249,16 @@ public: // Enable base squelching of duplicate validation/proposal messages bool vpReduceRelayBaseSquelchEnable = false; - ///////////////////// !!TEMPORARY CODE BLOCK!! //////////////////////// + /** + * ////////////////// !!TEMPORARY CODE BLOCK!! //////////////////////// + */ // Temporary squelching config for the peers selected as a source of // // validator messages. The config must be removed once squelching is // // made the default routing algorithm // std::size_t vpReduceRelaySquelchMaxSelectedPeers = 5; - ///////////////// END OF TEMPORARY CODE BLOCK ///////////////////// + /** + * ////////////// END OF TEMPORARY CODE BLOCK ///////////////////// + */ // Transaction reduce-relay feature bool txReduceRelayEnable = false; @@ -299,9 +316,9 @@ public: setupControl(bool bQuiet, bool bSilent, bool bStandalone); /** - * Load the config from the contents of the string. + * Load the config from the contents of the string. * - * @param fileContents String representing the config contents. + * @param fileContents String representing the config contents. */ void loadFromString(std::string const& fileContents); @@ -334,23 +351,24 @@ public: return signingEnabled_; } - /** Retrieve the default value for the item at the specified node size - - @param item The item for which the default value is needed - @param node Optional value, used to adjust the result to match the - size of a node (0: tiny, ..., 4: huge). If unseated, - uses the configured size (NODE_SIZE). - - @throw This method can throw std::out_of_range if you ask for values - that it does not recognize or request a non-default node-size. - - @return The value for the requested item. - - @note The defaults are selected so as to be reasonable, but the node - size is an imprecise metric that combines multiple aspects of - the underlying system; this means that we can't provide optimal - defaults in the code for every case. - */ + /** + * Retrieve the default value for the item at the specified node size + * + * @param item The item for which the default value is needed + * @param node Optional value, used to adjust the result to match the + * size of a node (0: tiny, ..., 4: huge). If unseated, + * uses the configured size (NODE_SIZE). + * + * @throws This method can throw std::out_of_range if you ask for values + * that it does not recognize or request a non-default node-size. + * + * @return The value for the requested item. + * + * @note The defaults are selected so as to be reasonable, but the node + * size is an imprecise metric that combines multiple aspects of + * the underlying system; this means that we can't provide optimal + * defaults in the code for every case. + */ [[nodiscard]] int getValueFor(SizedItem item, std::optional node = std::nullopt) const; diff --git a/src/xrpld/core/NetworkIDServiceImpl.h b/src/xrpld/core/NetworkIDServiceImpl.h index 2236a854ff..7977566ef8 100644 --- a/src/xrpld/core/NetworkIDServiceImpl.h +++ b/src/xrpld/core/NetworkIDServiceImpl.h @@ -9,12 +9,13 @@ namespace xrpl { // Forward declaration class Config; -/** Implementation of NetworkIDService that reads from Config. - - This class provides a NetworkIDService interface that wraps - the network ID from the application Config. It caches the - network ID at construction time. -*/ +/** + * Implementation of NetworkIDService that reads from Config. + * + * This class provides a NetworkIDService interface that wraps + * the network ID from the application Config. It caches the + * network ID at construction time. + */ class NetworkIDServiceImpl final : public NetworkIDService { public: diff --git a/src/xrpld/core/TimeKeeper.h b/src/xrpld/core/TimeKeeper.h index 9e067759ec..8ee9d17a06 100644 --- a/src/xrpld/core/TimeKeeper.h +++ b/src/xrpld/core/TimeKeeper.h @@ -8,7 +8,9 @@ namespace xrpl { -/** Manages various times used by the server. */ +/** + * Manages various times used by the server. + */ class TimeKeeper : public beast::AbstractClock { private: @@ -25,34 +27,36 @@ private: public: ~TimeKeeper() override = default; - /** Returns the current time, using the server's clock. - - It's possible for servers to have a different value for network - time, especially if they do not use some external mechanism for - time synchronization (e.g. NTP or SNTP). This is fine. - - This estimate is not directly visible to other servers over the - protocol, but it is possible for them to make an educated guess - if this server publishes proposals or validations. - - @note The network time is adjusted for the "XRPL epoch" which - was arbitrarily defined as 2000-01-01T00:00:00Z by Arthur - Britto and David Schwartz during early development of the - code. No rationale has been provided for this curious and - annoying, but otherwise unimportant, choice. - */ + /** + * Returns the current time, using the server's clock. + * + * It's possible for servers to have a different value for network + * time, especially if they do not use some external mechanism for + * time synchronization (e.g. NTP or SNTP). This is fine. + * + * This estimate is not directly visible to other servers over the + * protocol, but it is possible for them to make an educated guess + * if this server publishes proposals or validations. + * + * @note The network time is adjusted for the "XRPL epoch" which + * was arbitrarily defined as 2000-01-01T00:00:00Z by Arthur + * Britto and David Schwartz during early development of the + * code. No rationale has been provided for this curious and + * annoying, but otherwise unimportant, choice. + */ [[nodiscard]] time_point now() const override { return adjust(std::chrono::system_clock::now()); } - /** Returns the predicted close time, in network time. - - The predicted close time represents the notional "center" of the - network. Each server assumes that its clock is correct and tries - to pull the close time towards its measure of network time. - */ + /** + * Returns the predicted close time, in network time. + * + * The predicted close time represents the notional "center" of the + * network. Each server assumes that its clock is correct and tries + * to pull the close time towards its measure of network time. + */ [[nodiscard]] time_point closeTime() const { @@ -66,7 +70,9 @@ public: return closeOffset_.load(); } - /** Adjust the close time, based on the network's view of time. */ + /** + * Adjust the close time, based on the network's view of time. + */ std::chrono::seconds adjustCloseTime(std::chrono::seconds by) { diff --git a/src/xrpld/core/detail/Config.cpp b/src/xrpld/core/detail/Config.cpp index 0706163ab1..3b7b57328b 100644 --- a/src/xrpld/core/detail/Config.cpp +++ b/src/xrpld/core/detail/Config.cpp @@ -793,7 +793,9 @@ Config::loadFromString(std::string const& fileContents) { auto sec = section(Sections::kReduceRelay); - ///////////////////// !!TEMPORARY CODE BLOCK!! //////////////////////// + /** + * ////////////////// !!TEMPORARY CODE BLOCK!! //////////////////////// + */ // vp_enable config option is deprecated by vp_base_squelch_enable // // This option is kept for backwards compatibility. When squelching // // is the default algorithm, it must be replaced with: // @@ -821,9 +823,13 @@ Config::loadFromString(std::string const& fileContents) { vpReduceRelayBaseSquelchEnable = false; } - ///////////////// !!END OF TEMPORARY CODE BLOCK!! ///////////////////// + /** + * ////////////// !!END OF TEMPORARY CODE BLOCK!! ///////////////////// + */ - ///////////////////// !!TEMPORARY CODE BLOCK!! /////////////////////// + /** + * ////////////////// !!TEMPORARY CODE BLOCK!! /////////////////////// + */ // Temporary squelching config for the peers selected as a source of // // validator messages. The config must be removed once squelching is // // made the default routing algorithm. // @@ -835,7 +841,9 @@ Config::loadFromString(std::string const& fileContents) " vp_base_squelch_max_selected_peers must be " "greater than or equal to 3"); } - ///////////////// !!END OF TEMPORARY CODE BLOCK!! ///////////////////// + /** + * ////////////// !!END OF TEMPORARY CODE BLOCK!! ///////////////////// + */ txReduceRelayEnable = sec.valueOr(Keys::kTxEnable, false); txReduceRelayMetrics = sec.valueOr(Keys::kTxMetrics, false); diff --git a/src/xrpld/overlay/Cluster.h b/src/xrpld/overlay/Cluster.h index b3864e0fc6..703e1601aa 100644 --- a/src/xrpld/overlay/Cluster.h +++ b/src/xrpld/overlay/Cluster.h @@ -53,23 +53,27 @@ private: public: Cluster(beast::Journal j); - /** Determines whether a node belongs in the cluster - @return std::nullopt if the node isn't a member, - otherwise, the comment associated with the - node (which may be an empty string). - */ + /** + * Determines whether a node belongs in the cluster + * @return std::nullopt if the node isn't a member, + * otherwise, the comment associated with the + * node (which may be an empty string). + */ std::optional member(PublicKey const& node) const; - /** The number of nodes in the cluster list. */ + /** + * The number of nodes in the cluster list. + */ std::size_t size() const; - /** Store information about the state of a cluster node. - @param identity The node's public identity - @param name The node's name (may be empty) - @return true if we updated our information - */ + /** + * Store information about the state of a cluster node. + * @param identity The node's public identity + * @param name The node's name (may be empty) + * @return true if we updated our information + */ bool update( PublicKey const& identity, @@ -77,23 +81,25 @@ public: std::uint32_t loadFee = 0, NetClock::time_point reportTime = NetClock::time_point{}); - /** Invokes the callback once for every cluster node. - @note You are not allowed to call `update` from - within the callback. - */ + /** + * Invokes the callback once for every cluster node. + * @note You are not allowed to call `update` from + * within the callback. + */ void forEach(std::function func) const; - /** Load the list of cluster nodes. - - The section contains entries consisting of a base58 - encoded node public key, optionally followed by - a comment. - - @return false if an entry could not be parsed or - contained an invalid node public key, - true otherwise. - */ + /** + * Load the list of cluster nodes. + * + * The section contains entries consisting of a base58 + * encoded node public key, optionally followed by + * a comment. + * + * @return false if an entry could not be parsed or + * contained an invalid node public key, + * true otherwise. + */ bool load(Section const& nodes); }; diff --git a/src/xrpld/overlay/Compression.h b/src/xrpld/overlay/Compression.h index 4b7493e7e6..8d4a1d56c8 100644 --- a/src/xrpld/overlay/Compression.h +++ b/src/xrpld/overlay/Compression.h @@ -18,7 +18,8 @@ enum class Algorithm : std::uint8_t { None = 0x00, LZ4 = 0x90 }; enum class Compressed : std::uint8_t { On, Off }; -/** Decompress input stream. +/** + * Decompress input stream. * @tparam InputStream ZeroCopyInputStream * @param in Input source stream * @param inSize Size of compressed data @@ -57,7 +58,8 @@ decompress( return 0; } -/** Compress input data. +/** + * Compress input data. * @tparam BufferFactory Callable object or lambda. * Takes the requested buffer size and returns allocated buffer pointer. * @param in Data to compress diff --git a/src/xrpld/overlay/Message.h b/src/xrpld/overlay/Message.h index 63aa360f92..2e187a2a4d 100644 --- a/src/xrpld/overlay/Message.h +++ b/src/xrpld/overlay/Message.h @@ -39,7 +39,8 @@ class Message : public std::enable_shared_from_this using Algorithm = compression::Algorithm; public: - /** Constructor + /** + * Constructor * @param message Protocol message to serialize * @param type Protocol message type * @param validator Public Key of the source validator for Validation or @@ -50,7 +51,9 @@ public: protocol::MessageType type, std::optional const& validator = {}); - /** Retrieve the size of the packed but uncompressed message data. */ + /** + * Retrieve the size of the packed but uncompressed message data. + */ std::size_t getBufferSize(); @@ -60,7 +63,8 @@ public: static std::size_t totalSize(::google::protobuf::Message const& message); - /** Retrieve the packed message data. If compressed message is requested but + /** + * Retrieve the packed message data. If compressed message is requested but * the message is not compressible then the uncompressed buffer is returned. * @param compressed Request compressed (Compress::On) or * uncompressed (Compress::Off) payload buffer @@ -69,14 +73,18 @@ public: std::vector const& getBuffer(Compressed tryCompressed); - /** Get the traffic category */ + /** + * Get the traffic category + */ std::size_t getCategory() const { return category_; } - /** Get the validator's key */ + /** + * Get the validator's key + */ std::optional const& getValidatorKey() const { @@ -90,7 +98,8 @@ private: std::once_flag onceFlag_; std::optional validatorKey_; - /** Set the payload header + /** + * Set the payload header * @param in Pointer to the payload * @param payloadBytes Size of the payload excluding the header size * @param type Protocol message type @@ -106,14 +115,16 @@ private: Algorithm compression, std::uint32_t uncompressedBytes); - /** Try to compress the payload. + /** + * Try to compress the payload. * Can be called concurrently by multiple peers but is compressed once. * If the message is not compressible then the serialized buffer_ is used. */ void compress(); - /** Get the message type from the payload header. + /** + * Get the message type from the payload header. * First four bytes are the compression/algorithm flag and the payload size. * Next two bytes are the message type * @param in Payload header pointer diff --git a/src/xrpld/overlay/Overlay.h b/src/xrpld/overlay/Overlay.h index cc5be791a7..6cc229f5a0 100644 --- a/src/xrpld/overlay/Overlay.h +++ b/src/xrpld/overlay/Overlay.h @@ -31,7 +31,9 @@ class context; namespace xrpl { -/** Manages the set of connected peers. */ +/** + * Manages the set of connected peers. + */ class Overlay : public beast::PropertyStream::Source { protected: @@ -75,66 +77,84 @@ public: { } - /** Conditionally accept an incoming HTTP request. */ + /** + * Conditionally accept an incoming HTTP request. + */ virtual Handoff onHandoff( std::unique_ptr&& bundle, http_request_type&& request, boost::asio::ip::tcp::endpoint remoteAddress) = 0; - /** Establish a peer connection to the specified endpoint. - The call returns immediately, the connection attempt is - performed asynchronously. - */ + /** + * Establish a peer connection to the specified endpoint. + * The call returns immediately, the connection attempt is + * performed asynchronously. + */ virtual void connect(beast::IP::Endpoint const& address) = 0; - /** Returns the maximum number of peers we are configured to allow. */ + /** + * Returns the maximum number of peers we are configured to allow. + */ virtual int limit() = 0; - /** Returns the number of active peers. - Active peers are only those peers that have completed the - handshake and are using the peer protocol. - */ + /** + * Returns the number of active peers. + * Active peers are only those peers that have completed the + * handshake and are using the peer protocol. + */ [[nodiscard]] virtual std::size_t size() const = 0; - /** Return diagnostics on the status of all peers. - @deprecated This is superseded by PropertyStream - */ + /** + * Return diagnostics on the status of all peers. + * @deprecated This is superseded by PropertyStream + */ virtual json::Value json() = 0; - /** Returns a sequence representing the current list of peers. - The snapshot is made at the time of the call. - */ + /** + * Returns a sequence representing the current list of peers. + * The snapshot is made at the time of the call. + */ [[nodiscard]] virtual PeerSequence getActivePeers() const = 0; - /** Calls the checkTracking function on each peer - @param index the value to pass to the peer's checkTracking function - */ + /** + * Calls the checkTracking function on each peer + * @param index the value to pass to the peer's checkTracking function + */ virtual void checkTracking(std::uint32_t index) = 0; - /** Returns the peer with the matching short id, or null. */ + /** + * Returns the peer with the matching short id, or null. + */ [[nodiscard]] virtual std::shared_ptr findPeerByShortID(Peer::id_t const& id) const = 0; - /** Returns the peer with the matching public key, or null. */ + /** + * Returns the peer with the matching public key, or null. + */ virtual std::shared_ptr findPeerByPublicKey(PublicKey const& pubKey) = 0; - /** Broadcast a proposal. */ + /** + * Broadcast a proposal. + */ virtual void broadcast(protocol::TMProposeSet const& m) = 0; - /** Broadcast a validation. */ + /** + * Broadcast a validation. + */ virtual void broadcast(protocol::TMValidation const& m) = 0; - /** Relay a proposal. + /** + * Relay a proposal. * @param m the serialized proposal * @param uid the id used to identify this proposal * @param validator The pubkey of the validator that issued this proposal @@ -143,7 +163,8 @@ public: virtual std::set relay(protocol::TMProposeSet const& m, uint256 const& uid, PublicKey const& validator) = 0; - /** Relay a validation. + /** + * Relay a validation. * @param m the serialized validation * @param uid the id used to identify this validation * @param validator The pubkey of the validator that issued this validation @@ -152,7 +173,8 @@ public: virtual std::set relay(protocol::TMValidation const& m, uint256 const& uid, PublicKey const& validator) = 0; - /** Relay a transaction. If the tx reduce-relay feature is enabled then + /** + * Relay a transaction. If the tx reduce-relay feature is enabled then * randomly select peers to relay to and queue transaction's hash * for the rest of the peers. * @param hash transaction's hash @@ -165,7 +187,8 @@ public: std::optional> m, std::set const& toSkip) = 0; - /** Visit every active peer. + /** + * Visit every active peer. * * The visitor must be invocable as: * Function(std::shared_ptr const& peer); @@ -180,13 +203,16 @@ public: f(p); } - /** Increment and retrieve counter for transaction job queue overflows. */ + /** + * Increment and retrieve counter for transaction job queue overflows. + */ virtual void incJqTransOverflow() = 0; [[nodiscard]] virtual std::uint64_t getJqTransOverflow() const = 0; - /** Increment and retrieve counters for total peer disconnects, and + /** + * Increment and retrieve counters for total peer disconnects, and * disconnects we initiate for excessive resource consumption. */ virtual void @@ -198,19 +224,21 @@ public: [[nodiscard]] virtual std::uint64_t getPeerDisconnectCharges() const = 0; - /** Returns the ID of the network this server is configured for, if any. - - The ID is just a numerical identifier, with the IDs 0, 1 and 2 used to - identify the mainnet, the testnet and the devnet respectively. - - @return The numerical identifier configured by the administrator of the - server. An unseated optional, otherwise. - */ + /** + * Returns the ID of the network this server is configured for, if any. + * + * The ID is just a numerical identifier, with the IDs 0, 1 and 2 used to + * identify the mainnet, the testnet and the devnet respectively. + * + * @return The numerical identifier configured by the administrator of the + * server. An unseated optional, otherwise. + */ [[nodiscard]] virtual std::optional networkID() const = 0; - /** Returns tx reduce-relay metrics - @return json value of tx reduce-relay metrics + /** + * Returns tx reduce-relay metrics + * @return json value of tx reduce-relay metrics */ [[nodiscard]] virtual json::Value txMetrics() const = 0; diff --git a/src/xrpld/overlay/Peer.h b/src/xrpld/overlay/Peer.h index 29778b42a6..23a45dc512 100644 --- a/src/xrpld/overlay/Peer.h +++ b/src/xrpld/overlay/Peer.h @@ -25,17 +25,20 @@ enum class ProtocolFeature { LedgerReplay, }; -/** Represents a peer connection in the overlay. */ +/** + * Represents a peer connection in the overlay. + */ class Peer { public: using ptr = std::shared_ptr; - /** Uniquely identifies a peer. - This can be stored in tables to find the peer later. Callers - can discover if the peer is no longer connected and make - adjustments as needed. - */ + /** + * Uniquely identifies a peer. + * This can be stored in tables to find the peer later. Callers + * can discover if the peer is no longer connected and make + * adjustments as needed. + */ using id_t = std::uint32_t; virtual ~Peer() = default; @@ -50,19 +53,27 @@ public: [[nodiscard]] virtual beast::IP::Endpoint getRemoteAddress() const = 0; - /** Send aggregated transactions' hashes. */ + /** + * Send aggregated transactions' hashes. + */ virtual void sendTxQueue() = 0; - /** Aggregate transaction's hash. */ + /** + * Aggregate transaction's hash. + */ virtual void addTxQueue(uint256 const&) = 0; - /** Remove hash from the transactions' hashes queue. */ + /** + * Remove hash from the transactions' hashes queue. + */ virtual void removeTxQueue(uint256 const&) = 0; - /** Adjust this peer's load balance based on the type of load imposed. */ + /** + * Adjust this peer's load balance based on the type of load imposed. + */ virtual void charge(Resource::Charge const& fee, std::string const& context) = 0; @@ -73,7 +84,9 @@ public: [[nodiscard]] virtual id_t id() const = 0; - /** Returns `true` if this connection is a member of the cluster. */ + /** + * Returns `true` if this connection is a member of the cluster. + */ [[nodiscard]] virtual bool cluster() const = 0; diff --git a/src/xrpld/overlay/PeerSet.h b/src/xrpld/overlay/PeerSet.h index 4670ec9783..ffba7932de 100644 --- a/src/xrpld/overlay/PeerSet.h +++ b/src/xrpld/overlay/PeerSet.h @@ -15,16 +15,17 @@ namespace xrpl { -/** Supports data retrieval by managing a set of peers. - - When desired data (such as a ledger or a transaction set) - is missing locally it can be obtained by querying connected - peers. This class manages common aspects of the retrieval. - Callers maintain the set by adding and removing peers depending - on whether the peers have useful information. - - The data is represented by its hash. -*/ +/** + * Supports data retrieval by managing a set of peers. + * + * When desired data (such as a ledger or a transaction set) + * is missing locally it can be obtained by querying connected + * peers. This class manages common aspects of the retrieval. + * Callers maintain the set by adding and removing peers depending + * on whether the peers have useful information. + * + * The data is represented by its hash. + */ class PeerSet { public: @@ -42,7 +43,9 @@ public: std::function const&)> hasItem, std::function const&)> onPeerAdded) = 0; - /** send a message */ + /** + * send a message + */ template void sendRequest(MessageType const& message, std::shared_ptr const& peer) @@ -56,7 +59,9 @@ public: protocol::MessageType type, std::shared_ptr const& peer) = 0; - /** get the set of ids of previously added peers */ + /** + * get the set of ids of previously added peers + */ [[nodiscard]] virtual std::set const& getPeerIds() const = 0; }; diff --git a/src/xrpld/overlay/Slot.h b/src/xrpld/overlay/Slot.h index a29020e03a..4b5026414b 100644 --- a/src/xrpld/overlay/Slot.h +++ b/src/xrpld/overlay/Slot.h @@ -38,13 +38,17 @@ namespace xrpl::reduce_relay { template class Slots; -/** Peer's State */ +/** + * Peer's State + */ enum class PeerState : uint8_t { Counting, // counting messages Selected, // selected to relay, counting if Slot in Counting Squelched, // squelched, doesn't relay }; -/** Slot's State */ +/** + * Slot's State + */ enum class SlotState : uint8_t { Counting, // counting messages Selected, // peers selected, stop counting @@ -57,22 +61,26 @@ epoch(TP const& t) return std::chrono::duration_cast(t.time_since_epoch()); } -/** Abstract class. Declares squelch and unsquelch handlers. +/** + * Abstract class. Declares squelch and unsquelch handlers. * OverlayImpl inherits from this class. Motivation is * for easier unit tests to facilitate on the fly - * changing callbacks. */ + * changing callbacks. + */ class SquelchHandler { public: virtual ~SquelchHandler() = default; - /** Squelch handler + /** + * Squelch handler * @param validator Public key of the source validator * @param id Peer's id to squelch * @param duration Squelch duration in seconds */ virtual void squelch(PublicKey const& validator, Peer::id_t id, std::uint32_t duration) const = 0; - /** Unsquelch handler + /** + * Unsquelch handler * @param validator Public key of the source validator * @param id Peer's id to unsquelch */ @@ -101,9 +109,10 @@ private: // a callback to report ignored squelches using ignored_squelch_callback = std::function; - /** Constructor - * @param journal Journal for logging + /** + * Constructor * @param handler Squelch/Unsquelch implementation + * @param journal Journal for logging * @param maxSelectedPeers the maximum number of peers to be selected as * validator message source */ @@ -115,7 +124,8 @@ private: { } - /** Update peer info. If the message is from a new + /** + * Update peer info. If the message is from a new * peer or from a previously expired squelched peer then switch * the peer's and slot's state to Counting. If time of last * selection round is > 2 * kMaxUnsquelchExpireDefault then switch the @@ -141,7 +151,8 @@ private: protocol::MessageType type, ignored_squelch_callback callback); - /** Handle peer deletion when a peer disconnects. + /** + * Handle peer deletion when a peer disconnects. * If the peer is in Selected state then * call unsquelch handler for every peer in squelched state and reset * every peer's state to Counting. Switch Slot's state to Counting. @@ -154,39 +165,51 @@ private: void deletePeer(PublicKey const& validator, id_t id, bool erase); - /** Get the time of the last peer selection round */ + /** + * Get the time of the last peer selection round + */ [[nodiscard]] time_point const& getLastSelected() const { return lastSelected_; } - /** Return number of peers in state */ + /** + * Return number of peers in state + */ [[nodiscard]] std::uint16_t inState(PeerState state) const; - /** Return number of peers not in state */ + /** + * Return number of peers not in state + */ [[nodiscard]] std::uint16_t notInState(PeerState state) const; - /** Return Slot's state */ + /** + * Return Slot's state + */ [[nodiscard]] SlotState getState() const { return state_; } - /** Return selected peers */ + /** + * Return selected peers + */ [[nodiscard]] std::set getSelected() const; - /** Get peers info. Return map of peer's state, count, squelch + /** + * Get peers info. Return map of peer's state, count, squelch * expiration milsec, and last message time milsec. */ [[nodiscard]] std::unordered_map> getPeers() const; - /** Check if peers stopped relaying messages. If a peer is + /** + * Check if peers stopped relaying messages. If a peer is * selected peer then call unsquelch handler for all * currently squelched peers and switch the slot to * Counting state. @@ -195,7 +218,8 @@ private: void deleteIdlePeer(PublicKey const& validator); - /** Get random squelch duration between kMinUnsquelchExpire and + /** + * Get random squelch duration between kMinUnsquelchExpire and * min(max(kMaxUnsquelchExpireDefault, kSquelchPerPeer * npeers), * kMaxUnsquelchExpirePeers) * @param npeers number of peers that can be squelched in the Slot @@ -204,15 +228,21 @@ private: getSquelchDuration(std::size_t npeers); private: - /** Reset counts of peers in Selected or Counting state */ + /** + * Reset counts of peers in Selected or Counting state + */ void resetCounts(); - /** Initialize slot to Counting state */ + /** + * Initialize slot to Counting state + */ void initCounting(); - /** Data maintained for each peer */ + /** + * Data maintained for each peer + */ struct PeerInfo { PeerState state; // peer's state @@ -533,7 +563,8 @@ Slot::getPeers() const return r; } -/** Slots is a container for validator's Slot and handles Slot update +/** + * Slots is a container for validator's Slot and handles Slot update * when a message is received from a validator. It also handles Slot aging * and checks for peers which are disconnected or stopped relaying the messages. */ @@ -564,14 +595,18 @@ public: } ~Slots() = default; - /** Check if base squelching feature is enabled and ready */ + /** + * Check if base squelching feature is enabled and ready + */ bool baseSquelchReady() { return baseSquelchEnabled_ && reduceRelayReady(); } - /** Check if reduce_relay::kWaitOnBootup time passed since startup */ + /** + * Check if reduce_relay::kWaitOnBootup time passed since startup + */ bool reduceRelayReady() { @@ -584,7 +619,8 @@ public: return reduceRelayReady_; } - /** Calls Slot::update of Slot associated with the validator, with a noop + /** + * Calls Slot::update of Slot associated with the validator, with a noop * callback. * @param key Message's hash * @param validator Validator's public key @@ -601,7 +637,8 @@ public: updateSlotAndSquelch(key, validator, id, type, []() {}); } - /** Calls Slot::update of Slot associated with the validator. + /** + * Calls Slot::update of Slot associated with the validator. * @param key Message's hash * @param validator Validator's public key * @param id Peer's id which received the message @@ -616,13 +653,16 @@ public: protocol::MessageType type, Slot::ignored_squelch_callback callback); - /** Check if peers stopped relaying messages + /** + * Check if peers stopped relaying messages * and if slots stopped receiving messages from the validator. */ void deleteIdlePeers(); - /** Return number of peers in state */ + /** + * Return number of peers in state + */ [[nodiscard]] std::optional inState(PublicKey const& validator, PeerState state) const { @@ -632,7 +672,9 @@ public: return {}; } - /** Return number of peers not in state */ + /** + * Return number of peers not in state + */ [[nodiscard]] std::optional notInState(PublicKey const& validator, PeerState state) const { @@ -642,7 +684,9 @@ public: return {}; } - /** Return true if Slot is in state */ + /** + * Return true if Slot is in state + */ [[nodiscard]] bool inState(PublicKey const& validator, SlotState state) const { @@ -652,7 +696,9 @@ public: return false; } - /** Get selected peers */ + /** + * Get selected peers + */ std::set getSelected(PublicKey const& validator) { @@ -662,7 +708,8 @@ public: return {}; } - /** Get peers info. Return map of peer's state, count, and squelch + /** + * Get peers info. Return map of peer's state, count, and squelch * expiration milliseconds. */ std::unordered_map> @@ -674,7 +721,9 @@ public: return {}; } - /** Get Slot's state */ + /** + * Get Slot's state + */ std::optional getState(PublicKey const& validator) { @@ -684,7 +733,8 @@ public: return {}; } - /** Called when a peer is deleted. If the peer was selected to be the + /** + * Called when a peer is deleted. If the peer was selected to be the * source of messages from the validator then squelched peers have to be * unsquelched. * @param id Peer's id @@ -694,9 +744,11 @@ public: deletePeer(id_t id, bool erase); private: - /** Add message/peer if have not seen this message + /** + * Add message/peer if have not seen this message * from the peer. A message is aged after IDLED seconds. - * Return true if added */ + * Return true if added + */ bool addPeerMessage(uint256 const& key, id_t id); diff --git a/src/xrpld/overlay/Squelch.h b/src/xrpld/overlay/Squelch.h index 0485c91c81..6899bf39f0 100644 --- a/src/xrpld/overlay/Squelch.h +++ b/src/xrpld/overlay/Squelch.h @@ -11,7 +11,9 @@ namespace xrpl::reduce_relay { -/** Maintains squelching of relaying messages from validators */ +/** + * Maintains squelching of relaying messages from validators + */ template class Squelch { @@ -23,7 +25,8 @@ public: } virtual ~Squelch() = default; - /** Squelch validation/proposal relaying for the validator + /** + * Squelch validation/proposal relaying for the validator * @param validator The validator's public key * @param squelchDuration Squelch duration in seconds * @return false if invalid squelch duration @@ -31,13 +34,15 @@ public: bool addSquelch(PublicKey const& validator, std::chrono::seconds const& squelchDuration); - /** Remove the squelch + /** + * Remove the squelch * @param validator The validator's public key */ void removeSquelch(PublicKey const& validator); - /** Remove expired squelch + /** + * Remove expired squelch * @param validator Validator's public key * @return true if removed or doesn't exist, false if still active */ @@ -45,8 +50,10 @@ public: expireSquelch(PublicKey const& validator); private: - /** Maintains the list of squelched relaying to downstream peers. - * Expiration time is included in the TMSquelch message. */ + /** + * Maintains the list of squelched relaying to downstream peers. + * Expiration time is included in the TMSquelch message. + */ hash_map squelched_; beast::Journal const journal_; }; diff --git a/src/xrpld/overlay/detail/ConnectAttempt.h b/src/xrpld/overlay/detail/ConnectAttempt.h index bed3f672be..d7836e3c84 100644 --- a/src/xrpld/overlay/detail/ConnectAttempt.h +++ b/src/xrpld/overlay/detail/ConnectAttempt.h @@ -19,7 +19,9 @@ namespace xrpl { -/** Manages an outbound connection attempt. */ +/** + * Manages an outbound connection attempt. + */ class ConnectAttempt : public OverlayImpl::Child, public std::enable_shared_from_this { diff --git a/src/xrpld/overlay/detail/Handshake.cpp b/src/xrpld/overlay/detail/Handshake.cpp index 39fd93f1d4..a860d2d604 100644 --- a/src/xrpld/overlay/detail/Handshake.cpp +++ b/src/xrpld/overlay/detail/Handshake.cpp @@ -118,20 +118,21 @@ makeFeaturesResponseHeader( return str.str(); } -/** Hashes the latest finished message from an SSL stream. - - @param ssl the session to get the message from. - @param get a pointer to the function to call to retrieve the finished - message. This can be either: - - `SSL_get_finished` or - - `SSL_get_peer_finished`. - @return `true` if successful, `false` otherwise. - - @note This construct is non-standard. There are potential "standard" - alternatives that should be considered. For a discussion, on - this topic, see https://github.com/openssl/openssl/issues/5509 and - https://github.com/XRPLF/rippled/issues/2413. -*/ +/** + * Hashes the latest finished message from an SSL stream. + * + * @param ssl the session to get the message from. + * @param get a pointer to the function to call to retrieve the finished + * message. This can be either: + * - `SSL_get_finished` or + * - `SSL_get_peer_finished`. + * @return `true` if successful, `false` otherwise. + * + * @note This construct is non-standard. There are potential "standard" + * alternatives that should be considered. For a discussion, on + * this topic, see https://github.com/openssl/openssl/issues/5509 and + * https://github.com/XRPLF/rippled/issues/2413. + */ static std::optional> hashLastMessage(SSL const* ssl, size_t (*get)(const SSL*, void*, size_t)) { diff --git a/src/xrpld/overlay/detail/Handshake.h b/src/xrpld/overlay/detail/Handshake.h index 6dcc06bbf1..9a4e5ba507 100644 --- a/src/xrpld/overlay/detail/Handshake.h +++ b/src/xrpld/overlay/detail/Handshake.h @@ -26,19 +26,21 @@ using request_type = boost::beast::http::request using http_request_type = boost::beast::http::request; using http_response_type = boost::beast::http::response; -/** Computes a shared value based on the SSL connection state. - - When there is no man in the middle, both sides will compute the same - value. In the presence of an attacker, the computed values will be - different. - - @param ssl the SSL/TLS connection state. - @return A 256-bit value on success; an unseated optional otherwise. -*/ +/** + * Computes a shared value based on the SSL connection state. + * + * When there is no man in the middle, both sides will compute the same + * value. In the presence of an attacker, the computed values will be + * different. + * + * @param ssl the SSL/TLS connection state. + * @return A 256-bit value on success; an unseated optional otherwise. + */ std::optional makeSharedValue(stream_type& ssl, beast::Journal journal); -/** Insert fields headers necessary for upgrading the link to the peer protocol. +/** + * Insert fields headers necessary for upgrading the link to the peer protocol. */ void buildHandshake( @@ -49,17 +51,18 @@ buildHandshake( beast::IP::Address remoteIp, Application& app); -/** Validate header fields necessary for upgrading the link to the peer - protocol. - - This performs critical security checks that ensure that prevent - MITM attacks on our peer-to-peer links and that the remote peer - has the private keys that correspond to the public identity it - claims. - - @return The public key of the remote peer. - @throw A class derived from std::exception. -*/ +/** + * Validate header fields necessary for upgrading the link to the peer + * protocol. + * + * This performs critical security checks that ensure that prevent + * MITM attacks on our peer-to-peer links and that the remote peer + * has the private keys that correspond to the public identity it + * claims. + * + * @return The public key of the remote peer. + * @throws A class derived from std::exception. + */ PublicKey verifyHandshake( boost::beast::http::fields const& headers, @@ -69,16 +72,17 @@ verifyHandshake( beast::IP::Address remote, Application& app); -/** Make outbound http request - - @param crawlPublic if true then server's IP/Port are included in crawl - @param comprEnabled if true then compression feature is enabled - @param ledgerReplayEnabled if true then ledger-replay feature is enabled - @param txReduceRelayEnabled if true then transaction reduce-relay feature is - enabled - @param vpReduceRelayEnabled if true then validation/proposal reduce-relay - feature is enabled - @return http request with empty body +/** + * Make outbound http request + * + * @param crawlPublic if true then server's IP/Port are included in crawl + * @param comprEnabled if true then compression feature is enabled + * @param ledgerReplayEnabled if true then ledger-replay feature is enabled + * @param txReduceRelayEnabled if true then transaction reduce-relay feature is + * enabled + * @param vpReduceRelayEnabled if true then validation/proposal reduce-relay + * feature is enabled + * @return http request with empty body */ request_type makeRequest( @@ -88,17 +92,18 @@ makeRequest( bool txReduceRelayEnabled, bool vpReduceRelayEnabled); -/** Make http response - - @param crawlPublic if true then server's IP/Port are included in crawl - @param req incoming http request - @param publicIp server's public IP - @param remoteIp peer's IP - @param sharedValue shared value based on the SSL connection state - @param networkID specifies what network we intend to connect to - @param version supported protocol version - @param app Application's reference to access some common properties - @return http response +/** + * Make http response + * + * @param crawlPublic if true then server's IP/Port are included in crawl + * @param req incoming http request + * @param publicIp server's public IP + * @param remoteIp peer's IP + * @param sharedValue shared value based on the SSL connection state + * @param networkID specifies what network we intend to connect to + * @param version supported protocol version + * @param app Application's reference to access some common properties + * @return http response */ http_response_type makeResponse( @@ -127,22 +132,24 @@ static constexpr char kFeatureLedgerReplay[] = "ledgerreplay"; static constexpr char kDelimFeature[] = ";"; static constexpr char kDelimValue[] = ","; -/** Get feature's header value - @param headers request/response header - @param feature name - @return seated optional with feature's value if the feature - is found in the header, unseated optional otherwise +/** + * Get feature's header value + * @param headers request/response header + * @param feature name + * @return seated optional with feature's value if the feature + * is found in the header, unseated optional otherwise */ std::optional getFeatureValue(boost::beast::http::fields const& headers, std::string const& feature); -/** Check if a feature's value is equal to the specified value - @param headers request/response header - @param feature to check - @param value of the feature to check, must be a single value; i.e. not - value1,value2... - @return true if the feature's value matches the specified value, false if - doesn't match or the feature is not found in the header +/** + * Check if a feature's value is equal to the specified value + * @param headers request/response header + * @param feature to check + * @param value of the feature to check, must be a single value; i.e. not + * value1,value2... + * @return true if the feature's value matches the specified value, false if + * doesn't match or the feature is not found in the header */ bool isFeatureValue( @@ -150,23 +157,25 @@ isFeatureValue( std::string const& feature, std::string const& value); -/** Check if a feature is enabled - @param headers request/response header - @param feature to check - @return true if enabled +/** + * Check if a feature is enabled + * @param headers request/response header + * @param feature to check + * @return true if enabled */ bool featureEnabled(boost::beast::http::fields const& headers, std::string const& feature); -/** Check if a feature should be enabled for a peer. The feature - is enabled if its configured value is true and the http header - has the specified feature value. - @tparam headers request (inbound) or response (outbound) header - @param request http headers - @param feature to check - @param config feature's configuration value - @param value feature's value to check in the headers - @return true if the feature is enabled +/** + * Check if a feature should be enabled for a peer. The feature + * is enabled if its configured value is true and the http header + * has the specified feature value. + * @tparam Headers request (inbound) or response (outbound) header + * @param request http headers + * @param feature to check + * @param value feature's value to check in the headers + * @param config feature's configuration value + * @return true if the feature is enabled */ template bool @@ -179,7 +188,9 @@ peerFeatureEnabled( return config && isFeatureValue(request, feature, value); } -/** Wrapper for enable(1)/disable type(0) of feature */ +/** + * Wrapper for enable(1)/disable type(0) of feature + */ template bool peerFeatureEnabled(Headers const& request, std::string const& feature, bool config) @@ -187,14 +198,15 @@ peerFeatureEnabled(Headers const& request, std::string const& feature, bool conf return config && peerFeatureEnabled(request, feature, "1", config); } -/** Make request header X-Protocol-Ctl value with supported features - @param comprEnabled if true then compression feature is enabled - @param ledgerReplayEnabled if true then ledger-replay feature is enabled - @param txReduceRelayEnabled if true then transaction reduce-relay feature is - enabled - @param vpReduceRelayEnabled if true then validation/proposal reduce-relay - base squelch feature is enabled - @return X-Protocol-Ctl header value +/** + * Make request header X-Protocol-Ctl value with supported features + * @param comprEnabled if true then compression feature is enabled + * @param ledgerReplayEnabled if true then ledger-replay feature is enabled + * @param txReduceRelayEnabled if true then transaction reduce-relay feature is + * enabled + * @param vpReduceRelayEnabled if true then validation/proposal reduce-relay + * base squelch feature is enabled + * @return X-Protocol-Ctl header value */ std::string makeFeaturesRequestHeader( @@ -203,18 +215,19 @@ makeFeaturesRequestHeader( bool txReduceRelayEnabled, bool vpReduceRelayEnabled); -/** Make response header X-Protocol-Ctl value with supported features. - If the request has a feature that we support enabled - and the feature's configuration is enabled then enable this feature in - the response header. - @param header request's header - @param comprEnabled if true then compression feature is enabled - @param ledgerReplayEnabled if true then ledger-replay feature is enabled - @param txReduceRelayEnabled if true then transaction reduce-relay feature is - enabled - @param vpReduceRelayEnabled if true then validation/proposal reduce-relay - base squelch feature is enabled - @return X-Protocol-Ctl header value +/** + * Make response header X-Protocol-Ctl value with supported features. + * If the request has a feature that we support enabled + * and the feature's configuration is enabled then enable this feature in + * the response header. + * @param header request's header + * @param comprEnabled if true then compression feature is enabled + * @param ledgerReplayEnabled if true then ledger-replay feature is enabled + * @param txReduceRelayEnabled if true then transaction reduce-relay feature is + * enabled + * @param vpReduceRelayEnabled if true then validation/proposal reduce-relay + * base squelch feature is enabled + * @return X-Protocol-Ctl header value */ std::string makeFeaturesResponseHeader( diff --git a/src/xrpld/overlay/detail/Message.cpp b/src/xrpld/overlay/detail/Message.cpp index 120b34c78c..c6e0511515 100644 --- a/src/xrpld/overlay/detail/Message.cpp +++ b/src/xrpld/overlay/detail/Message.cpp @@ -127,41 +127,42 @@ Message::compress() } } -/** Set payload header - - The header is a variable-sized structure that contains information about - the type of the message and the length and encoding of the payload. - - The first bit determines whether a message is compressed or uncompressed; - for compressed messages, the next three bits identify the compression - algorithm. - - All multi-byte values are represented in big endian. - - For uncompressed messages (6 bytes), numbering bits from left to right: - - - The first 6 bits are set to 0. - - The next 26 bits represent the payload size. - - The remaining 16 bits represent the message type. - - For compressed messages (10 bytes), numbering bits from left to right: - - - The first 32 bits, together, represent the compression algorithm - and payload size: - - The first bit is set to 1 to indicate the message is compressed. - - The next 3 bits indicate the compression algorithm. - - The next 2 bits are reserved at this time and set to 0. - - The remaining 26 bits represent the payload size. - - The next 16 bits represent the message type. - - The remaining 32 bits are the uncompressed message size. - - The maximum size of a message at this time is 64 MB. Messages larger than - this will be dropped and the recipient may, at its option, sever the link. - - @note While nominally a part of the wire protocol, the framing is subject - to change; future versions of the code may negotiate the use of - substantially different framing. -*/ +/** + * Set payload header + * + * The header is a variable-sized structure that contains information about + * the type of the message and the length and encoding of the payload. + * + * The first bit determines whether a message is compressed or uncompressed; + * for compressed messages, the next three bits identify the compression + * algorithm. + * + * All multi-byte values are represented in big endian. + * + * For uncompressed messages (6 bytes), numbering bits from left to right: + * + * - The first 6 bits are set to 0. + * - The next 26 bits represent the payload size. + * - The remaining 16 bits represent the message type. + * + * For compressed messages (10 bytes), numbering bits from left to right: + * + * - The first 32 bits, together, represent the compression algorithm + * and payload size: + * - The first bit is set to 1 to indicate the message is compressed. + * - The next 3 bits indicate the compression algorithm. + * - The next 2 bits are reserved at this time and set to 0. + * - The remaining 26 bits represent the payload size. + * - The next 16 bits represent the message type. + * - The remaining 32 bits are the uncompressed message size. + * + * The maximum size of a message at this time is 64 MB. Messages larger than + * this will be dropped and the recipient may, at its option, sever the link. + * + * @note While nominally a part of the wire protocol, the framing is subject + * to change; future versions of the code may negotiate the use of + * substantially different framing. + */ void Message::setHeader( std::uint8_t* in, diff --git a/src/xrpld/overlay/detail/OverlayImpl.cpp b/src/xrpld/overlay/detail/OverlayImpl.cpp index b7af3b6ace..6a6a6edace 100644 --- a/src/xrpld/overlay/detail/OverlayImpl.cpp +++ b/src/xrpld/overlay/detail/OverlayImpl.cpp @@ -624,11 +624,12 @@ OverlayImpl::onWrite(beast::PropertyStream::Map& stream) } //------------------------------------------------------------------------------ -/** A peer has connected successfully - This is called after the peer handshake has been completed and during - peer activation. At this point, the peer address and the public key - are known. -*/ +/** + * A peer has connected successfully + * This is called after the peer handshake has been completed and during + * peer activation. At this point, the peer address and the public key + * are known. + */ void OverlayImpl::activate(std::shared_ptr const& peer) { @@ -725,10 +726,11 @@ OverlayImpl::reportOutboundTraffic(TrafficCount::Category cat, int size) { traffic_.addCount(cat, false, size); } -/** The number of active peers on the network - Active peers are only those peers that have completed the handshake - and are running the XRPL protocol. -*/ +/** + * The number of active peers on the network + * Active peers are only those peers that have completed the handshake + * and are running the XRPL protocol. + */ std::size_t OverlayImpl::size() const { diff --git a/src/xrpld/overlay/detail/OverlayImpl.h b/src/xrpld/overlay/detail/OverlayImpl.h index 83d5a81a89..092ac86a6d 100644 --- a/src/xrpld/overlay/detail/OverlayImpl.h +++ b/src/xrpld/overlay/detail/OverlayImpl.h @@ -194,14 +194,15 @@ public: PeerSequence getActivePeers() const override; - /** Get active peers excluding peers in toSkip. - @param toSkip peers to skip - @param active a number of active peers - @param disabled a number of peers with tx reduce-relay - feature disabled - @param enabledInSkip a number of peers with tx reduce-relay - feature enabled and in toSkip - @return active peers less peers in toSkip + /** + * Get active peers excluding peers in toSkip. + * @param toSkip peers to skip + * @param active a number of active peers + * @param disabled a number of peers with tx reduce-relay + * feature disabled + * @param enabledInSkip a number of peers with tx reduce-relay + * feature enabled and in toSkip + * @return active peers less peers in toSkip */ PeerSequence getActivePeers( @@ -251,11 +252,12 @@ public: void remove(std::shared_ptr const& slot); - /** Called when a peer has connected successfully - This is called after the peer handshake has been completed and during - peer activation. At this point, the peer address and the public key - are known. - */ + /** + * Called when a peer has connected successfully + * This is called after the peer handshake has been completed and during + * peer activation. At this point, the peer address and the public key + * are known. + */ void activate(std::shared_ptr const& peer); @@ -382,7 +384,8 @@ public: return setup_.networkID; } - /** Updates message count for validator/peer. Sends TMSquelch if the number + /** + * Updates message count for validator/peer. Sends TMSquelch if the number * of messages for N peers reaches threshold T. A message is counted * if a peer receives the message for the first time and if * the message has been relayed. @@ -398,7 +401,8 @@ public: std::set&& peers, protocol::MessageType type); - /** Overload to reduce allocation in case of single peer + /** + * Overload to reduce allocation in case of single peer */ void updateSlotAndSquelch( @@ -407,7 +411,8 @@ public: Peer::id_t peer, protocol::MessageType type); - /** Called when the peer is deleted. If the peer was selected to be the + /** + * Called when the peer is deleted. If the peer was selected to be the * source of messages from the validator then squelched peers have to be * unsquelched. * @param id Peer's id @@ -421,7 +426,9 @@ public: return txMetrics_.json(); } - /** Add tx reduce-relay metrics. */ + /** + * Add tx reduce-relay metrics. + */ template void addTxMetrics(Args... args) @@ -453,64 +460,72 @@ private: address_type remoteAddress, std::string const& msg); - /** Handles crawl requests. Crawl returns information about the - node and its peers so crawlers can map the network. - - @return true if the request was handled. - */ + /** + * Handles crawl requests. Crawl returns information about the + * node and its peers so crawlers can map the network. + * + * @return true if the request was handled. + */ bool processCrawl(http_request_type const& req, Handoff& handoff); - /** Handles validator list requests. - Using a /vl/ URL, will retrieve the - latest validator list (or UNL) that this node has for that - public key, if the node trusts that public key. - - @return true if the request was handled. - */ + /** + * Handles validator list requests. + * Using a /vl/ URL, will retrieve the + * latest validator list (or UNL) that this node has for that + * public key, if the node trusts that public key. + * + * @return true if the request was handled. + */ bool processValidatorList(http_request_type const& req, Handoff& handoff); - /** Handles health requests. Health returns information about the - health of the node. - - @return true if the request was handled. - */ + /** + * Handles health requests. Health returns information about the + * health of the node. + * + * @return true if the request was handled. + */ bool processHealth(http_request_type const& req, Handoff& handoff); - /** Handles non-peer protocol requests. - - @return true if the request was handled. - */ + /** + * Handles non-peer protocol requests. + * + * @return true if the request was handled. + */ bool processRequest(http_request_type const& req, Handoff& handoff); - /** Returns information about peers on the overlay network. - Reported through the /crawl API - Controlled through the config section [crawl] overlay=[0|1] - */ + /** + * Returns information about peers on the overlay network. + * Reported through the /crawl API + * Controlled through the config section [crawl] overlay=[0|1] + */ json::Value getOverlayInfo() const; - /** Returns information about the local server. - Reported through the /crawl API - Controlled through the config section [crawl] server=[0|1] - */ + /** + * Returns information about the local server. + * Reported through the /crawl API + * Controlled through the config section [crawl] server=[0|1] + */ json::Value getServerInfo(); - /** Returns information about the local server's performance counters. - Reported through the /crawl API - Controlled through the config section [crawl] counts=[0|1] - */ + /** + * Returns information about the local server's performance counters. + * Reported through the /crawl API + * Controlled through the config section [crawl] counts=[0|1] + */ json::Value getServerCounts(); - /** Returns information about the local server's UNL. - Reported through the /crawl API - Controlled through the config section [crawl] unl=[0|1] - */ + /** + * Returns information about the local server's UNL. + * Reported through the /crawl API + * Controlled through the config section [crawl] unl=[0|1] + */ json::Value getUnlInfo(); @@ -537,12 +552,16 @@ private: void sendEndpoints(); - /** Send once a second transactions' hashes aggregated by peers. */ + /** + * Send once a second transactions' hashes aggregated by peers. + */ void sendTxQueue() const; - /** Check if peers stopped relaying messages - * and if slots stopped receiving messages from the validator */ + /** + * Check if peers stopped relaying messages + * and if slots stopped receiving messages from the validator + */ void deleteIdlePeers(); diff --git a/src/xrpld/overlay/detail/PeerImp.cpp b/src/xrpld/overlay/detail/PeerImp.cpp index 4e42d46f46..8838970b5f 100644 --- a/src/xrpld/overlay/detail/PeerImp.cpp +++ b/src/xrpld/overlay/detail/PeerImp.cpp @@ -105,10 +105,14 @@ using namespace std::chrono_literals; namespace xrpl { namespace { -/** The threshold above which we treat a peer connection as high latency */ +/** + * The threshold above which we treat a peer connection as high latency + */ constexpr std::chrono::milliseconds kPeerHighLatency{300}; -/** How often we PING the peer to check for latency and sendq probe */ +/** + * How often we PING the peer to check for latency and sendq probe + */ constexpr std::chrono::seconds kPeerTimerInterval{60}; } // namespace diff --git a/src/xrpld/overlay/detail/PeerImp.h b/src/xrpld/overlay/detail/PeerImp.h index e9ef948d48..ea6eccd656 100644 --- a/src/xrpld/overlay/detail/PeerImp.h +++ b/src/xrpld/overlay/detail/PeerImp.h @@ -64,7 +64,9 @@ class SHAMap; class PeerImp : public Peer, public std::enable_shared_from_this, public OverlayImpl::Child { public: - /** Whether the peer's view of the ledger converges or diverges from ours */ + /** + * Whether the peer's view of the ledger converges or diverges from ours + */ enum class Tracking { Diverged, Unknown, Converged }; private: @@ -249,7 +251,9 @@ public: PeerImp& operator=(PeerImp const&) = delete; - /** Create an active incoming peer from an established ssl connection. */ + /** + * Create an active incoming peer from an established ssl connection. + */ PeerImp( Application& app, id_t id, @@ -261,7 +265,9 @@ public: std::unique_ptr&& streamPtr, OverlayImpl& overlay); - /** Create outgoing, handshaked peer. */ + /** + * Create outgoing, handshaked peer. + */ // VFALCO legacyPublicKey should be implied by the Slot template PeerImp( @@ -305,23 +311,29 @@ public: void send(std::shared_ptr const& m) override; - /** Send aggregated transactions' hashes */ + /** + * Send aggregated transactions' hashes + */ void sendTxQueue() override; - /** Add transaction's hash to the transactions' hashes queue - @param hash transaction's hash + /** + * Add transaction's hash to the transactions' hashes queue + * @param hash transaction's hash */ void addTxQueue(uint256 const& hash) override; - /** Remove transaction's hash from the transactions' hashes queue - @param hash transaction's hash + /** + * Remove transaction's hash from the transactions' hashes queue + * @param hash transaction's hash */ void removeTxQueue(uint256 const& hash) override; - /** Send a set of PeerFinder endpoints as a protocol message. */ + /** + * Send a set of PeerFinder endpoints as a protocol message. + */ template void sendEndpoints(FwdIt first, FwdIt last) @@ -347,16 +359,19 @@ public: return id_; } - /** Returns `true` if this connection will publicly share its IP address. */ + /** + * Returns `true` if this connection will publicly share its IP address. + */ bool crawl() const; bool cluster() const override; - /** Check if the peer is tracking - @param validationSeq The ledger sequence of a recently-validated ledger - */ + /** + * Check if the peer is tracking + * @param validationSeq The ledger sequence of a recently-validated ledger + */ void checkTracking(std::uint32_t validationSeq); @@ -369,7 +384,9 @@ public: return publicKey_; } - /** Return the version of xrpld that the peer is running, if reported. */ + /** + * Return the version of xrpld that the peer is running, if reported. + */ std::string getVersion() const; @@ -504,17 +521,18 @@ private: void onWriteMessage(error_code ec, std::size_t bytesTransferred); - /** Called from onMessage(TMTransaction(s)). - @param m Transaction protocol message - @param eraseTxQueue is true when called from onMessage(TMTransaction) - and is false when called from onMessage(TMTransactions). If true then - the transaction hash is erased from txQueue_. Don't need to erase from - the queue when called from onMessage(TMTransactions) because this - message is a response to the missing transactions request and the queue - would not have any of these transactions. - @param batch is false when called from onMessage(TMTransaction) - and is true when called from onMessage(TMTransactions). If true, then the - transaction is part of a batch, and should not be charged an extra fee. + /** + * Called from onMessage(TMTransaction(s)). + * @param m Transaction protocol message + * @param eraseTxQueue is true when called from onMessage(TMTransaction) + * and is false when called from onMessage(TMTransactions). If true then + * the transaction hash is erased from txQueue_. Don't need to erase from + * the queue when called from onMessage(TMTransactions) because this + * message is a response to the missing transactions request and the queue + * would not have any of these transactions. + * @param batch is false when called from onMessage(TMTransaction) + * and is true when called from onMessage(TMTransactions). If true, then the + * transaction is part of a batch, and should not be charged an extra fee. */ void handleTransaction( @@ -522,10 +540,11 @@ private: bool eraseTxQueue, bool batch); - /** Handle protocol message with hashes of transactions that have not - been relayed by an upstream node down to its peers - request - transactions, which have not been relayed to this peer. - @param m protocol message with transactions' hashes + /** + * Handle protocol message with hashes of transactions that have not + * been relayed by an upstream node down to its peers - request + * transactions, which have not been relayed to this peer. + * @param m protocol message with transactions' hashes */ void handleHaveTransactions(std::shared_ptr const& m); @@ -623,9 +642,10 @@ private: std::uint32_t version, std::vector const& blobs); - /** Process peer's request to send missing transactions. The request is - sent in response to TMHaveTransactions. - @param packet protocol message containing missing transactions' hashes. + /** + * Process peer's request to send missing transactions. The request is + * sent in response to TMHaveTransactions. + * @param packet protocol message containing missing transactions' hashes. */ void doTransactions(std::shared_ptr const& packet); @@ -669,52 +689,55 @@ protected: // Production callers reach these members only via // `onMessage(TMGetObjectByHash)` → JobQueue → `processGetObjectByHash`. - /** Process a generic-query TMGetObjectByHash message. - - Dispatched from `onMessage(TMGetObjectByHash)` to the JobQueue - (`JtLedgerReq`) so synchronous NodeStore lookups do not block the - peer's I/O strand. Caps iteration at `Tuning::kHardMaxReplyNodes` - regardless of hit/miss outcome and applies differential pricing - via `computeGetObjectByHashFee()` after the fetch loop completes. - - @param m The protocol message containing requested object hashes. + /** + * Process a generic-query TMGetObjectByHash message. + * + * Dispatched from `onMessage(TMGetObjectByHash)` to the JobQueue + * (`JtLedgerReq`) so synchronous NodeStore lookups do not block the + * peer's I/O strand. Caps iteration at `Tuning::kHardMaxReplyNodes` + * regardless of hit/miss outcome and applies differential pricing + * via `computeGetObjectByHashFee()` after the fetch loop completes. + * + * @param m The protocol message containing requested object hashes. */ void processGetObjectByHash(std::shared_ptr const& m); - /** Compute the per-message resource charge for a TMGetObjectByHash - request based on how much work was actually performed. - - The charge has three components on top of the base - `Resource::kFeeModerateBurdenPeer`: - - per-hit lookup cost (cheap; usually served from cache) - - per-miss lookup cost (expensive node store seeks) - - request-size band surcharge (escalates abusive batch sizes) - - The first `Tuning::kFreeObjectsPerRequest` objects are free so - that legitimate `InboundLedger::getNeededHashes()` traffic - (at most 8 objects) is unaffected. - - @param requested Number of objects requested by the message. This - value is used for request-size pricing and may - exceed `Tuning::kHardMaxReplyNodes` when this - helper is called directly, even though processing - caps the iterations to `Tuning::kHardMaxReplyNodes`. - @param found Number of objects successfully returned in the - reply. - @return A `Resource::Charge` whose cost reflects the work performed. + /** + * Compute the per-message resource charge for a TMGetObjectByHash + * request based on how much work was actually performed. + * + * The charge has three components on top of the base + * `Resource::kFeeModerateBurdenPeer`: + * - per-hit lookup cost (cheap; usually served from cache) + * - per-miss lookup cost (expensive node store seeks) + * - request-size band surcharge (escalates abusive batch sizes) + * + * The first `Tuning::kFreeObjectsPerRequest` objects are free so + * that legitimate `InboundLedger::getNeededHashes()` traffic + * (at most 8 objects) is unaffected. + * + * @param requested Number of objects requested by the message. This + * value is used for request-size pricing and may + * exceed `Tuning::kHardMaxReplyNodes` when this + * helper is called directly, even though processing + * caps the iterations to `Tuning::kHardMaxReplyNodes`. + * @param found Number of objects successfully returned in the + * reply. + * @return A `Resource::Charge` whose cost reflects the work performed. */ static Resource::Charge computeGetObjectByHashFee(int const requested, int const found); - /** Read-only accessor for the accumulated peer-message charge. - - Exposed at `protected` scope so test subclasses can verify the - oversized-request rejection path (Layer 1) without invoking the - full JobQueue handler. Production callers should never read this back — - the value is consumed by `charge()`/`disconnect()` internally. - - @return The current `Resource::Charge` accumulated on `fee_`. + /** + * Read-only accessor for the accumulated peer-message charge. + * + * Exposed at `protected` scope so test subclasses can verify the + * oversized-request rejection path (Layer 1) without invoking the + * full JobQueue handler. Production callers should never read this back — + * the value is consumed by `charge()`/`disconnect()` internally. + * + * @return The current `Resource::Charge` accumulated on `fee_`. */ Resource::Charge currentFeeCharge() const diff --git a/src/xrpld/overlay/detail/PeerSet.cpp b/src/xrpld/overlay/detail/PeerSet.cpp index 463b68bb6c..61bede37fe 100644 --- a/src/xrpld/overlay/detail/PeerSet.cpp +++ b/src/xrpld/overlay/detail/PeerSet.cpp @@ -33,7 +33,9 @@ public: std::function const&)> hasItem, std::function const&)> onPeerAdded) override; - /** Send a message to one or all peers. */ + /** + * Send a message to one or all peers. + */ void sendRequest( ::google::protobuf::Message const& message, @@ -49,7 +51,9 @@ private: Application& app_; beast::Journal journal_; - /** The identifiers of the peers we are tracking. */ + /** + * The identifiers of the peers we are tracking. + */ std::set peers_; }; diff --git a/src/xrpld/overlay/detail/ProtocolMessage.h b/src/xrpld/overlay/detail/ProtocolMessage.h index 156fcfc104..ef1bc8cb2b 100644 --- a/src/xrpld/overlay/detail/ProtocolMessage.h +++ b/src/xrpld/overlay/detail/ProtocolMessage.h @@ -42,7 +42,9 @@ protocolMessageType(protocol::TMProofPathRequest const&) return protocol::mtPROOF_PATH_REQ; } -/** Returns the name of a protocol message given its type. */ +/** + * Returns the name of a protocol message given its type. + */ template std::string protocolMessageName(int type) @@ -101,25 +103,35 @@ namespace detail { struct MessageHeader { - /** The size of the message on the wire. - - @note This is the sum of sizes of the header and the payload. - */ + /** + * The size of the message on the wire. + * + * @note This is the sum of sizes of the header and the payload. + */ std::uint32_t totalWireSize = 0; - /** The size of the header associated with this message. */ + /** + * The size of the header associated with this message. + */ std::uint32_t headerSize = 0; - /** The size of the payload on the wire. */ + /** + * The size of the payload on the wire. + */ std::uint32_t payloadWireSize = 0; - /** Uncompressed message size if the message is compressed. */ + /** + * Uncompressed message size if the message is compressed. + */ std::uint32_t uncompressedSize = 0; - /** The type of the message. */ + /** + * The type of the message. + */ std::uint16_t messageType = 0; - /** Indicates which compression algorithm the payload is compressed with. + /** + * Indicates which compression algorithm the payload is compressed with. * Currently only lz4 is supported. If None then the message is not * compressed. */ @@ -140,14 +152,15 @@ buffersEnd(BufferSequence const& bufs) return boost::asio::buffers_iterator::end(bufs); } -/** Parse a message header - * @return a seated optional if the message header was successfully - * parsed. An unseated optional otherwise, in which case - * @param ec contains more information: - * - set to `errc::success` if not enough bytes were present - * - set to `errc::no_message` if a valid header was not present - * @bufs - sequence of input buffers, can't be empty - * @size input data size +/** + * Parse a message header. + * + * @param ec On failure, set to `errc::success` if not enough bytes were + * present, or `errc::no_message` if a valid header was not present. + * @param bufs Sequence of input buffers; can't be empty. + * @param size Input data size. + * @return A seated optional if the message header was successfully parsed, or + * an unseated optional otherwise (see @p ec). */ template std::optional @@ -294,18 +307,19 @@ invoke(MessageHeader const& header, Buffers const& buffers, Handler& handler) } // namespace detail -/** Calls the handler for up to one protocol message in the passed buffers. - - If there is insufficient data to produce a complete protocol - message, zero is returned for the number of bytes consumed. - - @param buffers The buffer that contains the data we've received - @param handler The handler that will be used to process the message - @param hint If possible, a hint as to the amount of data to read next. The - returned value MAY be zero, which means "no hint" - - @return The number of bytes consumed, or the error code if any. -*/ +/** + * Calls the handler for up to one protocol message in the passed buffers. + * + * If there is insufficient data to produce a complete protocol + * message, zero is returned for the number of bytes consumed. + * + * @param buffers The buffer that contains the data we've received + * @param handler The handler that will be used to process the message + * @param hint If possible, a hint as to the amount of data to read next. The + * returned value MAY be zero, which means "no hint" + * + * @return The number of bytes consumed, or the error code if any. + */ template std::pair invokeProtocolMessage(Buffers const& buffers, Handler& handler, std::size_t& hint) diff --git a/src/xrpld/overlay/detail/ProtocolVersion.cpp b/src/xrpld/overlay/detail/ProtocolVersion.cpp index 62660b0e01..347e59accb 100644 --- a/src/xrpld/overlay/detail/ProtocolVersion.cpp +++ b/src/xrpld/overlay/detail/ProtocolVersion.cpp @@ -19,11 +19,12 @@ namespace xrpl { -/** The list of protocol versions we speak and we prefer to use. - - @note The list must be sorted in strictly ascending order (and so - it may not contain any duplicates!) -*/ +/** + * The list of protocol versions we speak and we prefer to use. + * + * @note The list must be sorted in strictly ascending order (and so + * it may not contain any duplicates!) + */ constexpr ProtocolVersion const kSupportedProtocolList[]{ {2, 1}, diff --git a/src/xrpld/overlay/detail/ProtocolVersion.h b/src/xrpld/overlay/detail/ProtocolVersion.h index d6d9da7ad3..b56871318a 100644 --- a/src/xrpld/overlay/detail/ProtocolVersion.h +++ b/src/xrpld/overlay/detail/ProtocolVersion.h @@ -10,11 +10,12 @@ namespace xrpl { -/** Represents a particular version of the peer-to-peer protocol. - - The protocol is represented as two pairs of 16-bit integers; a major - and a minor. - * */ +/** + * Represents a particular version of the peer-to-peer protocol. + * + * The protocol is represented as two pairs of 16-bit integers; a major + * and a minor. + */ using ProtocolVersion = std::pair; constexpr ProtocolVersion @@ -23,37 +24,48 @@ makeProtocol(std::uint16_t major, std::uint16_t minor) return {major, minor}; } -/** Print a protocol version a human-readable string. */ +/** + * Print a protocol version a human-readable string. + */ std::string to_string(ProtocolVersion const& p); -/** Parse a set of protocol versions. - - Given a comma-separated string, extract and return all those that look - like valid protocol versions (i.e. XRPL/2.0 and later). Strings that are - not parsable as valid protocol strings are excluded from the result set. - - @return A list of all apparently valid protocol versions. - - @note The returned list of protocol versions is guaranteed to contain - no duplicates and will be sorted in ascending protocol order. -*/ +/** + * Parse a set of protocol versions. + * + * Given a comma-separated string, extract and return all those that look + * like valid protocol versions (i.e. XRPL/2.0 and later). Strings that are + * not parsable as valid protocol strings are excluded from the result set. + * + * @return A list of all apparently valid protocol versions. + * + * @note The returned list of protocol versions is guaranteed to contain + * no duplicates and will be sorted in ascending protocol order. + */ std::vector parseProtocolVersions(boost::beast::string_view const& s); -/** Given a list of supported protocol versions, choose the one we prefer. */ +/** + * Given a list of supported protocol versions, choose the one we prefer. + */ std::optional negotiateProtocolVersion(std::vector const& versions); -/** Given a list of supported protocol versions, choose the one we prefer. */ +/** + * Given a list of supported protocol versions, choose the one we prefer. + */ std::optional negotiateProtocolVersion(boost::beast::string_view const& versions); -/** The list of all the protocol versions we support. */ +/** + * The list of all the protocol versions we support. + */ std::string const& supportedProtocolVersions(); -/** Determine whether we support a specific protocol version. */ +/** + * Determine whether we support a specific protocol version. + */ bool isProtocolSupported(ProtocolVersion const& v); diff --git a/src/xrpld/overlay/detail/TrafficCount.h b/src/xrpld/overlay/detail/TrafficCount.h index b96ee022d6..8dc8ddb08e 100644 --- a/src/xrpld/overlay/detail/TrafficCount.h +++ b/src/xrpld/overlay/detail/TrafficCount.h @@ -15,23 +15,23 @@ namespace xrpl { /** - TrafficCount is used to count ingress and egress wire bytes and number of - messages. The general intended usage is as follows: - 1. Determine the message category by callin TrafficCount::categorize - 2. Increment the counters for incoming or outgoing traffic by calling - TrafficCount::addCount - 3. Optionally, TrafficCount::addCount can be called at any time to - increment additional traffic categories, not captured by - TrafficCount::categorize. - - There are two special categories: - 1. category::total - this category is used to report the total traffic - amount. It should be incremented once just after receiving a new message, and - once just before sending a message to a peer. Messages whose category is not - in TrafficCount::categorize are not included in the total. - 2. category::unknown - this category is used to report traffic for - messages of unknown type. -*/ + * TrafficCount is used to count ingress and egress wire bytes and number of + * messages. The general intended usage is as follows: + * 1. Determine the message category by callin TrafficCount::categorize + * 2. Increment the counters for incoming or outgoing traffic by calling + * TrafficCount::addCount + * 3. Optionally, TrafficCount::addCount can be called at any time to + * increment additional traffic categories, not captured by + * TrafficCount::categorize. + * + * There are two special categories: + * 1. category::total - this category is used to report the total traffic + * amount. It should be incremented once just after receiving a new message, and + * once just before sending a message to a peer. Messages whose category is not + * in TrafficCount::categorize are not included in the total. + * 2. category::unknown - this category is used to report traffic for + * messages of unknown type. + */ class TrafficCount { public: @@ -186,7 +186,8 @@ public: TrafficCount() = default; - /** Given a protocol message, determine which traffic category it belongs to + /** + * Given a protocol message, determine which traffic category it belongs to */ static Category categorize( @@ -194,7 +195,9 @@ public: protocol::MessageType type, bool inbound); - /** Account for traffic associated with the given category */ + /** + * Account for traffic associated with the given category + */ void addCount(Category cat, bool inbound, int bytes) { @@ -219,9 +222,10 @@ public: } } - /** An up-to-date copy of all the counters - - @return an object which satisfies the requirements of Container + /** + * An up-to-date copy of all the counters + * + * @return an object which satisfies the requirements of Container */ [[nodiscard]] auto const& getCounts() const diff --git a/src/xrpld/overlay/detail/Tuning.h b/src/xrpld/overlay/detail/Tuning.h index 8357fcd130..5488fab07b 100644 --- a/src/xrpld/overlay/detail/Tuning.h +++ b/src/xrpld/overlay/detail/Tuning.h @@ -7,126 +7,161 @@ namespace xrpl::Tuning { -/** How many ledgers off a server can be and we will - still consider it converged */ +/** + * How many ledgers off a server can be and we will + * still consider it converged + */ static constexpr std::uint32_t kConvergedLedgerLimit = 24; -/** How many ledgers off a server has to be before we - consider it diverged */ +/** + * How many ledgers off a server has to be before we + * consider it diverged + */ static constexpr std::uint32_t kDivergedLedgerLimit = 128; -/** The soft cap on the number of ledger entries in a single reply. */ +/** + * The soft cap on the number of ledger entries in a single reply. + */ static constexpr auto kSoftMaxReplyNodes = 8192; -/** The hard cap on the number of ledger entries in a single reply. */ +/** + * The hard cap on the number of ledger entries in a single reply. + */ static constexpr auto kHardMaxReplyNodes = 12288; -/** How many timer intervals a sendq has to stay large before we disconnect */ +/** + * How many timer intervals a sendq has to stay large before we disconnect + */ static constexpr auto kSendqIntervals = 4; -/** How many messages on a send queue before we refuse queries */ +/** + * How many messages on a send queue before we refuse queries + */ static constexpr auto kDropSendQueue = 192; -/** How many messages we consider reasonable sustained on a send queue */ +/** + * How many messages we consider reasonable sustained on a send queue + */ static constexpr auto kTargetSendQueue = 128; -/** How often to log send queue size */ +/** + * How often to log send queue size + */ static constexpr auto kSendQueueLogFreq = 64; -/** How often we check for idle peers (seconds) */ +/** + * How often we check for idle peers (seconds) + */ static constexpr auto kCheckIdlePeers = 4; -/** The maximum number of levels to search */ +/** + * The maximum number of levels to search + */ static constexpr auto kMaxQueryDepth = 3; -/** Size of buffer used to read from the socket. */ +/** + * Size of buffer used to read from the socket. + */ constexpr std::size_t kReadBufferBytes = 16384; -/** TMGetObjectByHash differential pricing. +/** + * TMGetObjectByHash differential pricing. + * + * Honest peers ask for at most 8 hashes per call (the header, or up to + * 4 state + 4 tx hashes from `InboundLedger::getNeededHashes()`). The + * free tier covers them at zero cost. Beyond that, each lookup is billed: + * 'misses' cost much more than 'hits' because a miss does a node store seek + * while a hit is usually served from cache. On top of that, a size-band + * surcharge kicks in for larger requests so an attacker who crams a + * single message with thousands of hashes blows past + * `Resource::kDropThreshold` and gets disconnected. + * + * The numbers below are picked to keep three things true given + * `kDropThreshold = 25000`: + * + * - Honest traffic (<= 8 objects per request) is free. + * - A single all-miss request at `kHardMaxReplyNodes` (12288) costs + * more than the drop threshold, so an attacker gets dropped in one + * message. + * - A peer spamming 1024-object hit-only requests gets dropped in + * ~19 messages — fast enough to be useful, slow enough that an + * honest peer momentarily sending oversized requests has time to + * back off. + */ - Honest peers ask for at most 8 hashes per call (the header, or up to - 4 state + 4 tx hashes from `InboundLedger::getNeededHashes()`). The - free tier covers them at zero cost. Beyond that, each lookup is billed: - 'misses' cost much more than 'hits' because a miss does a node store seek - while a hit is usually served from cache. On top of that, a size-band - surcharge kicks in for larger requests so an attacker who crams a - single message with thousands of hashes blows past - `Resource::kDropThreshold` and gets disconnected. - - The numbers below are picked to keep three things true given - `kDropThreshold = 25000`: - - - Honest traffic (<= 8 objects per request) is free. - - A single all-miss request at `kHardMaxReplyNodes` (12288) costs - more than the drop threshold, so an attacker gets dropped in one - message. - - A peer spamming 1024-object hit-only requests gets dropped in - ~19 messages — fast enough to be useful, slow enough that an - honest peer momentarily sending oversized requests has time to - back off. */ - -/** How many objects a request can ask for before per-lookup billing - begins? - Twice the honest peak (8) so a peer that occasionally retries a hash - never trips pricing. Same value as `SHAMapInnerNode::kBranchFactor`; - that's a coincidence, not a requirement. */ +/** + * How many objects a request can ask for before per-lookup billing + * begins? + * Twice the honest peak (8) so a peer that occasionally retries a hash + * never trips pricing. Same value as `SHAMapInnerNode::kBranchFactor`; + * that's a coincidence, not a requirement. + */ static constexpr auto kFreeObjectsPerRequest = 16; -/** Cost of one cache-hit lookup. The unit; everything else is a - multiple of this. */ +/** + * Cost of one cache-hit lookup. The unit; everything else is a + * multiple of this. + */ static constexpr auto kCostPerLookupHit = 1; -/** Cost of one node-store miss, in units of `kCostPerLookupHit`. - - A miss does a node store disk seek; a hit usually comes from cache. - The 8x ratio is an order-of-magnitude guess at the latency gap on - SSD-backed nodes, not a measured number. The math only requires this - to be at least 2 — any smaller and a full-miss request at the hard - cap wouldn't trip the drop threshold. 8 leaves headroom: if - `kDropThreshold` goes up or `kHardMaxReplyNodes` comes down, the - drop-on-attack property still holds without a code change. */ +/** + * Cost of one node-store miss, in units of `kCostPerLookupHit`. + * + * A miss does a node store disk seek; a hit usually comes from cache. + * The 8x ratio is an order-of-magnitude guess at the latency gap on + * SSD-backed nodes, not a measured number. The math only requires this + * to be at least 2 — any smaller and a full-miss request at the hard + * cap wouldn't trip the drop threshold. 8 leaves headroom: if + * `kDropThreshold` goes up or `kHardMaxReplyNodes` comes down, the + * drop-on-attack property still holds without a code change. + */ static constexpr auto kCostPerLookupMiss = 8; -/** Size-band surcharges. Whichever band a request's size falls into, - its surcharge is added once on top of the per-lookup cost. - - The job of the surcharge is to make crossing a band edge feel like - a step, not a slope. With these values, the cost roughly doubles or triples at each cliff: - - n=64: costs 48 => n=65 costs 149 (~3x jump) - n=1024: costs 1108 => n=1025 costs 2009 (~2x jump) - - The 10x step between medium and large mirrors the ~16x step - between the band edges (64 -> 1024) so the cliff feels comparable - at both scales. +/** + * Size-band surcharges. Whichever band a request's size falls into, + * its surcharge is added once on top of the per-lookup cost. + * + * The job of the surcharge is to make crossing a band edge feel like + * a step, not a slope. With these values, the cost roughly doubles or triples at each cliff: + * + * n=64: costs 48 => n=65 costs 149 (~3x jump) + * n=1024: costs 1108 => n=1025 costs 2009 (~2x jump) + * + * The 10x step between medium and large mirrors the ~16x step + * between the band edges (64 -> 1024) so the cliff feels comparable + * at both scales. */ static constexpr auto kCostBandSmall = 0; static constexpr auto kCostBandMedium = 100; static constexpr auto kCostBandLarge = 1000; -/** How many hashes per type an honest peer asks for at a time. - - Matches the `4` passed to `neededStateHashes(4)` and - `neededTxHashes(4)` in `InboundLedger::getNeededHashes()`. Kept here - instead of imported from the ledger module so overlay stays - self-contained; if that `4` ever changes, update this in lockstep or - the band thresholds below will start charging honest peers. */ +/** + * How many hashes per type an honest peer asks for at a time. + * + * Matches the `4` passed to `neededStateHashes(4)` and + * `neededTxHashes(4)` in `InboundLedger::getNeededHashes()`. Kept here + * instead of imported from the ledger module so overlay stays + * self-contained; if that `4` ever changes, update this in lockstep or + * the band thresholds below will start charging honest peers. + */ static constexpr auto kLegitHashesPerType = 4; -/** Cutoffs that decide which size band a request falls into. - - A SHAMap inner node has 16 children; an honest peer asks for 4 - hashes per type. So: - - kBandSmallMax = 4 * 16 = 64 // one inner node's worth - kBandMediumMax = 4 * 16^2 = 1024 // a depth-2 subtree's worth - - A request up to 64 objects is small (no surcharge); up to 1024 is - medium; anything larger is large. The bounds are inclusive: a - request of exactly 64 is small, 65 is medium. Anything past 1024 is - well beyond what the honest sync path produces, so it's billed at - the large rate to drive attack-shaped traffic over the drop - threshold quickly. */ +/** + * Cutoffs that decide which size band a request falls into. + * + * A SHAMap inner node has 16 children; an honest peer asks for 4 + * hashes per type. So: + * + * kBandSmallMax = 4 * 16 = 64 // one inner node's worth + * kBandMediumMax = 4 * 16^2 = 1024 // a depth-2 subtree's worth + * + * A request up to 64 objects is small (no surcharge); up to 1024 is + * medium; anything larger is large. The bounds are inclusive: a + * request of exactly 64 is small, 65 is medium. Anything past 1024 is + * well beyond what the honest sync path produces, so it's billed at + * the large rate to drive attack-shaped traffic over the drop + * threshold quickly. + */ static constexpr auto kBandSmallMax = kLegitHashesPerType * SHAMapInnerNode::kBranchFactor; static constexpr auto kBandMediumMax = kBandSmallMax * SHAMapInnerNode::kBranchFactor; diff --git a/src/xrpld/overlay/detail/TxMetrics.h b/src/xrpld/overlay/detail/TxMetrics.h index 44cd0272ee..a9afa1d6b2 100644 --- a/src/xrpld/overlay/detail/TxMetrics.h +++ b/src/xrpld/overlay/detail/TxMetrics.h @@ -12,17 +12,19 @@ namespace xrpl::metrics { -/** Run single metrics rolling average. Can be either average of a value - per second or average of a value's sample per second. For instance, - for transaction it makes sense to have transaction bytes and count - per second, but for a number of selected peers to relay per transaction - it makes sense to have sample's average. +/** + * Run single metrics rolling average. Can be either average of a value + * per second or average of a value's sample per second. For instance, + * for transaction it makes sense to have transaction bytes and count + * per second, but for a number of selected peers to relay per transaction + * it makes sense to have sample's average. */ struct SingleMetrics { - /** Class constructor - @param ptu if true then calculate metrics per second, otherwise - sample's average + /** + * Class constructor + * @param ptu if true then calculate metrics per second, otherwise + * sample's average */ SingleMetrics(bool ptu = true) : perTimeUnit(ptu) { @@ -34,15 +36,18 @@ struct SingleMetrics std::uint32_t n{0}; bool perTimeUnit{true}; boost::circular_buffer rollingAvgAggregate{30, 0ull}; - /** Add metrics value + /** + * Add metrics value * @param val metrics value, either bytes or count */ void addMetrics(std::uint32_t val); }; -/** Run two metrics. For instance message size and count for - protocol messages. */ +/** + * Run two metrics. For instance message size and count for + * protocol messages. + */ struct MultipleMetrics { MultipleMetrics(bool ptu1 = true, bool ptu2 = true) : m1(ptu1), m2(ptu2) @@ -51,12 +56,14 @@ struct MultipleMetrics SingleMetrics m1; SingleMetrics m2; - /** Add metrics to m2. m1 in this case aggregates the frequency. - @param val2 m2 metrics value + /** + * Add metrics to m2. m1 in this case aggregates the frequency. + * @param val2 m2 metrics value */ void addMetrics(std::uint32_t val2); - /** Add metrics to m1 and m2. + /** + * Add metrics to m1 and m2. * @param val1 m1 metrics value * @param val2 m2 metrics value */ @@ -64,7 +71,9 @@ struct MultipleMetrics addMetrics(std::uint32_t val1, std::uint32_t val2); }; -/** Run transaction reduce-relay feature related metrics */ +/** + * Run transaction reduce-relay feature related metrics + */ struct TxMetrics { mutable std::mutex mutex; @@ -86,26 +95,30 @@ struct TxMetrics SingleMetrics notEnabled{false}; // TMTransactions number of transactions count per second SingleMetrics missingTx; - /** Add protocol message metrics - @param type protocol message type - @param val message size in bytes + /** + * Add protocol message metrics + * @param type protocol message type + * @param val message size in bytes */ void addMetrics(protocol::MessageType type, std::uint32_t val); - /** Add peers selected for relaying and suppressed peers metrics. - @param selected number of selected peers to relay - @param suppressed number of suppressed peers - @param notEnabled number of peers with tx reduce-relay featured disabled + /** + * Add peers selected for relaying and suppressed peers metrics. + * @param selected number of selected peers to relay + * @param suppressed number of suppressed peers + * @param notEnabled number of peers with tx reduce-relay featured disabled */ void addMetrics(std::uint32_t selected, std::uint32_t suppressed, std::uint32_t notEnabled); - /** Add number of missing transactions that a node requested - @param missing number of missing transactions + /** + * Add number of missing transactions that a node requested + * @param missing number of missing transactions */ void addMetrics(std::uint32_t missing); - /** Get json representation of the metrics - @return json object + /** + * Get json representation of the metrics + * @return json object */ json::Value json() const; diff --git a/src/xrpld/overlay/detail/ZeroCopyStream.h b/src/xrpld/overlay/detail/ZeroCopyStream.h index deccb627b9..9bbe69910b 100644 --- a/src/xrpld/overlay/detail/ZeroCopyStream.h +++ b/src/xrpld/overlay/detail/ZeroCopyStream.h @@ -11,11 +11,12 @@ namespace xrpl { -/** Implements ZeroCopyInputStream around a buffer sequence. - @tparam Buffers A type meeting the requirements of ConstBufferSequence. - @see - https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.io.zero_copy_stream -*/ +/** + * Implements ZeroCopyInputStream around a buffer sequence. + * @tparam Buffers A type meeting the requirements of ConstBufferSequence. + * @see + * https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.io.zero_copy_stream + */ template class ZeroCopyInputStream : public ::google::protobuf::io::ZeroCopyInputStream { @@ -105,10 +106,11 @@ ZeroCopyInputStream::Skip(int count) //------------------------------------------------------------------------------ -/** Implements ZeroCopyOutputStream around a Streambuf. - Streambuf matches the public interface defined by boost::asio::streambuf. - @tparam Streambuf A type meeting the requirements of Streambuf. -*/ +/** + * Implements ZeroCopyOutputStream around a Streambuf. + * Streambuf matches the public interface defined by boost::asio::streambuf. + * @tparam Streambuf A type meeting the requirements of Streambuf. + */ template class ZeroCopyOutputStream : public ::google::protobuf::io::ZeroCopyOutputStream { diff --git a/src/xrpld/overlay/make_Overlay.h b/src/xrpld/overlay/make_Overlay.h index f0dfa429c0..a62d4b49de 100644 --- a/src/xrpld/overlay/make_Overlay.h +++ b/src/xrpld/overlay/make_Overlay.h @@ -19,7 +19,9 @@ namespace xrpl { Overlay::Setup setupOverlay(BasicConfig const& config, beast::Journal j); -/** Creates the implementation of Overlay. */ +/** + * Creates the implementation of Overlay. + */ std::unique_ptr makeOverlay( Application& app, diff --git a/src/xrpld/overlay/predicates.h b/src/xrpld/overlay/predicates.h index 2527b8c728..56f4c80291 100644 --- a/src/xrpld/overlay/predicates.h +++ b/src/xrpld/overlay/predicates.h @@ -8,7 +8,9 @@ namespace xrpl { -/** Sends a message to all peers */ +/** + * Sends a message to all peers + */ struct SendAlways { using return_type = void; @@ -28,7 +30,9 @@ struct SendAlways //------------------------------------------------------------------------------ -/** Sends a message to match peers */ +/** + * Sends a message to match peers + */ template struct SendIfPred { @@ -49,7 +53,9 @@ struct SendIfPred } }; -/** Helper function to aid in type deduction */ +/** + * Helper function to aid in type deduction + */ template SendIfPred sendIf(std::shared_ptr const& m, Predicate const& f) @@ -59,7 +65,9 @@ sendIf(std::shared_ptr const& m, Predicate const& f) //------------------------------------------------------------------------------ -/** Sends a message to non-matching peers */ +/** + * Sends a message to non-matching peers + */ template struct SendIfNotPred { @@ -80,7 +88,9 @@ struct SendIfNotPred } }; -/** Helper function to aid in type deduction */ +/** + * Helper function to aid in type deduction + */ template SendIfNotPred sendIfNot(std::shared_ptr const& m, Predicate const& f) @@ -90,7 +100,9 @@ sendIfNot(std::shared_ptr const& m, Predicate const& f) //------------------------------------------------------------------------------ -/** Select the specific peer */ +/** + * Select the specific peer + */ struct MatchPeer { Peer const* matchPeer; @@ -108,7 +120,9 @@ struct MatchPeer //------------------------------------------------------------------------------ -/** Select all peers (except optional excluded) that are in our cluster */ +/** + * Select all peers (except optional excluded) that are in our cluster + */ struct PeerInCluster { MatchPeer skipPeer; @@ -132,7 +146,9 @@ struct PeerInCluster //------------------------------------------------------------------------------ -/** Select all peers that are in the specified set */ +/** + * Select all peers that are in the specified set + */ struct PeerInSet { std::set const& peerSet; diff --git a/src/xrpld/peerfinder/PeerfinderManager.h b/src/xrpld/peerfinder/PeerfinderManager.h index d482ae7241..0530343641 100644 --- a/src/xrpld/peerfinder/PeerfinderManager.h +++ b/src/xrpld/peerfinder/PeerfinderManager.h @@ -24,71 +24,101 @@ namespace xrpl::PeerFinder { using clock_type = beast::AbstractClock; -/** Represents a set of addresses. */ +/** + * Represents a set of addresses. + */ using IPAddresses = std::vector; //------------------------------------------------------------------------------ -/** PeerFinder configuration settings. */ +/** + * PeerFinder configuration settings. + */ struct Config { - /** The largest number of public peer slots to allow. - This includes both inbound and outbound, but does not include - fixed peers. - */ + /** + * The largest number of public peer slots to allow. + * This includes both inbound and outbound, but does not include + * fixed peers. + */ std::size_t maxPeers{Tuning::kDefaultMaxPeers}; - /** The number of automatic outbound connections to maintain. - Outbound connections are only maintained if autoConnect - is `true`. - */ + /** + * The number of automatic outbound connections to maintain. + * Outbound connections are only maintained if autoConnect + * is `true`. + */ std::size_t outPeers; - /** The number of automatic inbound connections to maintain. - Inbound connections are only maintained if wantIncoming - is `true`. - */ + /** + * The number of automatic inbound connections to maintain. + * Inbound connections are only maintained if wantIncoming + * is `true`. + */ std::size_t inPeers{0}; - /** `true` if we want our IP address kept private. */ + /** + * `true` if we want our IP address kept private. + */ bool peerPrivate = true; - /** `true` if we want to accept incoming connections. */ + /** + * `true` if we want to accept incoming connections. + */ bool wantIncoming{true}; - /** `true` if we want to establish connections automatically */ + /** + * `true` if we want to establish connections automatically + */ bool autoConnect{true}; - /** The listening port number. */ + /** + * The listening port number. + */ std::uint16_t listeningPort{0}; - /** The set of features we advertise. */ + /** + * The set of features we advertise. + */ std::string features; - /** Limit how many incoming connections we allow per IP */ + /** + * Limit how many incoming connections we allow per IP + */ int ipLimit{0}; - /** `true` if we want to verify endpoints in TMEndpoints messages */ + /** + * `true` if we want to verify endpoints in TMEndpoints messages + */ bool verifyEndpoints = true; //-------------------------------------------------------------------------- - /** Create a configuration with default values. */ + /** + * Create a configuration with default values. + */ Config(); - /** Returns a suitable value for outPeers according to the rules. */ + /** + * Returns a suitable value for outPeers according to the rules. + */ [[nodiscard]] std::size_t calcOutPeers() const; - /** Adjusts the values so they follow the business rules. */ + /** + * Adjusts the values so they follow the business rules. + */ void applyTuning(); - /** Write the configuration into a property stream */ + /** + * Write the configuration into a property stream + */ void onWrite(beast::PropertyStream::Map& map) const; - /** Make PeerFinder::Config from configuration parameters + /** + * Make PeerFinder::Config from configuration parameters * @param config server's configuration * @param port server's listening port * @param validationPublicKey true if validation public key is not empty @@ -111,7 +141,9 @@ struct Config //------------------------------------------------------------------------------ -/** Describes a connectable peer address along with some metadata. */ +/** + * Describes a connectable peer address along with some metadata. + */ struct Endpoint { Endpoint() = default; @@ -128,12 +160,16 @@ operator<(Endpoint const& lhs, Endpoint const& rhs) return lhs.address < rhs.address; } -/** A set of Endpoint used for connecting. */ +/** + * A set of Endpoint used for connecting. + */ using Endpoints = std::vector; //------------------------------------------------------------------------------ -/** Possible results from activating a slot. */ +/** + * Possible results from activating a slot. + */ enum class Result { InboundDisabled, DuplicatePeer, IpLimitExceeded, Full, Success }; /** @@ -170,57 +206,70 @@ to_string(Result result) noexcept return "unknown"; } -/** Maintains a set of IP addresses used for getting into the network. */ +/** + * Maintains a set of IP addresses used for getting into the network. + */ class Manager : public beast::PropertyStream::Source { protected: Manager() noexcept; public: - /** Destroy the object. - Any pending source fetch operations are aborted. - There may be some listener calls made before the - destructor returns. - */ + /** + * Destroy the object. + * Any pending source fetch operations are aborted. + * There may be some listener calls made before the + * destructor returns. + */ ~Manager() override = default; - /** Set the configuration for the manager. - The new settings will be applied asynchronously. - Thread safety: - Can be called from any threads at any time. - */ + /** + * Set the configuration for the manager. + * The new settings will be applied asynchronously. + * Thread safety: + * Can be called from any threads at any time. + */ virtual void setConfig(Config const& config) = 0; - /** Transition to the started state, synchronously. */ + /** + * Transition to the started state, synchronously. + */ virtual void start() = 0; - /** Transition to the stopped state, synchronously. */ + /** + * Transition to the stopped state, synchronously. + */ virtual void stop() = 0; - /** Returns the configuration for the manager. */ + /** + * Returns the configuration for the manager. + */ virtual Config config() = 0; - /** Add a peer that should always be connected. - This is useful for maintaining a private cluster of peers. - The string is the name as specified in the configuration - file, along with the set of corresponding IP addresses. - */ + /** + * Add a peer that should always be connected. + * This is useful for maintaining a private cluster of peers. + * The string is the name as specified in the configuration + * file, along with the set of corresponding IP addresses. + */ virtual void addFixedPeer(std::string_view name, std::vector const& addresses) = 0; - /** Add a set of strings as fallback IP::Endpoint sources. - @param name A label used for diagnostics. - */ + /** + * Add a set of strings as fallback IP::Endpoint sources. + * @param name A label used for diagnostics. + */ virtual void addFallbackStrings(std::string const& name, std::vector const& strings) = 0; - /** Add a URL as a fallback location to obtain IP::Endpoint sources. - @param name A label used for diagnostics. - */ + /** + * Add a URL as a fallback location to obtain IP::Endpoint sources. + * @param name A label used for diagnostics. + */ /* VFALCO NOTE Unimplemented virtual void addFallbackURL (std::string const& name, std::string const& url) = 0; @@ -228,38 +277,47 @@ public: //-------------------------------------------------------------------------- - /** Create a new inbound slot with the specified remote endpoint. - If nullptr is returned, then the slot could not be assigned. - Usually this is because of a detected self-connection. - */ + /** + * Create a new inbound slot with the specified remote endpoint. + * If nullptr is returned, then the slot could not be assigned. + * Usually this is because of a detected self-connection. + */ virtual std::pair, Result> newInboundSlot( beast::IP::Endpoint const& localEndpoint, beast::IP::Endpoint const& remoteEndpoint) = 0; - /** Create a new outbound slot with the specified remote endpoint. - If nullptr is returned, then the slot could not be assigned. - Usually this is because of a duplicate connection. - */ + /** + * Create a new outbound slot with the specified remote endpoint. + * If nullptr is returned, then the slot could not be assigned. + * Usually this is because of a duplicate connection. + */ virtual std::pair, Result> newOutboundSlot(beast::IP::Endpoint const& remoteEndpoint) = 0; - /** Called when mtENDPOINTS is received. */ + /** + * Called when mtENDPOINTS is received. + */ virtual void onEndpoints(std::shared_ptr const& slot, Endpoints const& endpoints) = 0; - /** Called when the slot is closed. - This always happens when the socket is closed, unless the socket - was canceled. - */ + /** + * Called when the slot is closed. + * This always happens when the socket is closed, unless the socket + * was canceled. + */ virtual void onClosed(std::shared_ptr const& slot) = 0; - /** Called when an outbound connection is deemed to have failed */ + /** + * Called when an outbound connection is deemed to have failed + */ virtual void onFailure(std::shared_ptr const& slot) = 0; - /** Called when we received redirect IPs from a busy peer. */ + /** + * Called when we received redirect IPs from a busy peer. + */ virtual void onRedirects( boost::asio::ip::tcp::endpoint const& remoteAddress, @@ -267,34 +325,42 @@ public: //-------------------------------------------------------------------------- - /** Called when an outbound connection attempt succeeds. - The local endpoint must be valid. If the caller receives an error - when retrieving the local endpoint from the socket, it should - proceed as if the connection attempt failed by calling on_closed - instead of on_connected. - @return `true` if the connection should be kept - */ + /** + * Called when an outbound connection attempt succeeds. + * The local endpoint must be valid. If the caller receives an error + * when retrieving the local endpoint from the socket, it should + * proceed as if the connection attempt failed by calling on_closed + * instead of on_connected. + * @return `true` if the connection should be kept + */ virtual bool onConnected(std::shared_ptr const& slot, beast::IP::Endpoint const& localEndpoint) = 0; - /** Request an active slot type. */ + /** + * Request an active slot type. + */ virtual Result activate(std::shared_ptr const& slot, PublicKey const& key, bool reserved) = 0; - /** Returns a set of endpoints suitable for redirection. */ + /** + * Returns a set of endpoints suitable for redirection. + */ virtual std::vector redirect(std::shared_ptr const& slot) = 0; - /** Return a set of addresses we should connect to. */ + /** + * Return a set of addresses we should connect to. + */ virtual std::vector autoconnect() = 0; virtual std::vector, std::vector>> buildEndpointsForPeers() = 0; - /** Perform periodic activity. - This should be called once per second. - */ + /** + * Perform periodic activity. + * This should be called once per second. + */ virtual void oncePerSecond() = 0; }; diff --git a/src/xrpld/peerfinder/Slot.h b/src/xrpld/peerfinder/Slot.h index f43b7d1009..9db39ac94c 100644 --- a/src/xrpld/peerfinder/Slot.h +++ b/src/xrpld/peerfinder/Slot.h @@ -9,7 +9,9 @@ namespace xrpl::PeerFinder { -/** Properties and state associated with a peer to peer overlay connection. */ +/** + * Properties and state associated with a peer to peer overlay connection. + */ class Slot { public: @@ -19,42 +21,53 @@ public: virtual ~Slot() = 0; - /** Returns `true` if this is an inbound connection. */ + /** + * Returns `true` if this is an inbound connection. + */ [[nodiscard]] virtual bool inbound() const = 0; - /** Returns `true` if this is a fixed connection. - A connection is fixed if its remote endpoint is in the list of - remote endpoints for fixed connections. - */ + /** + * Returns `true` if this is a fixed connection. + * A connection is fixed if its remote endpoint is in the list of + * remote endpoints for fixed connections. + */ [[nodiscard]] virtual bool fixed() const = 0; - /** Returns `true` if this is a reserved connection. - It might be a cluster peer, or a peer with a reservation. - This is only known after then handshake completes. + /** + * Returns `true` if this is a reserved connection. + * It might be a cluster peer, or a peer with a reservation. + * This is only known after then handshake completes. */ [[nodiscard]] virtual bool reserved() const = 0; - /** Returns the state of the connection. */ + /** + * Returns the state of the connection. + */ [[nodiscard]] virtual State state() const = 0; - /** The remote endpoint of socket. */ + /** + * The remote endpoint of socket. + */ [[nodiscard]] virtual beast::IP::Endpoint const& remoteEndpoint() const = 0; - /** The local endpoint of the socket, when known. */ + /** + * The local endpoint of the socket, when known. + */ [[nodiscard]] virtual std::optional const& localEndpoint() const = 0; [[nodiscard]] virtual std::optional listeningPort() const = 0; - /** The peer's public key, when known. - The public key is established when the handshake is complete. - */ + /** + * The peer's public key, when known. + * The public key is established when the handshake is complete. + */ [[nodiscard]] virtual std::optional const& publicKey() const = 0; }; diff --git a/src/xrpld/peerfinder/detail/Bootcache.h b/src/xrpld/peerfinder/detail/Bootcache.h index cee03fa322..c84fed42c7 100644 --- a/src/xrpld/peerfinder/detail/Bootcache.h +++ b/src/xrpld/peerfinder/detail/Bootcache.h @@ -16,21 +16,22 @@ namespace xrpl::PeerFinder { -/** Stores IP addresses useful for gaining initial connections. - - This is one of the caches that is consulted when additional outgoing - connections are needed. Along with the address, each entry has this - additional metadata: - - Valence - A signed integer which represents the number of successful - consecutive connection attempts when positive, and the number of - failed consecutive connection attempts when negative. - - When choosing addresses from the boot cache for the purpose of - establishing outgoing connections, addresses are ranked in decreasing - order of high uptime, with valence as the tie breaker. -*/ +/** + * Stores IP addresses useful for gaining initial connections. + * + * This is one of the caches that is consulted when additional outgoing + * connections are needed. Along with the address, each entry has this + * additional metadata: + * + * Valence + * A signed integer which represents the number of successful + * consecutive connection attempts when positive, and the number of + * failed consecutive connection attempts when negative. + * + * When choosing addresses from the boot cache for the purpose of + * establishing outgoing connections, addresses are ranked in decreasing + * order of high uptime, with valence as the tie breaker. + */ class Bootcache { private: @@ -107,15 +108,21 @@ public: ~Bootcache(); - /** Returns `true` if the cache is empty. */ + /** + * Returns `true` if the cache is empty. + */ [[nodiscard]] bool empty() const; - /** Returns the number of entries in the cache. */ + /** + * Returns the number of entries in the cache. + */ [[nodiscard]] map_type::size_type size() const; - /** IP::Endpoint iterators that traverse in decreasing valence. */ + /** + * IP::Endpoint iterators that traverse in decreasing valence. + */ /** @{ */ [[nodiscard]] const_iterator begin() const; @@ -129,31 +136,45 @@ public: clear(); /** @} */ - /** Load the persisted data from the Store into the container. */ + /** + * Load the persisted data from the Store into the container. + */ void load(); - /** Add a newly-learned address to the cache. */ + /** + * Add a newly-learned address to the cache. + */ bool insert(beast::IP::Endpoint const& endpoint); - /** Add a staticallyconfigured address to the cache. */ + /** + * Add a staticallyconfigured address to the cache. + */ bool insertStatic(beast::IP::Endpoint const& endpoint); - /** Called when an outbound connection handshake completes. */ + /** + * Called when an outbound connection handshake completes. + */ void onSuccess(beast::IP::Endpoint const& endpoint); - /** Called when an outbound connection attempt fails to handshake. */ + /** + * Called when an outbound connection attempt fails to handshake. + */ void onFailure(beast::IP::Endpoint const& endpoint); - /** Stores the cache in the persistent database on a timer. */ + /** + * Stores the cache in the persistent database on a timer. + */ void periodicActivity(); - /** Write the cache state to the property stream. */ + /** + * Write the cache state to the property stream. + */ void onWrite(beast::PropertyStream::Map& map); diff --git a/src/xrpld/peerfinder/detail/Checker.h b/src/xrpld/peerfinder/detail/Checker.h index 208bad390c..28ec83adb1 100644 --- a/src/xrpld/peerfinder/detail/Checker.h +++ b/src/xrpld/peerfinder/detail/Checker.h @@ -13,7 +13,9 @@ namespace xrpl::PeerFinder { -/** Tests remote listening sockets to make sure they are connectable. */ +/** + * Tests remote listening sockets to make sure they are connectable. + */ template class Checker { @@ -70,31 +72,36 @@ private: public: explicit Checker(boost::asio::io_context& ioContext); - /** Destroy the service. - Any pending I/O operations will be canceled. This call blocks until - all pending operations complete (either with success or with - operation_aborted) and the associated thread and io_context have - no more work remaining. - */ + /** + * Destroy the service. + * Any pending I/O operations will be canceled. This call blocks until + * all pending operations complete (either with success or with + * operation_aborted) and the associated thread and io_context have + * no more work remaining. + */ ~Checker(); - /** Stop the service. - Pending I/O operations will be canceled. - This issues cancel orders for all pending I/O operations and then - returns immediately. Handlers will receive operation_aborted errors, - or if they were already queued they will complete normally. - */ + /** + * Stop the service. + * Pending I/O operations will be canceled. + * This issues cancel orders for all pending I/O operations and then + * returns immediately. Handlers will receive operation_aborted errors, + * or if they were already queued they will complete normally. + */ void stop(); - /** Block until all pending I/O completes. */ + /** + * Block until all pending I/O completes. + */ void wait(); - /** Performs an async connection test on the specified endpoint. - The port must be non-zero. Note that the execution guarantees - offered by asio handlers are NOT enforced. - */ + /** + * Performs an async connection test on the specified endpoint. + * The port must be non-zero. Note that the execution guarantees + * offered by asio handlers are NOT enforced. + */ template void asyncConnect(beast::IP::Endpoint const& endpoint, Handler&& handler); diff --git a/src/xrpld/peerfinder/detail/Counts.h b/src/xrpld/peerfinder/detail/Counts.h index 0d8bf1c56e..c90598c1a1 100644 --- a/src/xrpld/peerfinder/detail/Counts.h +++ b/src/xrpld/peerfinder/detail/Counts.h @@ -13,28 +13,38 @@ namespace xrpl::PeerFinder { -/** Direction of a slot count adjustment. */ +/** + * Direction of a slot count adjustment. + */ enum class CountAdjustment : int { Decrement = -1, Increment = 1 }; -/** Manages the count of available connections for the various slots. */ +/** + * Manages the count of available connections for the various slots. + */ class Counts { public: - /** Adds the slot state and properties to the slot counts. */ + /** + * Adds the slot state and properties to the slot counts. + */ void add(Slot const& s) { adjust(s, CountAdjustment::Increment); } - /** Removes the slot state and properties from the slot counts. */ + /** + * Removes the slot state and properties from the slot counts. + */ void remove(Slot const& s) { adjust(s, CountAdjustment::Decrement); } - /** Returns `true` if the slot can become active. */ + /** + * Returns `true` if the slot can become active. + */ [[nodiscard]] bool canActivate(Slot const& s) const { @@ -52,7 +62,9 @@ public: return outActive_ < outMax_; } - /** Returns the number of attempts needed to bring us to the max. */ + /** + * Returns the number of attempts needed to bring us to the max. + */ [[nodiscard]] std::size_t attemptsNeeded() const { @@ -61,37 +73,46 @@ public: return Tuning::kMaxConnectAttempts - attempts_; } - /** Returns the number of outbound connection attempts. */ + /** + * Returns the number of outbound connection attempts. + */ [[nodiscard]] std::size_t attempts() const { return attempts_; } - /** Returns the total number of outbound slots. */ + /** + * Returns the total number of outbound slots. + */ [[nodiscard]] int outMax() const { return outMax_; } - /** Returns the number of outbound peers assigned an open slot. - Fixed peers do not count towards outbound slots used. - */ + /** + * Returns the number of outbound peers assigned an open slot. + * Fixed peers do not count towards outbound slots used. + */ [[nodiscard]] int outActive() const { return outActive_; } - /** Returns the number of fixed connections. */ + /** + * Returns the number of fixed connections. + */ [[nodiscard]] std::size_t fixed() const { return fixed_; } - /** Returns the number of active fixed connections. */ + /** + * Returns the number of active fixed connections. + */ [[nodiscard]] std::size_t fixedActive() const { @@ -100,7 +121,9 @@ public: //-------------------------------------------------------------------------- - /** Called when the config is set or changed. */ + /** + * Called when the config is set or changed. + */ void onConfig(Config const& config) { @@ -109,51 +132,64 @@ public: inMax_ = config.inPeers; } - /** Returns the number of accepted connections that haven't handshaked. */ + /** + * Returns the number of accepted connections that haven't handshaked. + */ [[nodiscard]] int acceptCount() const { return acceptCount_; } - /** Returns the number of connection attempts currently active. */ + /** + * Returns the number of connection attempts currently active. + */ [[nodiscard]] int connectCount() const { return attempts_; } - /** Returns the number of connections that are gracefully closing. */ + /** + * Returns the number of connections that are gracefully closing. + */ [[nodiscard]] int closingCount() const { return closingCount_; } - /** Returns the total number of inbound slots. */ + /** + * Returns the total number of inbound slots. + */ [[nodiscard]] int inMax() const { return inMax_; } - /** Returns the number of inbound peers assigned an open slot. */ + /** + * Returns the number of inbound peers assigned an open slot. + */ [[nodiscard]] int inboundActive() const { return inActive_; } - /** Returns the total number of active peers excluding fixed peers. */ + /** + * Returns the total number of active peers excluding fixed peers. + */ [[nodiscard]] int totalActive() const { return inActive_ + outActive_; } - /** Returns the number of unused inbound slots. - Fixed peers do not deduct from inbound slots or count towards totals. - */ + /** + * Returns the number of unused inbound slots. + * Fixed peers do not deduct from inbound slots or count towards totals. + */ [[nodiscard]] int inboundSlotsFree() const { @@ -162,9 +198,10 @@ public: return 0; } - /** Returns the number of unused outbound slots. - Fixed peers do not deduct from outbound slots or count towards totals. - */ + /** + * Returns the number of unused outbound slots. + * Fixed peers do not deduct from outbound slots or count towards totals. + */ [[nodiscard]] int outboundSlotsFree() const { @@ -175,7 +212,8 @@ public: //-------------------------------------------------------------------------- - /** Returns true if the slot logic considers us "connected" to the network. + /** + * Returns true if the slot logic considers us "connected" to the network. */ [[nodiscard]] bool isConnectedToNetwork() const @@ -189,7 +227,9 @@ public: return outMax_ <= 0; } - /** Output statistics. */ + /** + * Output statistics. + */ void onWrite(beast::PropertyStream::Map& map) const { @@ -203,7 +243,9 @@ public: map["total"] = active_; } - /** Records the state for diagnostics. */ + /** + * Records the state for diagnostics. + */ [[nodiscard]] std::string stateString() const { @@ -215,7 +257,9 @@ public: //-------------------------------------------------------------------------- private: - /** Increments or decrements a counter based on the adjustment direction. */ + /** + * Increments or decrements a counter based on the adjustment direction. + */ template static void adjustCounter(T& counter, CountAdjustment dir) @@ -295,31 +339,49 @@ private: } private: - /** Outbound connection attempts. */ + /** + * Outbound connection attempts. + */ int attempts_{0}; - /** Active connections, including fixed and reserved. */ + /** + * Active connections, including fixed and reserved. + */ std::size_t active_{0}; - /** Total number of inbound slots. */ + /** + * Total number of inbound slots. + */ std::size_t inMax_{0}; - /** Number of inbound slots assigned to active peers. */ + /** + * Number of inbound slots assigned to active peers. + */ std::size_t inActive_{0}; - /** Maximum desired outbound slots. */ + /** + * Maximum desired outbound slots. + */ std::size_t outMax_{0}; - /** Active outbound slots. */ + /** + * Active outbound slots. + */ std::size_t outActive_{0}; - /** Fixed connections. */ + /** + * Fixed connections. + */ std::size_t fixed_{0}; - /** Active fixed connections. */ + /** + * Active fixed connections. + */ std::size_t fixedActive_{0}; - /** Reserved connections. */ + /** + * Reserved connections. + */ std::size_t reserved_{0}; // Number of inbound connections that are diff --git a/src/xrpld/peerfinder/detail/Fixed.h b/src/xrpld/peerfinder/detail/Fixed.h index 3319994251..24d54775ef 100644 --- a/src/xrpld/peerfinder/detail/Fixed.h +++ b/src/xrpld/peerfinder/detail/Fixed.h @@ -9,7 +9,9 @@ namespace xrpl::PeerFinder { -/** Metadata for a Fixed slot. */ +/** + * Metadata for a Fixed slot. + */ class Fixed { public: @@ -19,14 +21,18 @@ public: Fixed(Fixed const&) = default; - /** Returns the time after which we should allow a connection attempt. */ + /** + * Returns the time after which we should allow a connection attempt. + */ [[nodiscard]] clock_type::time_point const& when() const { return when_; } - /** Updates metadata to reflect a failed connection. */ + /** + * Updates metadata to reflect a failed connection. + */ void failure(clock_type::time_point const& now) { @@ -34,7 +40,9 @@ public: when_ = now + std::chrono::minutes(Tuning::kConnectionBackoff[failures_]); } - /** Updates metadata to reflect a successful connection. */ + /** + * Updates metadata to reflect a successful connection. + */ void success(clock_type::time_point const& now) { diff --git a/src/xrpld/peerfinder/detail/Handouts.h b/src/xrpld/peerfinder/detail/Handouts.h index faadb51fd2..757f1a8e1b 100644 --- a/src/xrpld/peerfinder/detail/Handouts.h +++ b/src/xrpld/peerfinder/detail/Handouts.h @@ -17,10 +17,11 @@ namespace xrpl::PeerFinder { namespace detail { -/** Try to insert one object in the target. - When an item is handed out it is moved to the end of the container. - @return The number of objects inserted -*/ +/** + * Try to insert one object in the target. + * When an item is handed out it is moved to the end of the container. + * @return The number of objects inserted + */ // VFALCO TODO specialization that handles std::list for SequenceContainer // using splice for optimization over erase/push_back // @@ -43,10 +44,11 @@ handoutOne(Target& t, HopContainer& h) } // namespace detail -/** Distributes objects to targets according to business rules. - A best effort is made to evenly distribute items in the sequence - container list into the target sequence list. -*/ +/** + * Distributes objects to targets according to business rules. + * A best effort is made to evenly distribute items in the sequence + * container list into the target sequence list. + */ template void handout(TargetFwdIter first, TargetFwdIter last, SeqFwdIter seqFirst, SeqFwdIter seqLast) @@ -77,9 +79,10 @@ handout(TargetFwdIter first, TargetFwdIter last, SeqFwdIter seqFirst, SeqFwdIter //------------------------------------------------------------------------------ -/** Receives handouts for redirecting a connection. - An incoming connection request is redirected when we are full on slots. -*/ +/** + * Receives handouts for redirecting a connection. + * An incoming connection request is redirected when we are full on slots. + */ class RedirectHandouts { public: @@ -163,7 +166,9 @@ RedirectHandouts::tryInsert(Endpoint const& ep) //------------------------------------------------------------------------------ -/** Receives endpoints for a slot during periodic handouts. */ +/** + * Receives endpoints for a slot during periodic handouts. + */ class SlotHandouts { public: @@ -247,7 +252,9 @@ SlotHandouts::tryInsert(Endpoint const& ep) //------------------------------------------------------------------------------ -/** Receives handouts for making automatic connections. */ +/** + * Receives handouts for making automatic connections. + */ class ConnectHandouts { public: diff --git a/src/xrpld/peerfinder/detail/Livecache.h b/src/xrpld/peerfinder/detail/Livecache.h index 1dcc8f6daf..2015098847 100644 --- a/src/xrpld/peerfinder/detail/Livecache.h +++ b/src/xrpld/peerfinder/detail/Livecache.h @@ -55,10 +55,11 @@ protected: boost::intrusive::make_list>::type; public: - /** A list of Endpoint at the same hops - This is a lightweight wrapper around a reference to the underlying - container. - */ + /** + * A list of Endpoint at the same hops + * This is a lightweight wrapper around a reference to the underlying + * container. + */ template class Hop { @@ -169,18 +170,19 @@ protected: //------------------------------------------------------------------------------ -/** The Livecache holds the short-lived relayed Endpoint messages. - - Since peers only advertise themselves when they have open slots, - we want these messages to expire rather quickly after the peer becomes - full. - - Addresses added to the cache are not connection-tested to see if - they are connectable (with one small exception regarding neighbors). - Therefore, these addresses are not suitable for persisting across - launches or for bootstrapping, because they do not have verifiable - and locally observed uptime and connectability information. -*/ +/** + * The Livecache holds the short-lived relayed Endpoint messages. + * + * Since peers only advertise themselves when they have open slots, + * we want these messages to expire rather quickly after the peer becomes + * full. + * + * Addresses added to the cache are not connection-tested to see if + * they are connectable (with one small exception regarding neighbors). + * Therefore, these addresses are not suitable for persisting across + * launches or for bootstrapping, because they do not have verifiable + * and locally observed uptime and connectability information. + */ template > class Livecache : protected detail::LivecacheBase { @@ -198,7 +200,9 @@ private: public: using allocator_type = Allocator; - /** Create the cache. */ + /** + * Create the cache. + */ Livecache(clock_type& clock, beast::Journal journal, Allocator alloc = Allocator()); // @@ -318,7 +322,9 @@ public: return const_reverse_iterator(lists_.crend(), Transform()); } - /** Shuffle each hop list. */ + /** + * Shuffle each hop list. + */ void shuffle(); @@ -343,29 +349,39 @@ public: Histogram hist_{}; } hops; - /** Returns `true` if the cache is empty. */ + /** + * Returns `true` if the cache is empty. + */ [[nodiscard]] bool empty() const { return cache_.empty(); } - /** Returns the number of entries in the cache. */ + /** + * Returns the number of entries in the cache. + */ cache_type::size_type size() const { return cache_.size(); } - /** Erase entries whose time has expired. */ + /** + * Erase entries whose time has expired. + */ void expire(); - /** Creates or updates an existing Element based on a new message. */ + /** + * Creates or updates an existing Element based on a new message. + */ void insert(Endpoint const& ep); - /** Output statistics. */ + /** + * Output statistics. + */ void onWrite(beast::PropertyStream::Map& map); }; diff --git a/src/xrpld/peerfinder/detail/Logic.h b/src/xrpld/peerfinder/detail/Logic.h index 8ea348f560..b0047b1ea3 100644 --- a/src/xrpld/peerfinder/detail/Logic.h +++ b/src/xrpld/peerfinder/detail/Logic.h @@ -41,10 +41,11 @@ namespace xrpl::PeerFinder { -/** The Logic for maintaining the list of Slot addresses. - We keep this in a separate class so it can be instantiated - for unit tests. -*/ +/** + * The Logic for maintaining the list of Slot addresses. + * We keep this in a separate class so it can be instantiated + * for unit tests. + */ template class Logic { @@ -127,12 +128,13 @@ public: bootcache.load(); } - /** Stop the logic. - This will cancel the current fetch and set the stopping flag - to `true` to prevent further fetches. - Thread safety: - Safe to call from any thread. - */ + /** + * Stop the logic. + * This will cancel the current fetch and set the stopping flag + * to `true` to prevent further fetches. + * Thread safety: + * Safe to call from any thread. + */ void stop() { @@ -448,10 +450,11 @@ public: return Result::Success; } - /** Return a list of addresses suitable for redirection. - This is a legacy function, redirects should be returned in - the HTTP handshake and not via TMEndpoints. - */ + /** + * Return a list of addresses suitable for redirection. + * This is a legacy function, redirects should be returned in + * the HTTP handshake and not via TMEndpoints. + */ std::vector redirect(SlotImp::ptr const& slot) { @@ -462,9 +465,10 @@ public: return std::move(h.list()); } - /** Create new outbound connection attempts as needed. - This implements PeerFinder's "Outbound Connection Strategy" - */ + /** + * Create new outbound connection attempts as needed. + * This implements PeerFinder's "Outbound Connection Strategy" + */ // VFALCO TODO This should add the returned addresses to the // squelch list in one go once the list is built, // rather than having each module add to the squelch list. @@ -989,7 +993,9 @@ public: // //-------------------------------------------------------------------------- - /** Adds eligible Fixed addresses for outbound attempts. */ + /** + * Adds eligible Fixed addresses for outbound attempts. + */ template void getFixed(std::size_t needed, Container& c, ConnectHandouts::Squelches& squelches) diff --git a/src/xrpld/peerfinder/detail/SlotImp.h b/src/xrpld/peerfinder/detail/SlotImp.h index cf1915268f..898941b157 100644 --- a/src/xrpld/peerfinder/detail/SlotImp.h +++ b/src/xrpld/peerfinder/detail/SlotImp.h @@ -135,14 +135,17 @@ public: public: explicit RecentT(clock_type& clock); - /** Called for each valid endpoint received for a slot. - We also insert messages that we send to the slot to prevent - sending a slot the same address too frequently. - */ + /** + * Called for each valid endpoint received for a slot. + * We also insert messages that we send to the slot to prevent + * sending a slot the same address too frequently. + */ void insert(beast::IP::Endpoint const& ep, std::uint32_t hops); - /** Returns `true` if we should not send endpoint to the slot. */ + /** + * Returns `true` if we should not send endpoint to the slot. + */ bool filter(beast::IP::Endpoint const& ep, std::uint32_t hops); diff --git a/src/xrpld/peerfinder/detail/Source.h b/src/xrpld/peerfinder/detail/Source.h index cf8920e056..b205dc8dfb 100644 --- a/src/xrpld/peerfinder/detail/Source.h +++ b/src/xrpld/peerfinder/detail/Source.h @@ -10,18 +10,21 @@ namespace xrpl::PeerFinder { -/** A static or dynamic source of peer addresses. - These are used as fallbacks when we are bootstrapping and don't have - a local cache, or when none of our addresses are functioning. Typically - sources will represent things like static text in the config file, a - separate local file with addresses, or a remote HTTPS URL that can - be updated automatically. Another solution is to use a custom DNS server - that hands out peer IP addresses when name lookups are performed. -*/ +/** + * A static or dynamic source of peer addresses. + * These are used as fallbacks when we are bootstrapping and don't have + * a local cache, or when none of our addresses are functioning. Typically + * sources will represent things like static text in the config file, a + * separate local file with addresses, or a remote HTTPS URL that can + * be updated automatically. Another solution is to use a custom DNS server + * that hands out peer IP addresses when name lookups are performed. + */ class Source { public: - /** The results of a fetch. */ + /** + * The results of a fetch. + */ struct Results { explicit Results() = default; diff --git a/src/xrpld/peerfinder/detail/SourceStrings.h b/src/xrpld/peerfinder/detail/SourceStrings.h index 618970fa03..b79cf0df03 100644 --- a/src/xrpld/peerfinder/detail/SourceStrings.h +++ b/src/xrpld/peerfinder/detail/SourceStrings.h @@ -8,7 +8,9 @@ namespace xrpl::PeerFinder { -/** Provides addresses from a static set of strings. */ +/** + * Provides addresses from a static set of strings. + */ class SourceStrings : public Source { public: diff --git a/src/xrpld/peerfinder/detail/Store.h b/src/xrpld/peerfinder/detail/Store.h index 570dba0523..9393ef6c2b 100644 --- a/src/xrpld/peerfinder/detail/Store.h +++ b/src/xrpld/peerfinder/detail/Store.h @@ -8,7 +8,9 @@ namespace xrpl::PeerFinder { -/** Abstract persistence for PeerFinder data. */ +/** + * Abstract persistence for PeerFinder data. + */ class Store { public: diff --git a/src/xrpld/peerfinder/detail/StoreSqdb.h b/src/xrpld/peerfinder/detail/StoreSqdb.h index f868e89ff7..b17d2fdc5b 100644 --- a/src/xrpld/peerfinder/detail/StoreSqdb.h +++ b/src/xrpld/peerfinder/detail/StoreSqdb.h @@ -16,7 +16,9 @@ namespace xrpl::PeerFinder { -/** Database persistence for PeerFinder using SQLite */ +/** + * Database persistence for PeerFinder using SQLite + */ class StoreSqdb : public Store { private: diff --git a/src/xrpld/peerfinder/detail/Tuning.h b/src/xrpld/peerfinder/detail/Tuning.h index 1bf9df382e..ea4637dd9d 100644 --- a/src/xrpld/peerfinder/detail/Tuning.h +++ b/src/xrpld/peerfinder/detail/Tuning.h @@ -5,7 +5,9 @@ #include #include -/** Heuristically tuned constants. */ +/** + * Heuristically tuned constants. + */ /** @{ */ namespace xrpl::PeerFinder::Tuning { @@ -15,32 +17,41 @@ namespace xrpl::PeerFinder::Tuning { // //--------------------------------------------------------- -/** Time to wait between making batches of connection attempts */ +/** + * Time to wait between making batches of connection attempts + */ static constexpr auto kSecondsPerConnect = 10; -/** Maximum number of simultaneous connection attempts. */ +/** + * Maximum number of simultaneous connection attempts. + */ static constexpr auto kMaxConnectAttempts = 20; -/** The percentage of total peer slots that are outbound. - The number of outbound peers will be the larger of the - minOutCount and outPercent * Config::maxPeers specially - rounded. -*/ +/** + * The percentage of total peer slots that are outbound. + * The number of outbound peers will be the larger of the + * minOutCount and outPercent * Config::maxPeers specially + * rounded. + */ static constexpr auto kOutPercent = 15; -/** A hard minimum on the number of outgoing connections. - This is enforced outside the Logic, so that the unit test - can use any settings it wants. -*/ +/** + * A hard minimum on the number of outgoing connections. + * This is enforced outside the Logic, so that the unit test + * can use any settings it wants. + */ static constexpr auto kMinOutCount = 10; -/** The default value of Config::maxPeers. */ +/** + * The default value of Config::maxPeers. + */ static constexpr auto kDefaultMaxPeers = 21; -/** Max redirects we will accept from one connection. - Redirects are limited for security purposes, to prevent - the address caches from getting flooded. -*/ +/** + * Max redirects we will accept from one connection. + * Redirects are limited for security purposes, to prevent + * the address caches from getting flooded. + */ static constexpr auto kMaxRedirects = 30; //------------------------------------------------------------------------------ diff --git a/src/xrpld/peerfinder/detail/iosformat.h b/src/xrpld/peerfinder/detail/iosformat.h index a0b9ff537a..46c69ef602 100644 --- a/src/xrpld/peerfinder/detail/iosformat.h +++ b/src/xrpld/peerfinder/detail/iosformat.h @@ -12,7 +12,9 @@ namespace beast { // A collection of handy stream manipulators and // functions to produce nice looking log output. -/** Left justifies a field at the specified width. */ +/** + * Left justifies a field at the specified width. + */ struct Leftw { explicit Leftw(int width) : width(width) @@ -29,7 +31,9 @@ struct Leftw } }; -/** Produce a section heading and fill the rest of the line with dashes. */ +/** + * Produce a section heading and fill the rest of the line with dashes. + */ template std::basic_string heading(std::basic_string title, int width = 80, CharT fill = CharT('-')) @@ -40,7 +44,9 @@ heading(std::basic_string title, int width = 80, CharT return title; } -/** Produce a dashed line separator, with a specified or default size. */ +/** + * Produce a dashed line separator, with a specified or default size. + */ struct Divider { using CharT = char; @@ -58,7 +64,9 @@ struct Divider } }; -/** Creates a padded field with an optional fill character. */ +/** + * Creates a padded field with an optional fill character. + */ struct Fpad { explicit Fpad(int width, int pad = 0, char fill = ' ') : width(width + pad), fill(fill) @@ -90,7 +98,9 @@ to_string(T const& t) } // namespace detail -/** Justifies a field at the specified width. */ +/** + * Justifies a field at the specified width. + */ /** @{ */ template < class CharT, diff --git a/src/xrpld/peerfinder/make_Manager.h b/src/xrpld/peerfinder/make_Manager.h index 1f3f226397..1c13d7a4ca 100644 --- a/src/xrpld/peerfinder/make_Manager.h +++ b/src/xrpld/peerfinder/make_Manager.h @@ -12,7 +12,9 @@ namespace xrpl::PeerFinder { -/** Create a new Manager. */ +/** + * Create a new Manager. + */ std::unique_ptr makeManager( boost::asio::io_context& ioContext, diff --git a/src/xrpld/perflog/detail/PerfLogImp.h b/src/xrpld/perflog/detail/PerfLogImp.h index 88bf473554..14477512ff 100644 --- a/src/xrpld/perflog/detail/PerfLogImp.h +++ b/src/xrpld/perflog/detail/PerfLogImp.h @@ -24,7 +24,9 @@ namespace xrpl::perf { -/** A box coupling data with a mutex for locking access to it. */ +/** + * A box coupling data with a mutex for locking access to it. + */ template struct Locked { diff --git a/src/xrpld/rpc/Context.h b/src/xrpld/rpc/Context.h index fe6bb81ce3..81ba068d8f 100644 --- a/src/xrpld/rpc/Context.h +++ b/src/xrpld/rpc/Context.h @@ -20,7 +20,9 @@ class LedgerMaster; namespace RPC { -/** The context of information needed to call an RPC. */ +/** + * The context of information needed to call an RPC. + */ struct Context { beast::Journal const j; diff --git a/src/xrpld/rpc/DeliveredAmount.h b/src/xrpld/rpc/DeliveredAmount.h index bba045d494..dc635c6861 100644 --- a/src/xrpld/rpc/DeliveredAmount.h +++ b/src/xrpld/rpc/DeliveredAmount.h @@ -24,14 +24,13 @@ struct JsonContext; struct Context; /** - Add a `delivered_amount` field to the `meta` input/output parameter. - The field is only added to successful payment and check cash transactions. - If a delivered amount field is available in the TxMeta parameter, that value - is used. Otherwise, the transaction's `Amount` field is used. If neither is - available, then the delivered amount is set to "unavailable". - - @{ + * Add a `delivered_amount` field to the `meta` input/output parameter. + * The field is only added to successful payment and check cash transactions. + * If a delivered amount field is available in the TxMeta parameter, that value + * is used. Otherwise, the transaction's `Amount` field is used. If neither is + * available, then the delivered amount is set to "unavailable". */ +/** @{ */ void insertDeliveredAmount( json::Value& meta, diff --git a/src/xrpld/rpc/MPTokenIssuanceID.h b/src/xrpld/rpc/MPTokenIssuanceID.h index cb2bfd1bdc..f56826bfb8 100644 --- a/src/xrpld/rpc/MPTokenIssuanceID.h +++ b/src/xrpld/rpc/MPTokenIssuanceID.h @@ -11,13 +11,12 @@ namespace xrpl::RPC { /** - Add a `mpt_issuance_id` field to the `meta` input/output parameter. - The field is only added to successful MPTokenIssuanceCreate transactions. - The mpt_issuance_id is parsed from the sequence and the issuer in the - MPTokenIssuance object. - - @{ + * Add a `mpt_issuance_id` field to the `meta` input/output parameter. + * The field is only added to successful MPTokenIssuanceCreate transactions. + * The mpt_issuance_id is parsed from the sequence and the issuer in the + * MPTokenIssuance object. */ +/** @{ */ bool canHaveMPTokenIssuanceID( std::shared_ptr const& serializedTx, diff --git a/src/xrpld/rpc/RPCCall.h b/src/xrpld/rpc/RPCCall.h index a06eca4413..a72b35e344 100644 --- a/src/xrpld/rpc/RPCCall.h +++ b/src/xrpld/rpc/RPCCall.h @@ -23,7 +23,9 @@ namespace xrpl { // // Improvements to be more strict and to provide better diagnostics are welcome. -/** Processes XRPL RPC calls. */ +/** + * Processes XRPL RPC calls. + */ namespace RPCCall { int @@ -54,7 +56,8 @@ rpcCmdToJson( unsigned int apiVersion, beast::Journal j); -/** Internal invocation of RPC client. +/** + * Internal invocation of RPC client. * Used by both xrpld command line as well as xrpld unit tests */ std::pair diff --git a/src/xrpld/rpc/RPCHandler.h b/src/xrpld/rpc/RPCHandler.h index d1cd54145d..fcd0f54265 100644 --- a/src/xrpld/rpc/RPCHandler.h +++ b/src/xrpld/rpc/RPCHandler.h @@ -12,7 +12,9 @@ namespace xrpl::RPC { struct JsonContext; -/** Execute an RPC command and store the results in a json::Value. */ +/** + * Execute an RPC command and store the results in a json::Value. + */ Status doCommand(RPC::JsonContext&, json::Value&); diff --git a/src/xrpld/rpc/RPCSub.h b/src/xrpld/rpc/RPCSub.h index 95206e5cf6..bf31536bc5 100644 --- a/src/xrpld/rpc/RPCSub.h +++ b/src/xrpld/rpc/RPCSub.h @@ -11,7 +11,9 @@ namespace xrpl { -/** Subscription object for JSON RPC. */ +/** + * Subscription object for JSON RPC. + */ class RPCSub : public InfoSub { public: diff --git a/src/xrpld/rpc/Role.h b/src/xrpld/rpc/Role.h index 660fb92c7c..2c2ae6b781 100644 --- a/src/xrpld/rpc/Role.h +++ b/src/xrpld/rpc/Role.h @@ -17,7 +17,8 @@ namespace xrpl { -/** Indicates the level of administrative permission to grant. +/** + * Indicates the level of administrative permission to grant. * IDENTIFIED role has unlimited resources but cannot perform some * RPC commands. * ADMIN role has unlimited resources and is able to perform all RPC @@ -25,14 +26,15 @@ namespace xrpl { */ enum class Role { GUEST, USER, IDENTIFIED, ADMIN, PROXY, FORBID }; -/** Return the allowed privilege role. - params must meet the requirements of the JSON-RPC - specification. It must be of type Object, containing the key params - which is an array with at least one object. Inside this object - are the optional keys 'admin_user' and 'admin_password' used to - validate the credentials. If user is non-blank, it's username - passed in the HTTP header by a secureGateway proxy. -*/ +/** + * Return the allowed privilege role. + * params must meet the requirements of the JSON-RPC + * specification. It must be of type Object, containing the key params + * which is an array with at least one object. Inside this object + * are the optional keys 'admin_user' and 'admin_password' used to + * validate the credentials. If user is non-blank, it's username + * passed in the HTTP header by a secureGateway proxy. + */ Role requestRole( Role const& required, diff --git a/src/xrpld/rpc/Status.h b/src/xrpld/rpc/Status.h index ff77f2ace7..dda1e89d31 100644 --- a/src/xrpld/rpc/Status.h +++ b/src/xrpld/rpc/Status.h @@ -13,14 +13,15 @@ namespace xrpl::RPC { -/** Status represents the results of an operation that might fail. - - It wraps the legacy codes TER and error_code_i, providing both a uniform - interface and a way to attach additional information to existing status - returns. - - A Status can also be used to fill a json::Value with a JSON-RPC 2.0 - error response: see http://www.jsonrpc.org/specification#error_object +/** + * Status represents the results of an operation that might fail. + * + * It wraps the legacy codes TER and error_code_i, providing both a uniform + * interface and a way to attach additional information to existing status + * returns. + * + * A Status can also be used to fill a json::Value with a JSON-RPC 2.0 + * error response: see http://www.jsonrpc.org/specification#error_object */ struct Status : public std::exception { @@ -61,21 +62,27 @@ public: [[nodiscard]] std::string codeString() const; - /** Returns true if the Status is *not* OK. */ + /** + * Returns true if the Status is *not* OK. + */ operator bool() const { return code_ != kOK; } - /** Returns true if the Status is OK. */ + /** + * Returns true if the Status is OK. + */ bool operator!() const { return !bool(*this); } - /** Returns the Status as a TER. - This may only be called if type() == Type::TER. */ + /** + * Returns the Status as a TER. + * This may only be called if type() == Type::TER. + */ [[nodiscard]] TER toTER() const { @@ -83,8 +90,10 @@ public: return TER::fromInt(code_); } - /** Returns the Status as an error_code_i. - This may only be called if type() == Type::ErrorCodeI. */ + /** + * Returns the Status as an error_code_i. + * This may only be called if type() == Type::ErrorCodeI. + */ [[nodiscard]] ErrorCodeI toErrorCode() const { @@ -92,7 +101,8 @@ public: return ErrorCodeI(code_); } - /** Apply the Status to a JsonObject + /** + * Apply the Status to a JsonObject */ void inject(json::Value& object) const @@ -116,7 +126,9 @@ public: return messages_; } - /** Return the first message, if any. */ + /** + * Return the first message, if any. + */ [[nodiscard]] std::string message() const; @@ -129,9 +141,11 @@ public: [[nodiscard]] std::string toString() const; - /** Fill a json::Value with an RPC 2.0 response. - If the Status is OK, fillJson has no effect. - Not currently used. */ + /** + * Fill a json::Value with an RPC 2.0 response. + * If the Status is OK, fillJson has no effect. + * Not currently used. + */ void fillJson(json::Value&); diff --git a/src/xrpld/rpc/detail/AssetCache.h b/src/xrpld/rpc/detail/AssetCache.h index 4b89487526..71a7c262d4 100644 --- a/src/xrpld/rpc/detail/AssetCache.h +++ b/src/xrpld/rpc/detail/AssetCache.h @@ -30,18 +30,19 @@ public: return ledger_; } - /** Find the trust lines associated with an account. - - @param accountID The account - @param direction Whether the account is an "outgoing" link on the path. - "Outgoing" is defined as the source account, or an account found via a - trustline that has rippling enabled on the @accountID's side. If an - account is "outgoing", all trust lines will be returned. If an account is - not "outgoing", then any trust lines that don't have rippling enabled are - not usable, so only return trust lines that have rippling enabled on - @accountID's side. - @return Returns a vector of the usable trust lines. - */ + /** + * Find the trust lines associated with an account. + * + * @param accountID The account + * @param direction Whether the account is an "outgoing" link on the path. + * "Outgoing" is defined as the source account, or an account found via a + * trustline that has rippling enabled on the @accountID's side. If an + * account is "outgoing", all trust lines will be returned. If an account is + * not "outgoing", then any trust lines that don't have rippling enabled are + * not usable, so only return trust lines that have rippling enabled on + * @accountID's side. + * @return Returns a vector of the usable trust lines. + */ std::shared_ptr> getRippleLines(AccountID const& accountID, LineDirection direction); diff --git a/src/xrpld/rpc/detail/Handler.cpp b/src/xrpld/rpc/detail/Handler.cpp index 23eb8fdeec..4f5ce34c1f 100644 --- a/src/xrpld/rpc/detail/Handler.cpp +++ b/src/xrpld/rpc/detail/Handler.cpp @@ -21,7 +21,9 @@ namespace xrpl::RPC { namespace { -/** Adjust an old-style handler to be call-by-reference. */ +/** + * Adjust an old-style handler to be call-by-reference. + */ template Handler::Method byRef(Function const& f) diff --git a/src/xrpld/rpc/detail/Handler.h b/src/xrpld/rpc/detail/Handler.h index 5e583aa5bb..37259c8648 100644 --- a/src/xrpld/rpc/detail/Handler.h +++ b/src/xrpld/rpc/detail/Handler.h @@ -47,7 +47,9 @@ struct Handler Handler const* getHandler(unsigned int version, bool betaEnabled, std::string const&); -/** Return a json::ValueType::Object with a single entry. */ +/** + * Return a json::ValueType::Object with a single entry. + */ template json::Value makeObjectValue(Value const& value, json::StaticString const& field = jss::message) @@ -57,7 +59,9 @@ makeObjectValue(Value const& value, json::StaticString const& field = jss::messa return result; } -/** Return names of all methods. */ +/** + * Return names of all methods. + */ std::set getHandlerNames(); diff --git a/src/xrpld/rpc/detail/PathRequest.h b/src/xrpld/rpc/detail/PathRequest.h index 64bc6ef181..d40d9c82d6 100644 --- a/src/xrpld/rpc/detail/PathRequest.h +++ b/src/xrpld/rpc/detail/PathRequest.h @@ -114,9 +114,10 @@ private: int const, std::function const&); - /** Finds and sets a PathSet in the JSON argument. - Returns false if the source currencies are invalid. - */ + /** + * Finds and sets a PathSet in the JSON argument. + * Returns false if the source currencies are invalid. + */ bool findPaths( std::shared_ptr const&, diff --git a/src/xrpld/rpc/detail/PathRequestManager.cpp b/src/xrpld/rpc/detail/PathRequestManager.cpp index 7013353cb1..4953634181 100644 --- a/src/xrpld/rpc/detail/PathRequestManager.cpp +++ b/src/xrpld/rpc/detail/PathRequestManager.cpp @@ -26,9 +26,10 @@ namespace xrpl { -/** Get the current AssetCache, updating it if necessary. - Get the correct ledger to use. -*/ +/** + * Get the current AssetCache, updating it if necessary. + * Get the correct ledger to use. + */ std::shared_ptr PathRequestManager::getAssetCache(std::shared_ptr const& ledger, bool authoritative) { diff --git a/src/xrpld/rpc/detail/PathRequestManager.h b/src/xrpld/rpc/detail/PathRequestManager.h index 94d126ed23..f6eb80d291 100644 --- a/src/xrpld/rpc/detail/PathRequestManager.h +++ b/src/xrpld/rpc/detail/PathRequestManager.h @@ -23,7 +23,9 @@ namespace xrpl { class PathRequestManager { public: - /** A collection of all PathRequest instances. */ + /** + * A collection of all PathRequest instances. + */ PathRequestManager( Application& app, beast::Journal journal, @@ -34,9 +36,10 @@ public: full_ = collector->makeEvent("pathfind_full"); } - /** Update all of the contained PathRequest instances. - - @param ledger Ledger we are pathfinding in. + /** + * Update all of the contained PathRequest instances. + * + * @param ledger Ledger we are pathfinding in. */ void updateAll(std::shared_ptr const& ledger); diff --git a/src/xrpld/rpc/detail/Pathfinder.h b/src/xrpld/rpc/detail/Pathfinder.h index 5ef6c31b25..aeacd218d2 100644 --- a/src/xrpld/rpc/detail/Pathfinder.h +++ b/src/xrpld/rpc/detail/Pathfinder.h @@ -26,16 +26,19 @@ namespace xrpl { -/** Calculates payment paths. - - The @ref RippleCalc determines the quality of the found paths. - - @see RippleCalc -*/ +/** + * Calculates payment paths. + * + * The @ref RippleCalc determines the quality of the found paths. + * + * @see RippleCalc + */ class Pathfinder : public CountedObject { public: - /** Construct a pathfinder without an issuer.*/ + /** + * Construct a pathfinder without an issuer. + */ Pathfinder( std::shared_ptr const& cache, AccountID const& srcAccount, @@ -57,7 +60,9 @@ public: bool findPaths(int searchLevel, std::function const& continueCallback = {}); - /** Compute the rankings of the paths. */ + /** + * Compute the rankings of the paths. + */ void computePathRanks(int maxPaths, std::function const& continueCallback = {}); @@ -189,8 +194,10 @@ private: PathAsset srcPathAsset_; std::optional srcIssuer_; STAmount srcAmount_; - /** The amount remaining from srcAccount_ after the default liquidity has - been removed. */ + /** + * The amount remaining from srcAccount_ after the default liquidity has + * been removed. + */ STAmount remainingAmount_; bool convertAll_; std::optional domain_; diff --git a/src/xrpld/rpc/detail/RPCHandler.cpp b/src/xrpld/rpc/detail/RPCHandler.cpp index e839d9d78d..6f46aed62d 100644 --- a/src/xrpld/rpc/detail/RPCHandler.cpp +++ b/src/xrpld/rpc/detail/RPCHandler.cpp @@ -29,83 +29,82 @@ namespace xrpl::RPC { namespace { /** - This code is called from both the HTTP RPC handler and Websockets. - - The form of the Json returned is somewhat different between the two services. - - HTML: - Success: - { - "result" : { - "ledger" : { - "accepted" : false, - "transaction_hash" : "..." - }, - "ledger_index" : 10300865, - "validated" : false, - "status" : "success" # Status is inside the result. - } - } - - Failure: - { - "result" : { - // api_version == 1 - "error" : "noNetwork", - "error_code" : 17, - "error_message" : "Not synced to the network.", - - // api_version == 2 - "error" : "notSynced", - "error_code" : 18, - "error_message" : "Not synced to the network.", - - "request" : { - "command" : "ledger", - "ledger_index" : 10300865 - }, - "status" : "error" - } - } - - Websocket: - Success: - { - "result" : { - "ledger" : { - "accepted" : false, - "transaction_hash" : "..." - }, - "ledger_index" : 10300865, - "validated" : false - } - "type": "response", - "status": "success", # Status is OUTside the result! - "id": "client's ID", # Optional - "warning": 3.14 # Optional - } - - Failure: - { - // api_version == 1 - "error" : "noNetwork", - "error_code" : 17, - "error_message" : "Not synced to the network.", - - // api_version == 2 - "error" : "notSynced", - "error_code" : 18, - "error_message" : "Not synced to the network.", - - "request" : { - "command" : "ledger", - "ledger_index" : 10300865 - }, - "type": "response", - "status" : "error", - "id": "client's ID" # Optional - } - + * This code is called from both the HTTP RPC handler and Websockets. + * + * The form of the Json returned is somewhat different between the two services. + * + * HTML: + * Success: + * { + * "result" : { + * "ledger" : { + * "accepted" : false, + * "transaction_hash" : "..." + * }, + * "ledger_index" : 10300865, + * "validated" : false, + * "status" : "success" # Status is inside the result. + * } + * } + * + * Failure: + * { + * "result" : { + * // api_version == 1 + * "error" : "noNetwork", + * "error_code" : 17, + * "error_message" : "Not synced to the network.", + * + * // api_version == 2 + * "error" : "notSynced", + * "error_code" : 18, + * "error_message" : "Not synced to the network.", + * + * "request" : { + * "command" : "ledger", + * "ledger_index" : 10300865 + * }, + * "status" : "error" + * } + * } + * + * Websocket: + * Success: + * { + * "result" : { + * "ledger" : { + * "accepted" : false, + * "transaction_hash" : "..." + * }, + * "ledger_index" : 10300865, + * "validated" : false + * } + * "type": "response", + * "status": "success", # Status is OUTside the result! + * "id": "client's ID", # Optional + * "warning": 3.14 # Optional + * } + * + * Failure: + * { + * // api_version == 1 + * "error" : "noNetwork", + * "error_code" : 17, + * "error_message" : "Not synced to the network.", + * + * // api_version == 2 + * "error" : "notSynced", + * "error_code" : 18, + * "error_message" : "Not synced to the network.", + * + * "request" : { + * "command" : "ledger", + * "ledger_index" : 10300865 + * }, + * "type": "response", + * "status" : "error", + * "id": "client's ID" # Optional + * } */ ErrorCodeI diff --git a/src/xrpld/rpc/detail/RPCHelpers.h b/src/xrpld/rpc/detail/RPCHelpers.h index 4a4dca42e5..881b758487 100644 --- a/src/xrpld/rpc/detail/RPCHelpers.h +++ b/src/xrpld/rpc/detail/RPCHelpers.h @@ -162,7 +162,8 @@ keypairForSignature( json::Value& error, unsigned int apiVersion = kApiVersionIfUnspecified); -/** Parse subscribe/unsubscribe parameters +/** + * Parse subscribe/unsubscribe parameters */ ErrorCodeI parseSubUnsubJson( diff --git a/src/xrpld/rpc/detail/TransactionSign.cpp b/src/xrpld/rpc/detail/TransactionSign.cpp index b8731a2289..e1c5180b5c 100644 --- a/src/xrpld/rpc/detail/TransactionSign.cpp +++ b/src/xrpld/rpc/detail/TransactionSign.cpp @@ -993,7 +993,9 @@ checkFee( //------------------------------------------------------------------------------ -/** Returns a json::ValueType::Object. */ +/** + * Returns a json::ValueType::Object. + */ json::Value transactionSign( json::Value jvRequest, @@ -1027,7 +1029,9 @@ transactionSign( return transactionFormatResultImpl(txn.second, apiVersion); } -/** Returns a json::ValueType::Object. */ +/** + * Returns a json::ValueType::Object. + */ json::Value transactionSubmit( json::Value jvRequest, @@ -1150,7 +1154,9 @@ sortAndValidateSigners(STArray& signers, AccountID const& signingForID) } // namespace detail -/** Returns a json::ValueType::Object. */ +/** + * Returns a json::ValueType::Object. + */ json::Value transactionSignFor( json::Value jvRequest, @@ -1271,7 +1277,9 @@ transactionSignFor( return transactionFormatResultImpl(txn.second, apiVersion); } -/** Returns a json::ValueType::Object. */ +/** + * Returns a json::ValueType::Object. + */ json::Value transactionSubmitMultiSigned( json::Value jvRequest, diff --git a/src/xrpld/rpc/detail/TransactionSign.h b/src/xrpld/rpc/detail/TransactionSign.h index cb3fb176dc..dcb417dd16 100644 --- a/src/xrpld/rpc/detail/TransactionSign.h +++ b/src/xrpld/rpc/detail/TransactionSign.h @@ -33,33 +33,34 @@ getCurrentNetworkFee( int mult = Tuning::kDefaultAutoFillFeeMultiplier, int div = Tuning::kDefaultAutoFillFeeDivisor); -/** Fill in the fee on behalf of the client. - This is called when the client does not explicitly specify the fee. - The client may also put a ceiling on the amount of the fee. This ceiling - is expressed as a multiplier based on the current ledger's fee schedule. - - JSON fields - - "Fee" The fee paid by the transaction. Omitted when the client - wants the fee filled in. - - "fee_mult_max" A multiplier applied to the current ledger's transaction - fee that caps the maximum fee the server should auto fill. - If this optional field is not specified, then a default - multiplier is used. - "fee_div_max" A divider applied to the current ledger's transaction - fee that caps the maximum fee the server should auto fill. - If this optional field is not specified, then a default - divider (1) is used. "fee_mult_max" and "fee_div_max" - are both used such that the maximum fee will be - `base * fee_mult_max / fee_div_max` as an integer. - - @param tx The JSON corresponding to the transaction to fill in. - @param ledger A ledger for retrieving the current fee schedule. - @param roll Identifies if this is called by an administrative endpoint. - - @return A JSON object containing the error results, if any -*/ +/** + * Fill in the fee on behalf of the client. + * This is called when the client does not explicitly specify the fee. + * The client may also put a ceiling on the amount of the fee. This ceiling + * is expressed as a multiplier based on the current ledger's fee schedule. + * + * JSON fields + * + * "Fee" The fee paid by the transaction. Omitted when the client + * wants the fee filled in. + * + * "fee_mult_max" A multiplier applied to the current ledger's transaction + * fee that caps the maximum fee the server should auto fill. + * If this optional field is not specified, then a default + * multiplier is used. + * "fee_div_max" A divider applied to the current ledger's transaction + * fee that caps the maximum fee the server should auto fill. + * If this optional field is not specified, then a default + * divider (1) is used. "fee_mult_max" and "fee_div_max" + * are both used such that the maximum fee will be + * `base * fee_mult_max / fee_div_max` as an integer. + * + * @param tx The JSON corresponding to the transaction to fill in. + * @param ledger A ledger for retrieving the current fee schedule. + * @param roll Identifies if this is called by an administrative endpoint. + * + * @return A JSON object containing the error results, if any + */ json::Value checkFee( json::Value& request, @@ -89,7 +90,9 @@ getProcessTxnFn(NetworkOPs& netOPs) }; } -/** Returns a json::ValueType::Object. */ +/** + * Returns a json::ValueType::Object. + */ json::Value transactionSign( json::Value params, // Passed by value so it can be modified locally. @@ -99,7 +102,9 @@ transactionSign( std::chrono::seconds validatedLedgerAge, Application& app); -/** Returns a json::ValueType::Object. */ +/** + * Returns a json::ValueType::Object. + */ json::Value transactionSubmit( json::Value params, // Passed by value so it can be modified locally. @@ -110,7 +115,9 @@ transactionSubmit( Application& app, ProcessTransactionFn const& processTransaction); -/** Returns a json::ValueType::Object. */ +/** + * Returns a json::ValueType::Object. + */ json::Value transactionSignFor( json::Value params, // Passed by value so it can be modified locally. @@ -120,7 +127,9 @@ transactionSignFor( std::chrono::seconds validatedLedgerAge, Application& app); -/** Returns a json::ValueType::Object. */ +/** + * Returns a json::ValueType::Object. + */ json::Value transactionSubmitMultiSigned( json::Value params, // Passed by value so it can be modified locally. diff --git a/src/xrpld/rpc/detail/TrustLine.h b/src/xrpld/rpc/detail/TrustLine.h index a80d81e4ae..c3f76784ba 100644 --- a/src/xrpld/rpc/detail/TrustLine.h +++ b/src/xrpld/rpc/detail/TrustLine.h @@ -16,25 +16,27 @@ namespace xrpl { -/** Describes how an account was found in a path, and how to find the next set -of paths. "Outgoing" is defined as the source account, or an account found via a -trustline that has rippling enabled on the account's side. -"Incoming" is defined as an account found via a trustline that has rippling -disabled on the account's side. Any trust lines for an incoming account that -have rippling disabled are unusable in paths. -*/ +/** + * Describes how an account was found in a path, and how to find the next set + * of paths. "Outgoing" is defined as the source account, or an account found via a + * trustline that has rippling enabled on the account's side. + * "Incoming" is defined as an account found via a trustline that has rippling + * disabled on the account's side. Any trust lines for an incoming account that + * have rippling disabled are unusable in paths. + */ enum class LineDirection : bool { Incoming = false, Outgoing = true }; -/** Wraps a trust line SLE for convenience. - The complication of trust lines is that there is a - "low" account and a "high" account. This wraps the - SLE and expresses its data from the perspective of - a chosen account on the line. - - This wrapper is primarily used in the path finder and there can easily be - tens of millions of instances of this class. When modifying this class think - carefully about the memory implications. -*/ +/** + * Wraps a trust line SLE for convenience. + * The complication of trust lines is that there is a + * "low" account and a "high" account. This wraps the + * SLE and expresses its data from the perspective of + * a chosen account on the line. + * + * This wrapper is primarily used in the path finder and there can easily be + * tens of millions of instances of this class. When modifying this class think + * carefully about the memory implications. + */ class TrustLineBase { public: @@ -51,7 +53,9 @@ protected: TrustLineBase(TrustLineBase&&) = default; public: - /** Returns the state map key for the ledger entry. */ + /** + * Returns the state map key for the ledger entry. + */ [[nodiscard]] uint256 const& key() const { @@ -109,28 +113,36 @@ public: return getNoRipplePeer() ? LineDirection::Incoming : LineDirection::Outgoing; } - /** Have we set the freeze flag on our peer */ + /** + * Have we set the freeze flag on our peer + */ [[nodiscard]] bool getFreeze() const { return (flags_ & (viewLowest_ ? lsfLowFreeze : lsfHighFreeze)) != 0u; } - /** Have we set the deep freeze flag on our peer */ + /** + * Have we set the deep freeze flag on our peer + */ [[nodiscard]] bool getDeepFreeze() const { return (flags_ & (viewLowest_ ? lsfLowDeepFreeze : lsfHighDeepFreeze)) != 0u; } - /** Has the peer set the freeze flag on us */ + /** + * Has the peer set the freeze flag on us + */ [[nodiscard]] bool getFreezePeer() const { return (flags_ & (!viewLowest_ ? lsfLowFreeze : lsfHighFreeze)) != 0u; } - /** Has the peer set the deep freeze flag on us */ + /** + * Has the peer set the deep freeze flag on us + */ [[nodiscard]] bool getDeepFreezePeer() const { diff --git a/src/xrpld/rpc/detail/Tuning.h b/src/xrpld/rpc/detail/Tuning.h index 5a9d546472..b904822698 100644 --- a/src/xrpld/rpc/detail/Tuning.h +++ b/src/xrpld/rpc/detail/Tuning.h @@ -2,41 +2,63 @@ #include -/** Tuned constants. */ +/** + * Tuned constants. + */ /** @{ */ namespace xrpl::RPC::Tuning { -/** Represents RPC limit parameter values that have a min, default and max. */ +/** + * Represents RPC limit parameter values that have a min, default and max. + */ struct LimitRange { unsigned int rmin, rDefault, rmax; }; -/** Limits for the account_lines command. */ +/** + * Limits for the account_lines command. + */ static constexpr LimitRange kAccountLines = {.rmin = 10, .rDefault = 200, .rmax = 400}; -/** Limits for the account_channels command. */ +/** + * Limits for the account_channels command. + */ static constexpr LimitRange kAccountChannels = {.rmin = 10, .rDefault = 200, .rmax = 400}; -/** Limits for the account_objects command. */ +/** + * Limits for the account_objects command. + */ static constexpr LimitRange kAccountObjects = {.rmin = 10, .rDefault = 200, .rmax = 400}; -/** Limits for the account_offers command. */ +/** + * Limits for the account_offers command. + */ static constexpr LimitRange kAccountOffers = {.rmin = 10, .rDefault = 200, .rmax = 400}; -/** Limits for the account_tx command. */ +/** + * Limits for the account_tx command. + */ static constexpr LimitRange kAccountTx = {.rmin = 10, .rDefault = 200, .rmax = 400}; -/** Limits for the book_offers command. */ +/** + * Limits for the book_offers command. + */ static constexpr LimitRange kBookOffers = {.rmin = 1, .rDefault = 60, .rmax = 100}; -/** Limits for the no_ripple_check command. */ +/** + * Limits for the no_ripple_check command. + */ static constexpr LimitRange kNoRippleCheck = {.rmin = 10, .rDefault = 300, .rmax = 400}; -/** Limits for the account_nftokens command, in pages. */ +/** + * Limits for the account_nftokens command, in pages. + */ static constexpr LimitRange kAccountNfTokens = {.rmin = 20, .rDefault = 100, .rmax = 400}; -/** Limits for the nft_buy_offers & nft_sell_offers commands. */ +/** + * Limits for the nft_buy_offers & nft_sell_offers commands. + */ static constexpr LimitRange kNftOffers = {.rmin = 50, .rDefault = 250, .rmax = 500}; static constexpr int kDefaultAutoFillFeeMultiplier = 10; @@ -47,23 +69,33 @@ static constexpr int kMaxJobQueueClients = 500; constexpr auto kMaxValidatedLedgerAge = std::chrono::minutes{2}; static constexpr int kMaxRequestSize = 1000000; -/** Maximum number of pages in one response from a binary LedgerData request. */ +/** + * Maximum number of pages in one response from a binary LedgerData request. + */ static constexpr int kBinaryPageLength = 2048; -/** Maximum number of pages in one response from a Json LedgerData request. */ +/** + * Maximum number of pages in one response from a Json LedgerData request. + */ static constexpr int kJsonPageLength = 256; -/** Maximum number of pages in a LedgerData response. */ +/** + * Maximum number of pages in a LedgerData response. + */ constexpr int pageLength(bool isBinary) { return isBinary ? kBinaryPageLength : kJsonPageLength; } -/** Maximum number of source currencies allowed in a path find request. */ +/** + * Maximum number of source currencies allowed in a path find request. + */ static constexpr int kMaxSrcCur = 18; -/** Maximum number of auto source currencies in a path find request. */ +/** + * Maximum number of auto source currencies in a path find request. + */ static constexpr int kMaxAutoSrcCur = 88; } // namespace xrpl::RPC::Tuning diff --git a/src/xrpld/rpc/handlers/account/AccountNFTs.cpp b/src/xrpld/rpc/handlers/account/AccountNFTs.cpp index 5ce10f6121..ea9bec0f45 100644 --- a/src/xrpld/rpc/handlers/account/AccountNFTs.cpp +++ b/src/xrpld/rpc/handlers/account/AccountNFTs.cpp @@ -23,16 +23,17 @@ namespace xrpl { -/** General RPC command that can retrieve objects in the account root. - { - account: - ledger_hash: // optional - ledger_index: // optional - type: // optional, defaults to all account objects types - limit: // optional - marker: // optional, resume previous query - } -*/ +/** + * General RPC command that can retrieve objects in the account root. + * { + * account: + * ledger_hash: // optional + * ledger_index: // optional + * type: // optional, defaults to all account objects types + * limit: // optional + * marker: // optional, resume previous query + * } + */ json::Value doAccountNFTs(RPC::JsonContext& context) { diff --git a/src/xrpld/rpc/handlers/account/AccountObjects.cpp b/src/xrpld/rpc/handlers/account/AccountObjects.cpp index 8967509262..4a34ff02fc 100644 --- a/src/xrpld/rpc/handlers/account/AccountObjects.cpp +++ b/src/xrpld/rpc/handlers/account/AccountObjects.cpp @@ -27,16 +27,17 @@ namespace xrpl { -/** Gathers all objects for an account in a ledger. - @param ledger Ledger to search account objects. - @param account AccountID to find objects for. - @param typeFilter Gathers objects of these types. empty gathers all types. - @param dirIndex Begin gathering account objects from this directory. - @param entryIndex Begin gathering objects from this directory node. - @param limit Maximum number of objects to find. - @param sponsoredFilter If set, only return objects whose sponsored state matches the value. - @param jvResult A JSON result that holds the request objects. -*/ +/** + * Gathers all objects for an account in a ledger. + * @param ledger Ledger to search account objects. + * @param account AccountID to find objects for. + * @param typeFilter Gathers objects of these types. empty gathers all types. + * @param dirIndex Begin gathering account objects from this directory. + * @param entryIndex Begin gathering objects from this directory node. + * @param limit Maximum number of objects to find. + * @param sponsoredFilter If set, only return objects whose sponsored state matches the value. + * @param jvResult A JSON result that holds the request objects. + */ bool getAccountObjects( ReadView const& ledger, diff --git a/src/xrpld/rpc/handlers/orderbook/GetAggregatePrice.cpp b/src/xrpld/rpc/handlers/orderbook/GetAggregatePrice.cpp index d2f596ed8e..632456a3fa 100644 --- a/src/xrpld/rpc/handlers/orderbook/GetAggregatePrice.cpp +++ b/src/xrpld/rpc/handlers/orderbook/GetAggregatePrice.cpp @@ -42,7 +42,8 @@ using namespace boost::bimaps; // sorted descending by lastUpdateTime, ascending by AssetPrice using Prices = bimap>, multiset_of>; -/** Calls callback "f" on the ledger-object sle and up to three previous +/** + * Calls callback "f" on the ledger-object sle and up to three previous * metadata objects. Stops early if the callback returns true. */ static void diff --git a/src/xrpld/rpc/json_body.h b/src/xrpld/rpc/json_body.h index 49c2b0e6e0..0f56b852f7 100644 --- a/src/xrpld/rpc/json_body.h +++ b/src/xrpld/rpc/json_body.h @@ -13,7 +13,9 @@ namespace xrpl { -/// Body that holds JSON +/** + * Body that holds JSON + */ struct JsonBody { explicit JsonBody() = default; From 62634463f4adbe21d77206c75d0d7201e301e6fa Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Mon, 13 Jul 2026 14:27:14 +0100 Subject: [PATCH 4/5] style: Add pre-commit hook to check doxygen style (#7794) --- .cspell.config.yaml | 1 + .pre-commit-config.yaml | 5 + bin/pre-commit/check_doxygen_style.py | 430 ++++++++++++++++++++++++++ 3 files changed, 436 insertions(+) create mode 100755 bin/pre-commit/check_doxygen_style.py diff --git a/.cspell.config.yaml b/.cspell.config.yaml index dc24bf833b..3c5191f5e1 100644 --- a/.cspell.config.yaml +++ b/.cspell.config.yaml @@ -302,6 +302,7 @@ words: - takerpays - ters - TMEndpointv2 + - tparam - trixie - tx - txid diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 910bda8d4c..d3bf0209fb 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -43,6 +43,11 @@ repos: language: python entry: ./bin/pre-commit/fix_pragma_once.py files: \.(h|hpp)$ + - id: check-doxygen-style + name: check Doxygen comment style + entry: ./bin/pre-commit/check_doxygen_style.py + language: python + types_or: [c++, c] - repo: https://github.com/pre-commit/mirrors-clang-format rev: dd18dad857d6133e90bbe478f4f2f22ec0030269 # frozen: v22.1.5 diff --git a/bin/pre-commit/check_doxygen_style.py b/bin/pre-commit/check_doxygen_style.py new file mode 100755 index 0000000000..fd37450113 --- /dev/null +++ b/bin/pre-commit/check_doxygen_style.py @@ -0,0 +1,430 @@ +#!/usr/bin/env python3 +""" +Check C++ Doxygen comment style. + +Enforces the house convention for documentation comments: + + * Use ``/** ... */`` blocks, not ``///``, ``//!`` or ``/*! ... */``; a plain + ``/* ... */`` that contains Doxygen commands is a doc comment missing its + second star. Trailing member-after comments use ``///<`` (not ``//!<`` or + ``/**< ... */`` -- the block form gets reflowed/mis-attached by + clang-format on packed enum values, the line form does not). + * ``/**`` sits alone on its line; the closing ``*/`` sits alone on its line. + * Every content line is prefixed with `` * `` (no bare-indented continuation). + * The first content line is flush (not over-indented). + * Doxygen commands use the ``@cmd`` form, not ``\\cmd``. + * Use ``@return`` / ``@throws`` rather than prose ``Returns:`` / ``Throws:``. + * A plain ``//`` comment carrying a block-level ``@command`` (``@param``, + ``@return``, ``@see``, ...) is documentation and must be a ``/** ... */`` + block (Doxygen ignores ``//``). + * Use canonical command spellings: ``@return`` (not ``@returns``), + ``@throws`` (not ``@throw``), ``@see`` (not ``@sa``). + * Order block tags ``@tparam`` -> ``@param`` -> ``@return``. (Whether + ``@param`` order matches the signature is not checked here -- too fragile to + parse; Doxygen's WARN_IF_DOC_ERROR covers name mismatches.) + * One-liners are expanded to three lines, EXCEPT bare markers ``@{`` / ``@}`` + / ``@cond [label]`` / ``@endcond`` / ``@file [name]`` which stay on one line. + +Left intentionally alone (recognized, valid Doxygen that is not this style's +concern): + + * ``///<`` trailing "member-after" comments (the house form). + * Divider lines made only of slashes (``//////////``). + * Plain ``/* ... */`` (non-Doxygen) comments. + +Usage: + check_doxygen_style.py [FILE ...] # explicit files + check_doxygen_style.py # default: src/ and include/ trees + +Exit status is non-zero if any violation is found. +""" + +import argparse +import re +import sys +from collections.abc import Iterable, Iterator +from dataclasses import dataclass +from enum import Enum +from pathlib import Path + + +class Category(Enum): + """A kind of style violation: a printed ``label`` and its ``description``. + + The description is the default message; a few categories whose wording + depends on the offending text (see ``Finding.detail``) override it. + """ + + def __init__(self, label: str, description: str) -> None: + self.label = label + self.description = description + + BACKSLASH_COMMAND = ("backslash-command", "use the @cmd form, not \\cmd") + WRONG_COMMAND = ("wrong-command", "use the canonical command spelling") + TRIPLE_SLASH = ("triple-slash", "use a /** ... */ block instead of ///") + QT_MEMBER = ("qt-member", "use ///< instead of //!<") + QT_LINE = ("qt-line", "use a /** ... */ block instead of //!") + BLOCK_MEMBER = ("block-member", "use ///< instead of /**<") + DOC_IN_LINE_COMMENT = ( + "doc-in-line-comment", + "use a /** ... */ block for documentation, not //", + ) + QT_COMMENT = ("qt-comment", "use /** instead of /*!") + SINGLE_LINE_BLOCK = ( + "single-line-block", + "expand one-line /** ... */ to a multi-line block " + "(markers @{ @} @cond @endcond @file may stay)", + ) + TEXT_ON_OPENER = ("text-on-opener", "move text off the /** opener line") + BARE_CONTINUATION = ("bare-continuation", 'prefix continuation lines with " * "') + OVER_INDENTED = ("over-indented", "first content line is over-indented") + OVER_INDENTED_TAG = ( + "over-indented-tag", + 'Doxygen tag over-indented; use a single space after "*"', + ) + COMBINED_MARKER = ( + "combined-marker", + "scope marker @{ / @} should be its own single-line /** @{ */ block", + ) + PROSE_LABEL = ("prose-label", "use a Doxygen tag instead of a prose label") + CONTENT_ON_CLOSER = ("content-on-closer", "move content off the closing */ line") + PLAIN_BLOCK_DOC = ( + "plain-block-doc", + "documentation comment must open with /** not /*", + ) + TAG_ORDER = ( + "tag-order", + "block tags out of order; expected @tparam, then @param, then @return", + ) + + +@dataclass(frozen=True) +class Finding: + """A single style violation at a 1-based line number. + + ``detail`` overrides the category's default description when the message + depends on the offending text (e.g. which command was misspelled). + """ + + line: int + category: Category + detail: str | None = None + + @property + def message(self) -> str: + return self.detail if self.detail is not None else self.category.description + + +DEFAULT_ROOTS = ("src", "include") +EXTS = {".h", ".hpp", ".cpp", ".ipp", ".cxx", ".cc"} + +# Every Doxygen command we recognize when written with a backslash (\cmd). +_ALL_COMMANDS = ( + "brief|param|tparam|return|returns|retval|note|warning|pre|post|see|sa|ref|" + "throw|throws|exception|deprecated|details|code|endcode|verbatim|endverbatim|" + "li|arg|c|internal|since|todo|attention|remark|remarks|ingroup|defgroup" +) +# Block-level tags whose over-indentation we flag inside a block body. +_BLOCK_TAGS = ( + "param|tparam|returns?|retval|brief|throws?|note|warning|" + "pre|post|see|sa|details|deprecated" +) +# Tags that, appearing anywhere in a comment, mark it as documentation. +_ANY_DOC_TAGS = ( + "param|tparam|returns?|retval|brief|throws?|note|warning|pre|post|see|sa" +) +# Tags that make a plain // comment a mis-styled doc comment. +_LINE_DOC_TAGS = "brief|param|tparam|returns?|retval|throws?|note|see|pre|post" + +# \cmd that should be @cmd. +RE_BACKSLASH_CMD = re.compile(r"\\(" + _ALL_COMMANDS + r")\b") +# Bare markers that may legitimately stay on a single line. +RE_MARKER = re.compile(r"^@(\{|\}|cond(\s.*)?|endcond|file(\s.*)?)$") +# Prose section labels that should be Doxygen tags. +RE_PROSE_LABEL = re.compile(r"^\*\s(Returns|Throws|Exceptions):\s*$") +# An over-indented block tag: "*" followed by 2+ spaces then the tag. +RE_OVERINDENTED_TAG = re.compile(r"^\*\s{2,}@(" + _BLOCK_TAGS + r")\b") +# Any documentation tag (used to spot a doc comment hiding in a plain /* */). +RE_ANY_DOC_TAG = re.compile(r"@(" + _ANY_DOC_TAGS + r")\b") +# A documentation tag inside a // comment. +RE_LINE_DOC_TAG = re.compile(r"@(" + _LINE_DOC_TAGS + r")\b") +# Order-relevant tags, for the @tparam -> @param -> @return ordering check. +RE_ORDER_TAG = re.compile(r"^\*\s*@(param|tparam|returns?|retval)\b") +# First content line indented by 2+ spaces after the "*". +RE_FIRST_OVERINDENT = re.compile(r"^\s*\*\s{2,}\S") +# A scope marker @{ / @} sharing a comment with other text. +RE_COMBINED_MARKER = re.compile(r"^\*\s*@[{}]\s*$") + +# Non-canonical command spellings -> house spelling. +WRONG_SPELLINGS = [ + (re.compile(r"@returns\b"), "@return"), + (re.compile(r"@throw\b"), "@throws"), + (re.compile(r"@sa\b"), "@see"), +] + +# Order block tags should appear in; a body out of this order is a violation. +EXPECTED_TAG_ORDER = ("tparam", "param", "return") + + +def is_doxy_open(stripped: str) -> bool: + """True for a line-start Doxygen block opener we should normalize.""" + if stripped.startswith("/*!"): # Qt-style Doxygen + return not stripped.startswith("/*!<") # member-after, leave inline + return ( + stripped.startswith("/**") + and not stripped.startswith("/***") + and not stripped.startswith("/**/") + and not stripped.startswith("/**<") + ) + + +def _flag_commands(raw_line: str, stripped: str, index: int) -> list[Finding]: + """Non-terminal: flag \\cmd and misspelled @cmd on any comment-ish line.""" + if not stripped.startswith(("*", "//", "/*")): + return [] + findings: list[Finding] = [] + backslash = RE_BACKSLASH_CMD.search(raw_line) + if backslash: + command = backslash.group(1) + findings.append( + Finding( + index + 1, + Category.BACKSLASH_COMMAND, + f"use @{command} instead of \\{command}", + ) + ) + for pattern, replacement in WRONG_SPELLINGS: + wrong = pattern.search(raw_line) + if wrong: + findings.append( + Finding( + index + 1, + Category.WRONG_COMMAND, + f"use {replacement} instead of {wrong.group(0)}", + ) + ) + return findings + + +def _flag_line_comment(raw_line: str, stripped: str, index: int) -> Finding | None: + """Return the finding for a terminal single-line comment form, else None.""" + if stripped.startswith("///") and not stripped.startswith(("////", "///<")): + return Finding(index + 1, Category.TRIPLE_SLASH) + if "//!<" in raw_line: + return Finding(index + 1, Category.QT_MEMBER) + if stripped.startswith("//!"): + return Finding(index + 1, Category.QT_LINE) + if "/**<" in raw_line: + return Finding(index + 1, Category.BLOCK_MEMBER) + if stripped.startswith("//") and RE_LINE_DOC_TAG.search(stripped): + return Finding(index + 1, Category.DOC_IN_LINE_COMMENT) + return None + + +def _flag_single_line_block(stripped: str, line_no: int, is_qt: bool) -> list[Finding]: + """Findings for a whole /** ... */ or /*! ... */ block on one line.""" + inner = re.sub(r"^/\*[*!]", "", stripped) + inner = re.sub(r"\*/\s*$", "", inner).strip() + findings: list[Finding] = [] + if is_qt: + findings.append(Finding(line_no, Category.QT_COMMENT)) + if inner and not RE_MARKER.match(inner): + findings.append(Finding(line_no, Category.SINGLE_LINE_BLOCK)) + return findings + + +def _canonical_order_tag(body: str) -> str | None: + """The order-relevant tag (tparam/param/return) a body line opens with, if any.""" + match = RE_ORDER_TAG.match(body) + if match is None: + return None + command = match.group(1) + return "return" if command in ("return", "returns", "retval") else command + + +def _flag_body_line( + body_line: str, line_no: int, is_first_content: bool +) -> list[Finding]: + """Findings for one interior line of a multi-line block.""" + body = body_line.strip() + findings: list[Finding] = [] + if body and not body.startswith("*"): + findings.append(Finding(line_no, Category.BARE_CONTINUATION)) + if body.startswith("*"): + if is_first_content and RE_FIRST_OVERINDENT.match(body_line): + findings.append(Finding(line_no, Category.OVER_INDENTED)) + if RE_OVERINDENTED_TAG.match(body): + findings.append(Finding(line_no, Category.OVER_INDENTED_TAG)) + if RE_COMBINED_MARKER.match(body): + findings.append(Finding(line_no, Category.COMBINED_MARKER)) + label = RE_PROSE_LABEL.match(body) + if label: + suggested_tag = "@return" if label.group(1) == "Returns" else "@throws" + findings.append( + Finding( + line_no, + Category.PROSE_LABEL, + f'use {suggested_tag} instead of prose "{label.group(1)}:"', + ) + ) + return findings + + +def _flag_closer(closer_line: str, line_no: int) -> list[Finding]: + """Findings for content sharing the closing */ line.""" + before = closer_line[: closer_line.index("*/")].strip() + if before and before != "*": + return [Finding(line_no, Category.CONTENT_ON_CLOSER)] + return [] + + +def _flag_tag_order(first_tag_line: dict[str, int]) -> list[Finding]: + """One finding if the present block tags are not in EXPECTED_TAG_ORDER.""" + tag_lines = [ + first_tag_line[tag] for tag in EXPECTED_TAG_ORDER if tag in first_tag_line + ] + if tag_lines != sorted(tag_lines): + return [Finding(min(tag_lines), Category.TAG_ORDER)] + return [] + + +def _flag_doxy_block(lines: list[str], start: int) -> tuple[int, list[Finding]]: + """Handle a /** or /*! block opening at ``start``; return (next index, findings).""" + raw_line = lines[start] + stripped = raw_line.lstrip() + open_pos = raw_line.index("/*") + is_qt = stripped.startswith("/*!") + + # A whole block on one line: /** ... */. + if "*/" in raw_line[open_pos + 2 :]: + return start + 1, _flag_single_line_block(stripped, start + 1, is_qt) + + # Multi-line block: opener, then scan the body to the closer. + findings: list[Finding] = [] + if is_qt: + findings.append(Finding(start + 1, Category.QT_COMMENT)) + if raw_line[open_pos + 3 :].strip(): + findings.append(Finding(start + 1, Category.TEXT_ON_OPENER)) + + line_count = len(lines) + cursor = start + 1 + is_first_content = True + first_tag_line: dict[str, int] = {} # canonical tag -> 1-based first line + while cursor < line_count and "*/" not in lines[cursor]: + body_line = lines[cursor] + body = body_line.strip() + findings.extend(_flag_commands(body_line, body, cursor)) + tag = _canonical_order_tag(body) + if tag is not None: + first_tag_line.setdefault(tag, cursor + 1) + findings.extend(_flag_body_line(body_line, cursor + 1, is_first_content)) + if body.startswith("*"): + is_first_content = False + cursor += 1 + + if cursor < line_count: + closer_line = lines[cursor] + findings.extend(_flag_commands(closer_line, closer_line.strip(), cursor)) + findings.extend(_flag_closer(closer_line, cursor + 1)) + findings.extend(_flag_tag_order(first_tag_line)) + + return cursor + 1, findings + + +def _flag_plain_block(lines: list[str], start: int) -> tuple[int, list[Finding]]: + """Handle a line-start plain /* ... */ block; return (next index, findings). + + Only flagged when it hides a documentation command (a missing second star). + """ + line_count = len(lines) + cursor = start + while cursor < line_count and "*/" not in lines[cursor]: + cursor += 1 + findings: list[Finding] = [] + # The opener (start) is command-checked by check_file; check the rest here. + for i in range(start + 1, min(cursor + 1, line_count)): + findings.extend(_flag_commands(lines[i], lines[i].strip(), i)) + block_text = "\n".join( + lines[start : cursor + 1] if cursor < line_count else lines[start:] + ) + if RE_ANY_DOC_TAG.search(block_text): + findings.append(Finding(start + 1, Category.PLAIN_BLOCK_DOC)) + next_index = cursor + 1 if cursor < line_count else line_count + return next_index, findings + + +def check_file(path: Path) -> list[Finding]: + """Return all style violations found in one file.""" + lines = path.read_text(encoding="utf-8").split("\n") + findings: list[Finding] = [] + line_count = len(lines) + index = 0 + in_plain_block = False # inside a mid-line, non-Doxygen /* ... */ + while index < line_count: + raw_line = lines[index] + stripped = raw_line.lstrip() + + # Skip the interior of a plain block opened on an earlier line. + if in_plain_block: + in_plain_block = "*/" not in raw_line + index += 1 + continue + + findings.extend(_flag_commands(raw_line, stripped, index)) + + line_finding = _flag_line_comment(raw_line, stripped, index) + if line_finding is not None: + findings.append(line_finding) + index += 1 + elif is_doxy_open(stripped): + index, block_findings = _flag_doxy_block(lines, index) + findings.extend(block_findings) + elif stripped.startswith("/*"): + index, block_findings = _flag_plain_block(lines, index) + findings.extend(block_findings) + else: + # A /* that opens mid-line without closing starts a plain block. + if "/*" in raw_line and not stripped.startswith("//"): + if "*/" not in raw_line[raw_line.index("/*") + 2 :]: + in_plain_block = True + index += 1 + return findings + + +def iter_files(paths: Iterable[str]) -> Iterator[Path]: + """Yield every C++ source file among the given files and directories.""" + for raw_path in paths: + path = Path(raw_path) + if path.is_dir(): + for candidate in path.rglob("*"): + if candidate.is_file() and candidate.suffix in EXTS: + yield candidate + elif path.suffix in EXTS: + yield path + + +def main() -> int: + parser = argparse.ArgumentParser(description="Check Doxygen comment style.") + parser.add_argument( + "files", nargs="*", help="files or directories (default: src/ include/)" + ) + parser.add_argument( + "-q", "--quiet", action="store_true", help="only print the summary count" + ) + args = parser.parse_args() + roots = args.files or [root for root in DEFAULT_ROOTS if Path(root).is_dir()] + + total = 0 + for path in sorted(set(iter_files(roots)), key=str): + for finding in check_file(path): + total += 1 + if not args.quiet: + print( + f"{path}:{finding.line}: {finding.category.label}: {finding.message}" + ) + print(f"\n{total} doc-style violation(s)", file=sys.stderr) + return 1 if total else 0 + + +if __name__ == "__main__": + sys.exit(main()) From 2b4d067acebabe97d46e00dd42d6eae369fe3654 Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Mon, 13 Jul 2026 15:38:33 +0100 Subject: [PATCH 5/5] test: Add tests for check doxygen style (#7795) --- bin/pre-commit/check_doxygen_style.py | 38 +- bin/pre-commit/test_check_doxygen_style.py | 406 +++++++++++++++++++++ 2 files changed, 430 insertions(+), 14 deletions(-) create mode 100755 bin/pre-commit/test_check_doxygen_style.py diff --git a/bin/pre-commit/check_doxygen_style.py b/bin/pre-commit/check_doxygen_style.py index fd37450113..6a9af9399f 100755 --- a/bin/pre-commit/check_doxygen_style.py +++ b/bin/pre-commit/check_doxygen_style.py @@ -6,9 +6,9 @@ Enforces the house convention for documentation comments: * Use ``/** ... */`` blocks, not ``///``, ``//!`` or ``/*! ... */``; a plain ``/* ... */`` that contains Doxygen commands is a doc comment missing its - second star. Trailing member-after comments use ``///<`` (not ``//!<`` or - ``/**< ... */`` -- the block form gets reflowed/mis-attached by - clang-format on packed enum values, the line form does not). + second star. Trailing member-after comments use ``///<`` (not ``//!<``, + ``/*!< ... */`` or ``/**< ... */`` -- the block forms get reflowed and + mis-attached by clang-format on packed enum values, the line form does not). * ``/**`` sits alone on its line; the closing ``*/`` sits alone on its line. * Every content line is prefixed with `` * `` (no bare-indented continuation). * The first content line is flush (not over-indented). @@ -65,6 +65,7 @@ class Category(Enum): QT_MEMBER = ("qt-member", "use ///< instead of //!<") QT_LINE = ("qt-line", "use a /** ... */ block instead of //!") BLOCK_MEMBER = ("block-member", "use ///< instead of /**<") + QT_BLOCK_MEMBER = ("qt-block-member", "use ///< instead of /*!<") DOC_IN_LINE_COMMENT = ( "doc-in-line-comment", "use a /** ... */ block for documentation, not //", @@ -155,11 +156,12 @@ RE_FIRST_OVERINDENT = re.compile(r"^\s*\*\s{2,}\S") # A scope marker @{ / @} sharing a comment with other text. RE_COMBINED_MARKER = re.compile(r"^\*\s*@[{}]\s*$") -# Non-canonical command spellings -> house spelling. +# Non-canonical command spellings -> the house spelling (bare command names). +# Used both to flag a wrong @form and to suggest the right @form for a \wrong. +CANONICAL_COMMAND = {"returns": "return", "throw": "throws", "sa": "see"} WRONG_SPELLINGS = [ - (re.compile(r"@returns\b"), "@return"), - (re.compile(r"@throw\b"), "@throws"), - (re.compile(r"@sa\b"), "@see"), + (re.compile(rf"@{wrong}\b"), f"@{right}") + for wrong, right in CANONICAL_COMMAND.items() ] # Order block tags should appear in; a body out of this order is a violation. @@ -179,18 +181,19 @@ def is_doxy_open(stripped: str) -> bool: def _flag_commands(raw_line: str, stripped: str, index: int) -> list[Finding]: - """Non-terminal: flag \\cmd and misspelled @cmd on any comment-ish line.""" + """Flag \\cmd and misspelled @cmd on a comment line (opener, body, or closer).""" if not stripped.startswith(("*", "//", "/*")): return [] findings: list[Finding] = [] backslash = RE_BACKSLASH_CMD.search(raw_line) if backslash: command = backslash.group(1) + canonical = CANONICAL_COMMAND.get(command, command) findings.append( Finding( index + 1, Category.BACKSLASH_COMMAND, - f"use @{command} instead of \\{command}", + f"use @{canonical} instead of \\{command}", ) ) for pattern, replacement in WRONG_SPELLINGS: @@ -207,7 +210,7 @@ def _flag_commands(raw_line: str, stripped: str, index: int) -> list[Finding]: def _flag_line_comment(raw_line: str, stripped: str, index: int) -> Finding | None: - """Return the finding for a terminal single-line comment form, else None.""" + """Return the finding for a single-line comment form (///, //!, /**<, ...), else None.""" if stripped.startswith("///") and not stripped.startswith(("////", "///<")): return Finding(index + 1, Category.TRIPLE_SLASH) if "//!<" in raw_line: @@ -216,6 +219,8 @@ def _flag_line_comment(raw_line: str, stripped: str, index: int) -> Finding | No return Finding(index + 1, Category.QT_LINE) if "/**<" in raw_line: return Finding(index + 1, Category.BLOCK_MEMBER) + if "/*!<" in raw_line: + return Finding(index + 1, Category.QT_BLOCK_MEMBER) if stripped.startswith("//") and RE_LINE_DOC_TAG.search(stripped): return Finding(index + 1, Category.DOC_IN_LINE_COMMENT) return None @@ -353,9 +358,9 @@ def _flag_plain_block(lines: list[str], start: int) -> tuple[int, list[Finding]] return next_index, findings -def check_file(path: Path) -> list[Finding]: - """Return all style violations found in one file.""" - lines = path.read_text(encoding="utf-8").split("\n") +def check_source(text: str) -> list[Finding]: + """Return all style violations found in the given source text.""" + lines = text.split("\n") findings: list[Finding] = [] line_count = len(lines) index = 0 @@ -391,6 +396,11 @@ def check_file(path: Path) -> list[Finding]: return findings +def check_file(path: Path) -> list[Finding]: + """Return all style violations found in one file.""" + return check_source(path.read_text(encoding="utf-8")) + + def iter_files(paths: Iterable[str]) -> Iterator[Path]: """Yield every C++ source file among the given files and directories.""" for raw_path in paths: @@ -422,7 +432,7 @@ def main() -> int: print( f"{path}:{finding.line}: {finding.category.label}: {finding.message}" ) - print(f"\n{total} doc-style violation(s)", file=sys.stderr) + print(f"\n{total} doxygen-style violation(s)", file=sys.stderr) return 1 if total else 0 diff --git a/bin/pre-commit/test_check_doxygen_style.py b/bin/pre-commit/test_check_doxygen_style.py new file mode 100755 index 0000000000..861414f46d --- /dev/null +++ b/bin/pre-commit/test_check_doxygen_style.py @@ -0,0 +1,406 @@ +#!/usr/bin/env python3 +""" +Tests for check_doxygen_style.py. + +Run directly (no test framework needed): + ./bin/pre-commit/test_check_doxygen_style.py +or under pytest: + pytest bin/pre-commit/test_check_doxygen_style.py +""" + +import sys +import textwrap + +from check_doxygen_style import Finding, check_source + + +def findings_for(text: str) -> list[Finding]: + """Return the style violations for the given source text. + + The text is dedented and its leading newline stripped, so fixtures can be + written as indented triple-quoted here-docs while keeping honest 1-based + line numbers. + """ + text = textwrap.dedent(text).lstrip("\n") + return check_source(text) + + +def labels_for(text: str) -> list[str]: + return [f.category.label for f in findings_for(text)] + + +def messages_for(text: str) -> list[str]: + return [f.message for f in findings_for(text)] + + +# --- well-formed input produces nothing ------------------------------------- + + +def test_clean_block_ok() -> None: + code = """ + /** + * Brief. + * + * @tparam T a type + * @param x the x + * @return the result + */ + """ + assert findings_for(code) == [] + + +def test_blank_lines_inside_block_ok() -> None: + code = """ + /** + * a + * + * b + */ + """ + assert findings_for(code) == [] + + +def test_member_and_divider_allowed() -> None: + assert findings_for("int x; ///< ok member\n") == [] + assert findings_for("//////////\n") == [] + assert findings_for("//// text\n") == [] + + +# --- line-comment forms ------------------------------------------------------ + + +def test_triple_slash() -> None: + code = "/// doc\n" + assert labels_for(code) == ["triple-slash"] + + +def test_qt_line() -> None: + code = "//! doc\n" + assert labels_for(code) == ["qt-line"] + + +def test_qt_member() -> None: + code = "int x; //!< doc\n" + assert labels_for(code) == ["qt-member"] + + +def test_block_member() -> None: + code = "int x; /**< doc */\n" + assert labels_for(code) == ["block-member"] + + +def test_qt_block_member() -> None: + code = "int x; /*!< doc */\n" + assert labels_for(code) == ["qt-block-member"] + + +def test_doc_in_line_comment() -> None: + code = "// @param x\n" + assert labels_for(code) == ["doc-in-line-comment"] + + +# --- block forms ------------------------------------------------------------- + + +def test_qt_comment() -> None: + code = """ + /*! + * brief + */ + """ + assert labels_for(code) == ["qt-comment"] + + +def test_qt_comment_single_line() -> None: + # /*! ... */ on one line -> qt-comment (plus single-line-block) + code = "/*! brief */\n" + assert labels_for(code) == ["qt-comment", "single-line-block"] + + +def test_single_line_block() -> None: + code = "/** brief */\n" + assert labels_for(code) == ["single-line-block"] + + +def test_single_line_markers_allowed() -> None: + for marker in ("@{", "@}", "@cond LABEL", "@endcond", "@file foo.h"): + code = f"/** {marker} */\n" + assert findings_for(code) == [], marker + + +def test_text_on_opener() -> None: + code = """ + /** text here + * more + */ + """ + assert labels_for(code) == ["text-on-opener"] + + +def test_bare_continuation() -> None: + code = """ + /** + * a + bare line + */ + """ + assert labels_for(code) == ["bare-continuation"] + + +def test_over_indented_first_line() -> None: + code = """ + /** + * over + */ + """ + assert labels_for(code) == ["over-indented"] + + +def test_over_indented_tag() -> None: + # a flush first line consumes "first content", isolating the tag check + code = """ + /** + * brief + * @param x + */ + """ + assert labels_for(code) == ["over-indented-tag"] + + +def test_combined_marker() -> None: + code = """ + /** + * @{ + */ + """ + assert labels_for(code) == ["combined-marker"] + + +def test_prose_label() -> None: + for word in ("Returns", "Throws", "Exceptions"): + code = f""" + /** + * {word}: + */ + """ + assert labels_for(code) == ["prose-label"], word + + +def test_content_on_closer() -> None: + code = """ + /** + * a + * b */ + """ + assert labels_for(code) == ["content-on-closer"] + + +def test_plain_block_doc() -> None: + assert labels_for("/* @param x */\n") == ["plain-block-doc"] + assert findings_for("/* just an ordinary note */\n") == [] + + +def test_tag_order() -> None: + out_of_order = """ + /** + * @param x + * @tparam T + */ + """ + assert labels_for(out_of_order) == ["tag-order"] + + correct = """ + /** + * @tparam T + * @param x + * @return r + */ + """ + assert findings_for(correct) == [] + + single = """ + /** + * @param x + */ + """ + assert findings_for(single) == [] # single tag: never out of order + + +# --- command spelling (must work on body/closer lines, not just the opener) -- + + +def test_backslash_command_on_body_line() -> None: + code = r""" + /** + * \brief x + */ + """ + assert labels_for(code) == ["backslash-command"] + + +def test_backslash_command_suggests_canonical_spelling() -> None: + # a backslash + non-canonical spelling is fixed in one pass, not two: + # \sa -> @see (not @sa), \returns -> @return (not @returns) + sa = r""" + /** + * \sa other + */ + """ + assert messages_for(sa) == [r"use @see instead of \sa"] + + returns = r""" + /** + * \returns x + */ + """ + assert messages_for(returns) == [r"use @return instead of \returns"] + + +def test_wrong_command_on_body_line() -> None: + code = """ + /** + * @returns x + */ + """ + assert labels_for(code) == ["wrong-command"] + + +def test_body_line_commands_regression() -> None: + # regression: these live on body lines of a multi-line block + code = r""" + /** + * @returns bad + * @throw ex + * @sa other + * \param y + */ + """ + assert labels_for(code) == [ + "wrong-command", + "wrong-command", + "wrong-command", + "backslash-command", + ] + + +def test_command_on_closer_line() -> None: + code = """ + /** + * a + * @sa b */ + """ + assert labels_for(code) == ["wrong-command", "content-on-closer"] + + +def test_no_double_count_across_opener_body_closer() -> None: + code = """ + /** @returns opener + * @throw body + * @sa closer */ + """ + assert labels_for(code).count("wrong-command") == 3 + + +def test_code_with_word_allowed() -> None: + # @code{.cpp} is valid Doxygen and must not be flagged + code = """ + /** + * @code{.cpp} + * int x; + * @endcode + */ + """ + assert findings_for(code) == [] + + +# --- rendered message text --------------------------------------------------- + + +def test_message_uses_category_description() -> None: + # a static category renders its default description + code = "/// doc\n" + assert messages_for(code) == ["use a /** ... */ block instead of ///"] + + +def test_message_detail_overrides() -> None: + # dynamic categories render the offending text via Finding.detail + backslash = r""" + /** + * \param y + */ + """ + assert messages_for(backslash) == [r"use @param instead of \param"] + + wrong = """ + /** + * @returns x + */ + """ + assert messages_for(wrong) == ["use @return instead of @returns"] + + prose = """ + /** + * Throws: + */ + """ + assert messages_for(prose) == ['use @throws instead of prose "Throws:"'] + + +# --- robustness -------------------------------------------------------------- + + +def test_empty_file_no_crash() -> None: + assert findings_for("") == [] + + +def test_mid_line_plain_block_skipped() -> None: + # a /* opened mid-line (after code) and spanning lines is skipped, so its + # comment-like contents are not analyzed + code = """ + int x = 0; /* note: @returns is not a real tag here + * @param also not real + */ + int y = 0; + """ + assert findings_for(code) == [] + + +def test_unclosed_block_scanned_to_eof() -> None: + # an unterminated /** block is still scanned to EOF (no crash, body checked) + code = """ + /** + * @returns x + """ + assert labels_for(code) == ["wrong-command"] + + +def test_banner_and_empty_comment_not_flagged() -> None: + code = """ + /*** + * banner + ***/ + """ + assert findings_for(code) == [] + assert findings_for("/**/\n") == [] + + +def main() -> int: + tests = sorted( + (name, fn) + for name, fn in globals().items() + if name.startswith("test_") and callable(fn) + ) + failed = 0 + for name, fn in tests: + try: + fn() + print(f"PASS {name}") + except AssertionError as exc: + failed += 1 + print(f"FAIL {name}: {exc!r}") + print(f"\n{len(tests) - failed}/{len(tests)} passed") + return 1 if failed else 0 + + +if __name__ == "__main__": + sys.exit(main())